Skip to content

Commit 5ea0fbd

Browse files
committed
Moved gradient calculations from gradient.py
1 parent cfdd04f commit 5ea0fbd

File tree

1 file changed

+131
-12
lines changed

1 file changed

+131
-12
lines changed

halostack/image.py

Lines changed: 131 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
'''Module for image I/O and conversions'''
2424

25-
from halostack.gradients import Gradient
2625
from PythonMagick import Image as PMImage
2726
from PythonMagick import Blob
2827
import 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

Comments
 (0)