catali@gentoo.org>ozaToth contributed an automake/autoconf build. Included a license file to keep Debian/Redhat/etc. happy. Fabbro contributed patch to correctly link with _LIBADD and not _LDFLAGS in Makefile.am. It also uses the variable BOOST_PYTHON_LIB instead of BOOST_PYTHON_LIBS for linking. Find the documentation for PythonMagickWand at http://public.procoders.net/PythonMagickWand/docs/html/index.html. * * * If you get ... matching function for call to 'registry_lookup(const volatile void (*)()) when compiling PythonMagick, upgrade your Boost Python to 1.34. See http://www.boost.org/more/getting_started.html#CVS and checkout with cvs -z3 -d:pserver:anonymous@boost.cvs.sourceforge.net:/cvsroot/boost checkout -r RC_1_34_0 boost Thanks to Nigel Rowe for this update. * * * Here is a short PythonMagick example to try: from PythonMagick import * img=Image('30x30','red') img.write('test1.png') data=file('test1.png','rb').read() img=Image(Blob(data)) img.write('test2.png') print "now you should have two png files" Prerequisites: * boost 1.32 * python 2.3 or newer * ImageMagick 6.8.9-6+ (has to be build as shared library) * Scons Tutorial for doing basic operations Load an image from disk import PythonMagick img = PythonMagick.Image("test.tif") Display Here is a wxPython solution for displaying an image. Convert an PythonMagick image to wxImage, then you display using standard method. Here's how you convert. def convertMGtoWX(self, magickimage): img = PythonMagick.Image(magickimage) # make copy img.depth = 8 # change depth only for display img.magick = "RGB" data = img.data wximg = wx.wxEmptyImage(img.columns(), img.rows()) wximg.SetData(data) return wximg A complete short program This short program uses wxPython GUI, loads an image, displays the image, and does a simple image processing operation (threshold). from wxPython import wx import PythonMagick ID_FILE_OPEN = wx.wxNewId() ID_FILE_EXIT = wx.wxNewId() ID_THRESHOLD = wx.wxNewId() class ImagePanel(wx.wxPanel): def __init__(self, parent, id): wx.wxPanel.__init__(self, parent, id) self.image = None # wxPython image wx.EVT_PAINT(self, self.OnPaint) def display(self, magickimage): self.image = self.convertMGtoWX(magickimage) self.Refresh(True) def OnPaint(self, evt): dc = wx.wxPaintDC(self) if self.image: dc.DrawBitmap(self.image.ConvertToBitmap(), 0,0) def convertMGtoWX(self, magickimage): img = PythonMagick.Image(magickimage) # make copy img.depth = 8 # change depth only for display img.magick = "RGB" data = img.data wximg = wx.wxEmptyImage(img.columns(), img.rows()) wximg.SetData(data) return wximg class mtFrame(wx.wxFrame): def __init__(self, parent, ID, title): wx.wxFrame.__init__(self, parent, ID, title, wx.wxDefaultPosition, wx.wxSize(500, 400)) self.iPanel = ImagePanel(self, -1) self.im = None # Magick image ## Construct "File" menu self.menuBar = wx.wxMenuBar() self.menuFile = wx.wxMenu() self.menuFile.Append(ID_FILE_OPEN, "&Open image","") wx.EVT_MENU(self, ID_FILE_OPEN, self.OnOpen) self.menuFile.AppendSeparator() self.menuFile.Append(ID_FILE_EXIT, "E&xit", "") wx.EVT_MENU(self, ID_FILE_EXIT, self.OnExit) self.menuBar.Append(self.menuFile, "&File"); ## Construct "Process" menu self.menuProcess = wx.wxMenu() self.menuProcess.Append(ID_THRESHOLD, "Threshold", "") wx.EVT_MENU(self, ID_THRESHOLD, self.OnThreshold) self.menuBar.Append(self.menuProcess, "&Process") self.SetMenuBar(self.menuBar) def OnOpen(self, event): fd = wx.wxFileDialog(self, "Open Image", "", "", "*.*", wx.wxOPEN) if fd.ShowModal() == wx.wxID_OK: self.loadImage(fd.GetPath()) fd.Destroy() def loadImage(self, path): path = str(path) try: self.im = PythonMagick.Image(path) self.iPanel.display(self.im) except IOError: print "can't open the file" ##-------------- Process ------------------------ def OnThreshold(self, event): self.im = self.Threshold(self.im, 0.5) self.iPanel.display(self.im) #self.im.write('d:/threshold.tif') def Threshold(self, image, threshold): """ Threshold image. Input threshold is normalized (0-1.0) """ img = PythonMagick.Image(image) # copy img.threshold(threshold *65535.0) return img ##----------------------------------------------- def OnCloseWindow(self, event): self.Destroy() def OnExit(self, event): self.Close(True) #--------------------------------------------------------------------------- class mtApp(wx.wxApp): def OnInit(self): frame = mtFrame(wx.NULL, -1, "MagickSimple1") frame.Show(True) self.SetTopWindow(frame) return True app = mtApp(0) app.MainLoop() -Bob Klimek 9-23-03 HOW TO CONVERT IMAGE Load an image from disk import PythonMagick img = PythonMagick.Image("d:/test.tif") Convert Magick image to wxPython image def convertMGtoWX(self, magickimage): img = PythonMagick.Image(magickimage) # make copy img.depth = 8 # change depth only for display img.magick = "RGB" data = img.data wximg = wx.wxEmptyImage(img.columns(), img.rows()) wximg.SetData(data) return wximg Convert Magick image to PIL (Python Imageing Library) image def convertMGtoPIL(magickimage): 'works with grayscale and color' img = PythonMagick.Image(magickimage) # make copy img.depth = 8 # this takes 0.04 sec. for 640x480 image img.magick = "RGB" data = img.data w, h = img.columns(), img.rows() # convert string array to an RGB Pil image pilimage = Image.fromstring('RGB', (w, h), data) return pilimage Convert PIL image to Magick image def convertPILtoMG(pilimage): 'returns RGB image' if pilimage == None: return None if pilimage.mode == 'L': pilimage = pilimage.convert("RGB") size = "%sx%s" % (pilimage.size[0], pilimage.size[1]) data = pilimage.tostring('raw','RGB') mdata = PythonMagick.Blob(data) img = PythonMagick.Image(mdata, size, 8, 'RGB') return img Convert Magick image to list def convertMagickImageToList(image): 'converts a Magick image to a list of integers as RGBRGBRGB...' img = PythonMagick.Image(image) # make copy img.depth = 8 img.magick = "RGB" data = img.data aList = map(ord, data) return aList Convert a list to Magick image def convertListToMagickImage(rgbList , w, h): 'converts a list of integers (RGBRGB...) to a Magick image' data = ''.join(map(chr, rgbList)) # convert to string size = "%sx%s" % (w, h) mdata = PythonMagick.Blob(data) img = PythonMagick.Image(mdata, size, 8, 'RGB') return img IMAGE PROCESSING Note: A personal preference: In most of the below functions, I make a copy of the input image before proceeding with the operation. This de-couples the input and output image. Doing it this way takes slightly more time (the copy takes 0.00002 sec. on a 1.2 GHz PC) but nothing you'll notice. All of the following image processing operation assume that PythonMagick is imported as such: import PythonMagick Threshold def Threshold(image, t): """ Threshold image. Input threshold value (t) should by normalized (0-1.0) """ img = PythonMagick.Image(image) # copy img.threshold(t *65535.0) return img Image Subtraction def subImage(img1, img2): 'subtract (img1 - img2)' size1 = "%sx%s" % (img1.columns(), img1.rows()) size2 = "%sx%s" % (img2.columns(), img2.rows()) if size1 != size2: print 'image1 and image2 are different sizes' return None img2.composite(img1, 0, 0, PythonMagick.CompositeOperator.MinusCompositeOp) return img2 Resize Image def Resize(image, w, h): img = PythonMagick.Image(image) # copy s = "!%sx%s" % (w, h) img.sample(s) return img Extract a Color Plane (Channel) from a RGB image def ExtractPlane(image, colorPlane): if str(image.type) == 'TrueColorType': if colorPlane == 0: image.channel(PythonMagick.ChannelType.RedChannel) elif colorPlane == 1: image.channel(PythonMagick.ChannelType.GreenChannel) elif colorPlane == 2: image.channel(PythonMagick.ChannelType.BlueChannel) elif colorPlane == 3: image.type = PythonMagick.ImageType.GrayscaleType # converts to grayscale image.type = PythonMagick.ImageType.TrueColorType # convert to RGB return image else: return image Crop out (extract) a sub-image def Crop(image, x1, y1, w, h): img = PythonMagick.Image(image) # make a copy rect = "%sx%s+%s+%s" % (w, h, x1, y1) img.crop(rect) return img Paste a sub-image into a larger image def Paste(destImage, sourceImage, x1, y1): op = PythonMagick.CompositeOperator.OverCompositeOp # a kluge to prevent changing to grayscale - doesn't always happen type = destImage.type destImage.composite(sourceImage, x1, y1, op) destImage.type = type return destImage Get pixel value as 16-bit integer (for Q:16 version) def getPixel(image, x, y): 'returns 16-bit integers (0-65535)' color = image.pixelColor(x, y) return (color.redQuantum, color.greenQuantum, color.blueQuantum) Get pixel value as a normalized float (0.0 - 1.0) def getPixelNorm(image, x, y): 'returns normalized floats (0.0 - 1.0)' color = image.pixelColor(x, y) rgb = PythonMagick.ColorRGB(color) return (rgb.red, rgb.green, rgb.blue) Set pixel def setPixel(image, x, y, rgb): color = PythonMagick.ColorRGB(rgb[0], rgb[1], rgb[2]) #~ red = PythonMagick.ColorGray(0.6) image.pixelColor(x, y, color) Filters - some but by no means all of them. def Filter(image, filterType): img = PythonMagick.Image(image) # copy if filterType == 'smooth': img.blur(1.0, 0.5) return img elif filterType == 'smoothmore': return Blur(img, 2.0, 0.9) elif filterType == 'median': return MedianFilter(img, 1.0) elif filterType == 'minimizenoise': return Enhance(img) elif filterType == 'sharpen': return Sharpen(img, 2.0, 1.0) elif filterType == 'unsharpmask': return UnsharpMask(img, 2.0, 1.0, 0.85, 0.0) elif filterType == 'edgedetect': return Edge(img, 1.0) elif filterType == 'edgedetectmore': return Edge(img, 2.0) elif filterType == 'despeckle': return Despeckle(img) elif filterType == 'invert': return Invert(img) else: return img def Sharpen(image, radius=1.0, sigma=0.5): img = PythonMagick.Image(image) # copy img.sharpen(radius, sigma) return img def Blur(image, radius=1.0, sigma=0.5): """Blur image. The radius parameter specifies the radius of the Gaussian, in pixels, not counting the center pixel. The sigma parameter specifies the standard deviation of the Laplacian, in pixels. """ img = PythonMagick.Image(image) # copy img.blur(radius, sigma) return img def MedianFilter(image, radius=1.0): """Filter image by replacing each pixel component with the median color in a circular neighborhood. """ img = PythonMagick.Image(image) # copy img.medianFilter(radius) return img def Enhance(image): """Enhance image (minimize noise). Very slight effect. Its sort of like smoothing without degrading the edges. Takes out some texture. """ img = PythonMagick.Image(image) # copy img.enhance() return img def Edge(image, radius=1.0): """Edge image (hilight edges in image). The radius is the radius of the pixel neighborhood. Specify a radius of zero for automatic radius selection. """ img = PythonMagick.Image(image) # copy img.edge(radius) return img def Despeckle(image): """ Reduce speckle noise. This filter works quite nice with minimal smoothing of edges. """ img = PythonMagick.Image(image) # copy img.despeckle() return img def Invert(img, grayscale=0): 'inverse video' img.negate(grayscale) return img -Bob Klimek 9-23-03