Bump version to 1.0.
[python/dscho.git] / Mac / Contrib / ImageHelpers / ImageMac.py
blob3e465325c3f3986fc5ccad988f9c8ea49553d8a5
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.
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.
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.
20 Being one of my first experiences with Python I didn't use Exceptions to signal error conditions, sorry.
22 '''
23 import W
24 import Qd
25 from ExtPixMapWrapper import *
26 from Numeric import *
27 import Image
28 import macfs
30 class ImageMacWin(W.Window):
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)
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()
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])
58 def do_update(self,macoswindowid,event):
59 #print "update"
60 self.do_drawing()
62 class NumericMacWin(W.Window):
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)
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()
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])
90 def do_update(self,macoswindowid,event):
91 #print "update"
92 self.do_drawing()
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'
104 def Image2Numeric(im):
105 tmp=fromstring(im.tostring(),UnsignedInt8)
107 if (im.mode=='RGB')|(im.mode=='YCbCr'):
108 bands=3
110 if (im.mode=='RGBA')|(im.mode=='CMYK'):
111 bands=4
113 if (im.mode=='L'):
114 bands=1
116 tmp.shape=(im.size[0],im.size[1],bands)
117 return transpose(tmp,(1,0,2))
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())
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"
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"
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.
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
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
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
191 def diffBWImage(num1,num2):
192 return 127+(num1-num2)/2
194 def averageN(N,avrg,new):
195 return ((N-1)*avrg+new)/N
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
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)))
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]
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
249 genOutput(g,'QD',size=(600,400))
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))
265 showNumeric(num,"Numeric")
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")
272 print "here"
273 else:
274 print "did not open file"
276 if __name__ == '__main__':
277 test()