2222
2323'''Module for image I/O and conversions'''
2424
25- from halostack .gradients import Gradient
2625from PythonMagick import Image as PMImage
2726from PythonMagick import Blob
2827import numpy as np
@@ -42,6 +41,7 @@ def __init__(self, img=None, fname=None, adjustments=None):
4241 def __add__ (self , img ):
4342 self ._to_numpy ()
4443 if isinstance (img , Image ):
44+ img .to_numpy ()
4545 return Image (img = self .img + img .img )
4646 else :
4747 # Assume a numpy array or scalar
@@ -53,6 +53,7 @@ def __radd__(self, img):
5353 def __sub__ (self , img ):
5454 self ._to_numpy ()
5555 if isinstance (img , Image ):
56+ img .to_numpy ()
5657 return Image (img = self .img - img .img )
5758 else :
5859 # Assume a numpy array or scalar
@@ -67,6 +68,7 @@ def __isub__(self, img):
6768 def __mul__ (self , img ):
6869 self ._to_numpy ()
6970 if isinstance (img , Image ):
71+ img .to_numpy ()
7072 return Image (img = self .img * img .img )
7173 else :
7274 # Assume a numpy array or scalar
@@ -78,6 +80,7 @@ def __rmul__(self, img):
7880 def __div__ (self , img ):
7981 self ._to_numpy ()
8082 if isinstance (img , Image ):
83+ img .to_numpy ()
8184 return Image (img = self .img / img .img )
8285 else :
8386 # Assume a numpy array or scalar
@@ -96,24 +99,28 @@ def __lt__(self, img):
9699 def __le__ (self , img ):
97100 self ._to_numpy ()
98101 if isinstance (img , Image ):
102+ img .to_numpy ()
99103 img = img .img
100104 return self .img <= img
101105
102106 def __gt__ (self , img ):
103107 self ._to_numpy ()
104108 if isinstance (img , Image ):
109+ img .to_numpy ()
105110 img = img .img
106111 return self .img > img
107112
108113 def __ge__ (self , img ):
109114 self ._to_numpy ()
110115 if isinstance (img , Image ):
116+ img .to_numpy ()
111117 img = img .img
112118 return self .img >= img
113119
114120 def __eq__ (self , img ):
115121 self ._to_numpy ()
116122 if isinstance (img , Image ):
123+ img .to_numpy ()
117124 img = img .img
118125 return self .img == img
119126
@@ -130,6 +137,11 @@ def _read(self):
130137 '''
131138 self .img = PMImage (self .fname )
132139
140+ def to_numpy (self ):
141+ '''Convert from PMImage to numpy.
142+ '''
143+ self ._to_numpy ()
144+
133145 def _to_numpy (self ):
134146 '''Convert from PMImage to numpy.
135147 '''
@@ -145,8 +157,6 @@ def save(self, fname, bits=16, scale=True, adjustments=None):
145157 '''
146158 if scale :
147159 self ._scale (bits )
148- if adjustments :
149- self ._adjust (adjustments )
150160 self ._to_imagemagick ()
151161 self .img .write (fname )
152162
@@ -175,17 +185,18 @@ def luminance(self):
175185 else :
176186 return Image (img = self .img )
177187
178- def _adjust (self , adjustments ):
188+ def adjust (self , adjustments ):
179189 '''Adjust the image with the given function(s) and arguments.
180190 '''
181191 self ._to_imagemagick ()
182192 functions = {'usm' : self ._usm ,
183193 'emboss' : self ._emboss ,
194+ 'blur' : self ._blur ,
184195 'gamma' : self ._gamma ,
185196 'br' : self ._blue_red_subtract ,
186197 'rg' : self ._red_green_subtract ,
187198 'rgb_sub' : self ._rgb_subtract ,
188- 'gradient' : self ._gradient
199+ 'gradient' : self ._remove_gradient ,
189200 }
190201 for key in adjustments :
191202 func = functions [key ]
@@ -215,14 +226,91 @@ def _rgb_subtract(self):
215226 self ._to_numpy ()
216227 self .img -= self .luminance ().img
217228
218- def _gradient (self ):
219- '''Remove the background gradient.
229+ def _remove_gradient (self , method , * args ):
230+ '''Calculate the gradient from the image, subtract from the
231+ original, scale back to full bit depth and return the result.
220232 '''
221233 self ._to_numpy ()
222- # grad = Gradient( self.img, method, *args)
223- # grad.remove_gradient()
234+ gradient = self ._calculate_gradient ( method , * args )
235+
224236 self .img = self .img
225237
238+ def _calculate_gradient (self , method , order = 2 , * args ):
239+ '''Calculate gradient from the image using the given method.
240+ param method: name of the method for calculating the gradient
241+ '''
242+ methods = {'blur' : self ._gradient_blur ,
243+ 'random' : self ._gradient_random_points ,
244+ 'grid' : self ._gradient_grid_points
245+ # 'user': self._gradient_get_user_points,
246+ # 'mask': self._gradient_mask_points,
247+ # 'all': self._gradient_all_points
248+ }
249+
250+ func = methods [method ]
251+ result = func (* args )
252+ shape = self .img .shape
253+
254+ if method is 'blur' :
255+ return result
256+
257+ x_pts , y_pts = result
258+ if len (shape ) == 2 :
259+ return Image (img = self ._gradient_fit_surface (x_pts , y_pts ,
260+ order = order ))
261+ else :
262+ gradient = np .empty (shape )
263+ for i in range (shape [2 ]):
264+ gradient [:, :, i ] = self ._gradient_fit_surface (x_pts , y_pts ,
265+ order = order ,
266+ chan = i )
267+ return Image (img = gradient )
268+
269+ def _gradient_blur (self ):
270+ '''Blur the image to get the approximation of the background
271+ gradient.
272+ '''
273+ gradient = self .img + 0
274+ gradient .adjust ({'blur' : 50 })
275+ return gradient
276+
277+ def _gradient_random_points (self , * args ):
278+ '''Automatically extract background points for gradient estimation.
279+ '''
280+ shape = self .img .shape
281+ y_pts = np .random .randint (shape [0 ], size = (args ,))
282+ x_pts = np .random .randint (shape [1 ], size = (args ,))
283+
284+ return (x_pts , y_pts )
285+
286+ def _gradient_grid_points (self , * args ):
287+ '''Get uniform sampling of image locations.
288+ '''
289+ shape = self .img .shape
290+ y_locs = np .arange (0 , shape [0 ], args )
291+ x_locs = np .arange (0 , shape [1 ], args )
292+ x_mat , y_mat = np .meshgrid (x_locs , y_locs , indexing = 'ij' )
293+
294+ y_pts = y_mat .ravel ()
295+ x_pts = x_mat .ravel ()
296+
297+ return (x_pts , y_pts )
298+
299+ def _gradient_fit_surface (self , x_pts , y_pts , order = 2 , chan = None ):
300+ '''Fit a surface to the given channel.
301+ '''
302+ shape = self .img .shape
303+ x_locs , y_locs = np .meshgrid (np .arange (shape [0 ]),
304+ np .arange (shape [1 ]))
305+ if chan :
306+ poly = polyfit2d (x_pts , y_pts , self .img [y_pts , x_pts , chan ].ravel (),
307+ order = order )
308+ return polyval2d (x_locs , y_locs , poly )
309+ else :
310+ poly = polyfit2d (x_pts , y_pts , self .img [y_pts , x_pts ].ravel (),
311+ order = order )
312+ return polyval2d (x_locs , y_locs , poly )
313+
226314 def _scale (self , bits ):
227315 '''Scale image to cover the whole bit-range.
228316 '''
@@ -247,7 +335,13 @@ def _emboss(self):
247335 ImageMagick.
248336 '''
249337 self ._to_imagemagick ()
250- self .img .emboss (90 , 45 )
338+ self .img .shade (90 , 45 )
339+
340+ def _blur (self , weight = 50 ):
341+ '''Blur the image.
342+ '''
343+ self ._to_imagemagick ()
344+ self .img .blur (0 , weight )
251345
252346 def _gamma (self , gamma ):
253347 '''Apply gamma correction to the image.
@@ -284,12 +378,37 @@ def to_imagemagick(img):
284378 else :
285379 out_img .depth (16 )
286380 out_img .magick ('RGB' )
287- shape = out_img .shape
381+ shape = img .shape
288382 out_img .size (str (shape [1 ])+ 'x' + str (shape [0 ]))
289383 blob = Blob ()
290- blob .data = img .tosting ()
384+ blob .data = img .tostring ()
291385 out_img .read (blob )
292386 out_img .magick ('PNG' )
293387
294388 return out_img
295389 return img
390+
391+ def polyfit2d (x_loc , y_loc , z_val , order = 2 ):
392+ '''Fit a 2-D polynomial to the given data.
393+
394+ Implementation from: http://stackoverflow.com/a/7997925
395+ '''
396+ ncols = (order + 1 )** 2
397+ g_mat = np .zeros ((x_loc .size , ncols ))
398+ ij_loc = itertools .product (range (order + 1 ), range (order + 1 ))
399+ for k , (i , j ) in enumerate (ij_loc ):
400+ g_mat [:, k ] = x_loc ** i * y_loc ** j
401+ poly , _ , _ , _ = np .linalg .lstsq (g_mat , z_val )
402+ return poly
403+
404+ def polyval2d (x_loc , y_loc , poly ):
405+ '''Evaluate 2-D polynomial *poly* at the given locations
406+
407+ Implementation from: http://stackoverflow.com/a/7997925
408+ '''
409+ order = int (np .sqrt (len (poly ))) - 1
410+ ij_loc = itertools .product (range (order + 1 ), range (order + 1 ))
411+ surf = np .zeros_like (x_loc )
412+ for arr , (i , j ) in zip (poly , ij_loc ):
413+ surf += arr * x_loc ** i * y_loc ** j
414+ return surf
0 commit comments