1
# Functions which need the PIL
3
from scipy_base import ppimport
7
from scipy_base import exp, amin, amax, ravel, asarray, cast, arange, \
8
ones, NewAxis, transpose, mgrid, iscomplexobj, sum, zeros
10
Image = ppimport('Image')
11
ImageFilter = ppimport('ImageFilter')
13
__all__ = ['fromimage','toimage','imsave','imread','bytescale',
14
'imrotate','imresize','imshow','imfilter','radon']
16
_UInt8 = Numeric.UnsignedInt8
18
# Returns a byte-scaled image
19
def bytescale(data, cmin=None, cmax=None, high=255, low=0):
20
if data.typecode == _UInt8:
24
cmin = amin(ravel(data))
26
cmax = amax(ravel(data))
27
scale = high *1.0 / (cmax-cmin or 1)
28
bytedata = ((data*1.0-cmin)*scale + 0.4999).astype(_UInt8)
29
return bytedata + cast[_UInt8](low)
31
def imread(name,flatten=0):
32
"""Read an image file from a filename.
36
- flatten (0): if true, the image is flattened by calling convert('F') on
37
the resulting image object. This flattens the color layers into a single
42
return fromimage(im,flatten=flatten)
44
def imsave(name, arr):
45
"""Save an array to an image file.
51
def fromimage(im, flatten=0):
52
"""Takes a PIL image and returns a copy of the image in a Numeric container.
53
If the image is RGB returns a 3-dimensional array: arr[:,:,n] is each channel
57
- flatten (0): if true, the image is flattened by calling convert('F') on
58
the image object before extracting the numerical data. This flattens the
59
color layers into a single grayscale layer. Note that the supplied image
60
object is NOT modified.
62
assert Image.isImageType(im), "Not a PIL image."
68
im = im.convert(mode='L')
77
arr = Numeric.fromstring(str,type)
82
if im.palette.rawmode != 'RGB':
83
print "Warning: Image has invalid palette."
85
pal = Numeric.fromstring(im.palette.data,type)
87
pal.shape = (int(N/3.0),3)
89
if mode in ['RGB','YCbCr']:
91
elif mode in ['CMYK','RGBA']:
98
_errstr = "Mode is unknown or incompatible with input array shape."
99
def toimage(arr,high=255,low=0,cmin=None,cmax=None,pal=None,
100
mode=None,channel_axis=None):
101
"""Takes a Numeric array and returns a PIL image. The mode of the
102
PIL image depends on the array shape, the pal keyword, and the mode
105
For 2-D arrays, if pal is a valid (N,3) byte-array giving the RGB values
106
(from 0 to 255) then mode='P', otherwise mode='L', unless mode is given
107
as 'F' or 'I' in which case a float and/or integer array is made
109
For 3-D arrays, the channel_axis argument tells which dimension of the
110
array holds the channel data.
111
For 3-D arrays if one of the dimensions is 3, the mode is 'RGB'
112
by default or 'YCbCr' if selected.
115
The Numeric array must be either 2 dimensional or 3 dimensional.
118
if iscomplexobj(data):
119
raise ValueError, "Cannot convert a complex-valued array."
120
shape = list(data.shape)
121
valid = len(shape)==2 or ((len(shape)==3) and \
122
((3 in shape) or (4 in shape)))
123
assert valid, "Not a suitable array shape for any mode."
125
shape = (shape[1],shape[0]) # columns show up first
127
image = Image.fromstring(mode,shape,data.astype('f').tostring())
129
if mode in [None, 'L', 'P']:
130
bytedata = bytescale(data,high=high,low=low,cmin=cmin,cmax=cmax)
131
image = Image.fromstring('L',shape,bytedata.tostring())
133
image.putpalette(asarray(pal,typecode=_UInt8).tostring())
134
# Becomes a mode='P' automagically.
135
elif mode == 'P': # default gray-scale
136
pal = arange(0,256,1,typecode='b')[:,NewAxis] * \
137
ones((3,),typecode='b')[NewAxis,:]
138
image.putpalette(asarray(pal,typecode=_UInt8).tostring())
140
if mode == '1': # high input gives threshold for 1
141
bytedata = ((data > high)*255).astype('b')
142
image = Image.fromstring('L',shape,bytedata.tostring())
143
image = image.convert(mode='1')
146
cmin = amin(ravel(data))
148
cmax = amax(ravel(data))
149
data = (data*1.0 - cmin)*(high-low)/(cmax-cmin) + low
151
image = Image.fromstring(mode,shape,data.astype('i').tostring())
153
raise ValueError, _errstr
156
# if here then 3-d array with a 3 or a 4 in the shape length.
157
# Check for 3 in datacube shape --- 'RGB' or 'YCbCr'
158
if channel_axis is None:
160
ca = Numeric.nonzero(asarray(shape) == 3)[0]
162
ca = Numeric.nonzero(asarray(shape) == 4)
166
raise ValueError, "Could not find channel dimension."
171
if numch not in [3,4]:
172
raise ValueError, "Channel axis dimension is not valid."
174
bytedata = bytescale(data,high=high,low=low,cmin=cmin,cmax=cmax)
176
strdata = bytedata.tostring()
177
shape = (shape[1],shape[0])
179
strdata = transpose(bytedata,(0,2,1)).tostring()
180
shape = (shape[2],shape[0])
182
strdata = transpose(bytedata,(1,2,0)).tostring()
183
shape = (shape[2],shape[1])
185
if numch == 3: mode = 'RGB'
189
if mode not in ['RGB','RGBA','YCbCr','CMYK']:
190
raise ValueError, _errstr
192
if mode in ['RGB', 'YCbCr']:
193
assert numch == 3, "Invalid array shape for mode."
194
if mode in ['RGBA', 'CMYK']:
195
assert numch == 4, "Invalid array shape for mode."
197
# Here we know data and mode is coorect
198
image = Image.fromstring(mode, shape, strdata)
201
def imrotate(arr,angle,interp='bilinear'):
202
"""Rotate an image counter-clockwise by angle degrees.
204
Interpolation methods can be:
205
'nearest' : for nearest neighbor
206
'bilinear' : for bilinear
207
'cubic' or 'bicubic' : for bicubic
210
func = {'nearest':0,'bilinear':2,'bicubic':3,'cubic':3}
212
im = im.rotate(angle,resample=func[interp])
215
def imresize(arr,newsize,interp='bilinear',mode=None):
216
newsize=list(newsize)
218
newsize = tuple(newsize)
220
func = {'nearest':0,'bilinear':2,'bicubic':3,'cubic':3}
221
im = toimage(arr,mode=mode)
222
im = im.resize(newsize,resample=func[interp])
226
"""Simple showing of an image through an external viewer.
229
if (len(arr.shape) == 3) and (arr.shape[2] == 4):
232
im.save('/tmp/scipy_imshow.png')
233
if os.system("(xv /tmp/scipy_imshow.png; rm -f /tmp/scipy_imshow.png)&"):
237
print "Warning: Alpha channel may not be handled correctly."
242
def imresize(arr,size):
245
If size is an integer it is a percentage of current size.
246
If size is a float it is a fraction of current size.
247
If size is a tuple it is the size of the output image.
251
if ts is types.IntType:
253
if type(size) is types.FloatType:
254
size = (im.size[0]*size,im.size[1]*size)
256
size = (size[1],size[0])
257
imnew = im.resize(size)
258
return fromimage(imnew)
261
def imfilter(arr,ftype):
262
"""Simple filtering of an image.
265
'blur', 'contour', 'detail', 'edge_enhance', 'edge_enhance_more',
266
'emboss', 'find_edges', 'smooth', 'smooth_more', 'sharpen'
268
_tdict = {'blur':ImageFilter.BLUR,
269
'contour':ImageFilter.CONTOUR,
270
'detail':ImageFilter.DETAIL,
271
'edge_enhance':ImageFilter.EDGE_ENHANCE,
272
'edge_enhance_more':ImageFilter.EDGE_ENHANCE_MORE,
273
'emboss':ImageFilter.EMBOSS,
274
'find_edges':ImageFilter.FIND_EDGES,
275
'smooth':ImageFilter.SMOOTH,
276
'smooth_more':ImageFilter.SMOOTH_MORE,
277
'sharpen':ImageFilter.SHARPEN
281
if ftype not in _tdict.keys():
282
raise ValueError, "Unknown filter type."
283
return fromimage(im.filter(_tdict[ftype]))
286
def radon(arr,theta=None):
289
s = zeros((arr.shape[1],len(theta)),'d')
292
im = imrotate(arr,-th)
293
s[:,k] = sum(im,axis=0)