Jack Jansen | d35509a | 2000-09-22 12:46:19 +0000 | [diff] [blame] | 1 | ''' |
| 2 | ImageMac.py by Trocca Riccardo (rtrocca@libero.it) |
| 3 | This module provides functions to display images and Numeric arrays |
| 4 | It provides two classes ImageMacWin e NumericMacWin and two simple methods showImage and |
| 5 | showNumeric. |
| 6 | |
| 7 | They work like this: |
| 8 | showImage(Image,"optional window title",zoomFactor) |
| 9 | the same for showNumeric |
| 10 | zoomfactor (defaults to 1) allows to zoom in the image by a factor of 1x 2x 3x and so on |
| 11 | I did't try with a 0.5x or similar. |
| 12 | The windows don't provide a scrollbar or a resize box. |
| 13 | Probably a better solution (and more similar to the original implementation in PIL and NumPy) |
| 14 | would be to save a temp file is some suitable format and then make an application (through appleevents) to open it. |
| 15 | Good guesses should be GraphicConverter or PictureViewer. |
| 16 | |
| 17 | However the classes ImageMacWin e NumericMacWin use an extended version of PixMapWrapper in order to |
| 18 | provide an image buffer and then blit it in the window. |
| 19 | |
| 20 | Being one of my first experiences with Python I didn't use Exceptions to signal error conditions, sorry. |
| 21 | |
| 22 | ''' |
| 23 | import W |
| 24 | import Qd |
| 25 | from ExtPixMapWrapper import * |
| 26 | from Numeric import * |
| 27 | import Image |
| 28 | import macfs |
| 29 | |
| 30 | class ImageMacWin(W.Window): |
| 31 | |
| 32 | def __init__(self,size=(300,300),title="ImageMacWin"): |
| 33 | self.pm=ExtPixMapWrapper() |
| 34 | self.empty=1 |
| 35 | self.size=size |
| 36 | W.Window.__init__(self,size,title) |
| 37 | |
| 38 | def Show(self,image,resize=0): |
| 39 | #print "format: ", image.format," size: ",image.size," mode: ",image.mode |
| 40 | #print "string len :",len(image.tostring()) |
| 41 | self.pm.fromImage(image) |
| 42 | self.empty=0 |
| 43 | if resize: |
| 44 | self.size=(image.size[0]*resize,image.size[1]*resize) |
| 45 | W.Window.do_resize(self,self.size[0],self.size[1],self.wid) |
| 46 | self.do_drawing() |
| 47 | |
| 48 | def do_drawing(self): |
| 49 | #print "do_drawing" |
| 50 | self.SetPort() |
| 51 | Qd.RGBForeColor( (0,0,0) ) |
| 52 | Qd.RGBBackColor((65535, 65535, 65535)) |
| 53 | Qd.EraseRect((0,0,self.size[0],self.size[1])) |
| 54 | if not self.empty: |
| 55 | #print "should blit" |
| 56 | self.pm.blit(0,0,self.size[0],self.size[1]) |
| 57 | |
| 58 | def do_update(self,macoswindowid,event): |
| 59 | #print "update" |
| 60 | self.do_drawing() |
| 61 | |
| 62 | class NumericMacWin(W.Window): |
| 63 | |
| 64 | def __init__(self,size=(300,300),title="ImageMacWin"): |
| 65 | self.pm=ExtPixMapWrapper() |
| 66 | self.empty=1 |
| 67 | self.size=size |
| 68 | W.Window.__init__(self,size,title) |
| 69 | |
| 70 | def Show(self,num,resize=0): |
| 71 | #print "shape: ", num.shape |
| 72 | #print "string len :",len(num.tostring()) |
| 73 | self.pm.fromNumeric(num) |
| 74 | self.empty=0 |
| 75 | if resize: |
| 76 | self.size=(num.shape[1]*resize,num.shape[0]*resize) |
| 77 | W.Window.do_resize(self,self.size[0],self.size[1],self.wid) |
| 78 | self.do_drawing() |
| 79 | |
| 80 | def do_drawing(self): |
| 81 | #print "do_drawing" |
| 82 | self.SetPort() |
| 83 | Qd.RGBForeColor( (0,0,0) ) |
| 84 | Qd.RGBBackColor((65535, 65535, 65535)) |
| 85 | Qd.EraseRect((0,0,self.size[0],self.size[1])) |
| 86 | if not self.empty: |
| 87 | #print "should blit" |
| 88 | self.pm.blit(0,0,self.size[0],self.size[1]) |
| 89 | |
| 90 | def do_update(self,macoswindowid,event): |
| 91 | #print "update" |
| 92 | self.do_drawing() |
| 93 | |
| 94 | ''' |
| 95 | Some utilities: convert an Image to a NumPy array and viceversa. |
| 96 | The Image2Numeric function doesn't make any color space conversion. |
| 97 | The Numeric2Image function returns an L or RGB or RGBA images depending on the shape of |
| 98 | the array: |
| 99 | (x,y) -> 'L' |
| 100 | (x,y,1) -> 'L' |
| 101 | (x,y,3) -> 'RGB' |
| 102 | (x,y,4) -> 'RGBA' |
| 103 | ''' |
| 104 | def Image2Numeric(im): |
| 105 | tmp=fromstring(im.tostring(),UnsignedInt8) |
| 106 | |
| 107 | if (im.mode=='RGB')|(im.mode=='YCbCr'): |
| 108 | bands=3 |
| 109 | |
| 110 | if (im.mode=='RGBA')|(im.mode=='CMYK'): |
| 111 | bands=4 |
| 112 | |
| 113 | if (im.mode=='L'): |
| 114 | bands=1 |
| 115 | |
| 116 | tmp.shape=(im.size[0],im.size[1],bands) |
| 117 | return transpose(tmp,(1,0,2)) |
| 118 | |
| 119 | def Numeric2Image(num): |
| 120 | #sometimes a monoband image's shape can be (x,y,1), other times just (x,y). Here w deal with both |
| 121 | if len(num.shape)==3: |
| 122 | bands=num.shape[2] |
| 123 | if bands==1: |
| 124 | mode='L' |
| 125 | elif bands==3: |
| 126 | mode='RGB' |
| 127 | else: |
| 128 | mode='RGBA' |
| 129 | return Image.fromstring(mode,(num.shape[1],num.shape[0]),transpose(num,(1,0,2)).astype(UnsignedInt8).tostring()) |
| 130 | else: |
| 131 | return Image.fromstring('L',(num.shape[1],num.shape[0]),transpose(num).astype(UnsignedInt8).tostring()) |
| 132 | |
| 133 | def showImage(im,title="ImageWin",zoomFactor=1): |
| 134 | imw=ImageMacWin((300,200),title) |
| 135 | imw.open() |
| 136 | try: |
| 137 | imw.Show(im,zoomFactor ) |
| 138 | except MemoryError,e: |
| 139 | imw.close() |
| 140 | print "ImageMac.showImage: Insufficient Memory" |
| 141 | |
| 142 | |
| 143 | def showNumeric(num,title="NumericWin",zoomFactor=1): |
| 144 | #im=Numeric2Image(num) |
| 145 | numw=NumericMacWin((300,200),title) |
| 146 | numw.open() |
| 147 | try: |
| 148 | numw.Show(num,zoomFactor ) |
| 149 | except MemoryError: |
| 150 | numw.close() |
| 151 | print "ImageMac.showNumeric Insufficient Memory" |
| 152 | |
| 153 | ''' |
| 154 | GimmeImage pops up a file dialog and asks for an image file. |
| 155 | it returns a PIL image. |
| 156 | Optional argument: a string to be displayed by the dialog. |
| 157 | ''' |
| 158 | |
| 159 | def GimmeImage(prompt="Image File:"): |
| 160 | import macfs |
| 161 | fsspec, ok = macfs.PromptGetFile(prompt) |
| 162 | if ok: |
| 163 | path = fsspec.as_pathname() |
| 164 | return Image.open(path) |
| 165 | return None |
| 166 | |
| 167 | ''' |
| 168 | This is just some experimental stuff: |
| 169 | Filter3x3 a convolution filter (too slow use signal tools instead) |
| 170 | diffBWImage subtracts 2 images contained in NumPy arrays |
| 171 | averageN it computes the average of a list incrementally |
| 172 | BWImage converts an RGB or RGBA image (in a NumPy array) to BW |
| 173 | SplitBands splits the bands of an Image (inside a NumPy) |
| 174 | NumHisto and PlotHisto are some experiments to plot an intesity histogram |
| 175 | ''' |
| 176 | |
| 177 | def Filter3x3(mul,fi,num): |
| 178 | (a,b,c,d,e,f,g,h,i)=fi |
| 179 | print fi |
| 180 | num.shape=(num.shape[0],num.shape[1]) |
| 181 | res=zeros(num.shape) |
| 182 | for x in range(1,num.shape[0]-1): |
| 183 | for y in range(1,num.shape[1]-1): |
| 184 | xb=x-1 |
| 185 | xa=x+1 |
| 186 | yb=y-1 |
| 187 | ya=y+1 |
| 188 | res[x,y]=int((a*num[xb,yb]+b*num[x,yb]+c*num[xa,yb]+d*num[xb,y]+e*num[x,y]+f*num[xa,y]+g*num[xb,ya]+h*num[x,ya]+i*num[xa,ya])/mul) |
| 189 | return res |
| 190 | |
| 191 | def diffBWImage(num1,num2): |
| 192 | return 127+(num1-num2)/2 |
| 193 | |
| 194 | def averageN(N,avrg,new): |
| 195 | return ((N-1)*avrg+new)/N |
| 196 | |
| 197 | def BWImage(num): |
| 198 | if num.shape[2]==3: |
| 199 | bw=array(((0.3086,0.6094,0.0820))) |
| 200 | else: |
| 201 | bw=array(((0.3086,0.6094,0.0820,0))) |
| 202 | res=innerproduct(num,bw) |
| 203 | res.shape=(res.shape[0],res.shape[1]) |
| 204 | return res |
| 205 | |
| 206 | def SplitBands(num): |
| 207 | x=num.shape[0] |
| 208 | y=num.shape[1] |
| 209 | if num.shape[2]==3: |
| 210 | return (reshape(num[:,:,0],(x,y)),reshape(num[:,:,1],(x,y)),reshape(num[:,:,2],(x,y))) |
| 211 | else: |
| 212 | return (reshape(num[:,:,0],(x,y)),reshape(num[:,:,1],(x,y)),reshape(num[:,:,2],(x,y)),reshape(num[:,:,3],(x,y))) |
| 213 | |
| 214 | def NumHisto(datas): |
| 215 | #print "type(datas) ",type(datas) |
| 216 | a=ravel(datas) |
| 217 | n=searchsorted(sort(a),arange(0,256)) |
| 218 | n=concatenate([n,[len(a)]]) |
| 219 | return n[1:]-n[:-1] |
| 220 | |
| 221 | def PlotHisto(datas,ratio=1): |
| 222 | from graphite import * |
| 223 | from MLab import max |
| 224 | h=NumHisto(datas) |
| 225 | #print "histo: ",h |
| 226 | #print "histo.shape: ",h.shape |
| 227 | maxval=max(h) |
| 228 | #print "maxval ",maxval |
| 229 | h.shape=(256,1) |
| 230 | x=arange(0,256) |
| 231 | x.shape=(256,1) |
| 232 | datah=concatenate([x,h],1) |
| 233 | print "data: " |
| 234 | print datah |
| 235 | g=Graph() |
| 236 | g.datasets.append(Dataset(datah)) |
| 237 | f0=PointPlot() |
| 238 | f0.lineStyle = LineStyle(width=2, color=red, kind=SOLID) |
| 239 | g.formats = [f0] |
| 240 | g.axes[X].range = [0,255] |
| 241 | g.axes[X].tickMarks[0].spacing = 10 |
| 242 | #g.axes[X].tickMarks[0].labels = "%d" |
| 243 | g.axes[Y].range = [0,maxval/ratio] |
| 244 | g.bottom = 370 |
| 245 | g.top =10 |
| 246 | g.left=10 |
| 247 | g.right=590 |
| 248 | |
| 249 | genOutput(g,'QD',size=(600,400)) |
| 250 | |
| 251 | def test(): |
| 252 | import MacOS |
| 253 | import Image |
| 254 | import ImageFilter |
| 255 | import Numeric |
| 256 | fsspec, ok = macfs.PromptGetFile("Image File:") |
| 257 | if ok: |
| 258 | path = fsspec.as_pathname() |
| 259 | im=Image.open(path) |
| 260 | #im2=im.filter(ImageFilter.SMOOTH) |
| 261 | showImage(im,"normal") |
| 262 | num=Image2Numeric(im) |
| 263 | #num=Numeric.transpose(num,(1,0,2)) |
| 264 | |
| 265 | showNumeric(num,"Numeric") |
| 266 | |
| 267 | print "num.shape ",num.shape |
| 268 | showImage(Numeric2Image(num),"difficile") |
| 269 | #showImage(im.filter(ImageFilter.SMOOTH),"smooth") |
| 270 | #showImage(im.filter(ImageFilter.FIND_EDGES).filter(ImageFilter.SHARPEN),"detail") |
| 271 | |
| 272 | print "here" |
| 273 | else: |
| 274 | print "did not open file" |
| 275 | |
| 276 | if __name__ == '__main__': |
| 277 | test() |