Coverage for utils/image_processor.py: 38%
417 statements
« prev ^ index » next coverage.py v7.0.4, created at 2023-01-10 09:27 -0600
« prev ^ index » next coverage.py v7.0.4, created at 2023-01-10 09:27 -0600
1"""
2Copyright 1999 Illinois Institute of Technology
4Permission is hereby granted, free of charge, to any person obtaining
5a copy of this software and associated documentation files (the
6"Software"), to deal in the Software without restriction, including
7without limitation the rights to use, copy, modify, merge, publish,
8distribute, sublicense, and/or sell copies of the Software, and to
9permit persons to whom the Software is furnished to do so, subject to
10the following conditions:
12The above copyright notice and this permission notice shall be
13included in all copies or substantial portions of the Software.
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18IN NO EVENT SHALL ILLINOIS INSTITUTE OF TECHNOLOGY BE LIABLE FOR ANY
19CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23Except as contained in this notice, the name of Illinois Institute
24of Technology shall not be used in advertising or otherwise to promote
25the sale, use or other dealings in this Software without prior written
26authorization from Illinois Institute of Technology.
27"""
29import copy
30import cv2
31import numpy as np
32import fabio
33from skimage.morphology import white_tophat
34from pyFAI.azimuthalIntegrator import AzimuthalIntegrator
36def distance(pt1, pt2):
37 """
38 Get distance between 2 points
39 :param pt1: first point (tuple or list of 2 values)
40 :param pt2: second point (tuple or list of 2 values)
41 :return: distance (float)
42 """
43 return np.sqrt((pt1[0]-pt2[0])**2+(pt1[1]-pt2[1])**2)
45def get16bitImage(img):
46 """
47 Convert a image to uint16 image
48 :param img: input image
49 :return: uint16 image
50 """
51 max_val = img.max()
52 min_val = img.min()
53 if max_val == min_val:
54 return (img * 65535. / min_val).astype('uint16')
55 else:
56 dev = max_val - min_val
57 return (np.round(img*65535./dev)).astype('uint16')
59def get8bitImage(img, min=None, max=None):
60 """
61 Convert a image to uint8 image
62 :param img: input image
63 :param min: min intensity
64 :param max: max intensity
65 :return: uint8 image
66 """
67 cimg = np.array(img, dtype=np.float32)
68 if max is None:
69 max = img.max() * 0.5
70 cimg[cimg > max] = max
72 if min is None:
73 min = img.min()
74 cimg[cimg < min] = min
76 cimg -= min
77 min = 0
78 max = cimg.max()
80 if max <= min:
81 img8bit = (cimg * 0.).astype('uint8')
82 else:
83 alpha = 255. / (max)
84 img8bit = cv2.convertScaleAbs(cimg, alpha=alpha)
86 return img8bit
88def inverte(imagem):
89 """
90 Invert grey scale image
91 :param imagem: input image
92 :return: inverted image
93 """
94 imagem = (255-imagem)
95 return imagem
97def getThreshold(img, percent):
98 """
99 Get threshold value by using percentage of number of points
100 :param img: input image
101 :param percent: percentage of number of points higher threshold value
102 :return: threshold value
103 """
104 bins = np.arange(img.max())
105 hist, bins = np.histogram(img, bins)
106 hist = np.array(hist)
108 thrhold = 0
109 dev = min(1000, img.max())
110 for t in np.arange(0, img.max()-1, img.max()/dev):
111 valueHist = np.sum(hist[int(t):int(img.max())])
112 if (valueHist/(1.0*img.shape[0]*img.shape[1]))<percent:
113 if valueHist < 100 and t > 1:
114 thrhold = t-1
115 else:
116 thrhold = t
117 # find number of pixel ----> thrhold = t-1
118 break
119 return thrhold
121def thresholdImg(img, percent, convert_type=cv2.THRESH_BINARY_INV):
122 """
123 Apply thresholding by percent
124 :param img: input image
125 :param percent: percentage of number of points higher threshold value
126 :param convert_type: convert type see http://docs.opencv.org/trunk/d7/d4d/tutorial_py_thresholding.html
127 :return: threshold image
128 """
129 th = max(0, getThreshold(img, percent=percent)-1)
130 _, thres = cv2.threshold(img, th, 255, convert_type, dst=img)
131 return thres
133def bkImg(img, percent=0.01, morph=25):
134 """
135 Apply thresholding and morphology
136 :param img: input image
137 :param percent: percentage of number of points higher threshold value
138 :param morph: morphology size
139 :return: image
140 """
141 img = thresholdImg(img, percent)
142 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (morph, morph))
143 img = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
144 img = inverte(img)
145 return img
147def getBGR(img):
148 """
149 Convert grayscale image to RGB image
150 :param img: grayscale image
151 :return: RGB image
152 """
153 copy_img = copy.copy(img)
154 copy_img = cv2.resize(copy_img, (int(copy_img.shape[1]), int(copy_img.shape[0])))
155 return cv2.cvtColor(copy_img, cv2.COLOR_GRAY2BGR)
157def getContours(img, n1=1, n2=2):
158 """
159 Give the contours for the image.
160 :param img, n1, n2:
161 :return: contours
162 """
163 ret = cv2.findContours(img, n1, n2)
164 if len(ret) == 3:
165 return ret[1]
166 if len(ret) == 2:
167 return ret[0]
168 return None
170def getCenter(img):
171 """
172 Find center of the diffraction.
173 :param img: input image
174 :return: center
175 """
176 img = get8bitImage(copy.copy(img))
177 img = cv2.GaussianBlur(img, (5, 5), 0)
178 init_center = None
180 ## Find init center by apply thresholding and fit ellipse to the contour which has the maximum size
181 cimg = bkImg(copy.copy(img), 0.005, 50)
183 contours = getContours(cimg)
184 cnt = max(contours, key=len)
185 if len(cnt) > 5:
186 ellipse = cv2.fitEllipse(cnt)
187 init_center = (ellipse[0][0], ellipse[0][1])
189 ## Find center by apply thresholding and fit ellipse to the contour of reflections and find the average center of reflections
190 if init_center is not None:
191 cimg = thresholdImg(copy.copy(img), 0.00015)
192 contours = getContours(cimg)
194 # Find 2 biggest contours (reflections)
195 cnts = sorted(contours, key=len, reverse=True)[:6]
196 reflections = []
197 for i, cnt in enumerate(cnts):
198 # Fit ellipse to 6 reflections if its size is bigger than 10
199 if len(cnt) >= 10:
200 ellipse = cv2.fitEllipse(cnt)
201 center = ellipse[0]
202 axes = ellipse[1]
203 center = (center[0], center[1])
204 reflections.append((center, np.pi*axes[0]*axes[1]))
206 inds = np.arange(0, len(reflections))
207 if len(reflections) > 1:
208 r1 = None
209 r2 = None
210 min_diff = 99999
211 for i in inds:
212 other_inds = np.delete(inds, i)
213 its_pair = min(other_inds, key=lambda k:abs(reflections[i][1]-reflections[k][1]))
214 diff = abs(reflections[i][1]-reflections[its_pair][1])
215 if diff < min_diff:
216 r1 = i
217 r2 = its_pair
218 min_diff = diff
220 if r1 is not None and r2 is not None:
221 x = ((reflections[r1][0][0]+reflections[r1][0][0]) / 2.)
222 y = ((reflections[r1][0][1] + reflections[r1][0][1]) / 2.)
223 if init_center is not None and distance(init_center, (x, y)) < 7:
224 # Return average center of reflections
225 return (x, y)
227 # # Find the average center location from all fitting ellipses
228 # if len(centers) % 2 != 0:
229 # # if number of center is odd, remove one reflection
230 # centers = centers[:-1]
231 # sum_centers = np.sum(centers, axis=0)
232 # x = int(round(1.* sum_centers[0] / len(centers)))
233 # y = int(round(1.* sum_centers[1] / len(centers)))
234 # if init_center is not None and distance(init_center, (x,y)) < 7:
235 # # Return average center of reflections
236 # return (x, y)
237 return init_center
239 # Find cener by using opencv moments. See http://docs.opencv.org/trunk/dd/d49/tutorial_py_contour_features.html
240 nZero = cv2.findNonZero(img) # copy_img)#
241 if nZero is not None:
242 copy_img = bkImg(copy.copy(img), 0.015, 30)
243 m = cv2.moments(copy_img)
244 if m['m00'] != 0:
245 # initial center
246 return ((m['m10'] / m['m00']), (m['m01'] / m['m00']))
248 # Find Center by fitting circle in the image
249 cimg = bkImg(copy.copy(img), 0.0015, 50)
250 circles = cv2.HoughCircles(cimg, 3 , 1, 100,
251 param1=60, param2=20, minRadius=0, maxRadius=0) # 3 = cv2.HOUGH_GRADIENT
252 if circles is not None:
253 return (circles[0][0][0], circles[0][0][1])
255 # If there's no method working return center of the image
256 return (img.shape[1] / 2, img.shape[0] / 2)
258def get_ring_model(hist):
259 """
260 Fit gaussian model to histogram
261 :param hist:
262 :return:
263 """
264 # Smooth histogram to find parameters easier
265 from .histogram_processor import smooth
266 hist[1] = smooth(hist[1], 20)
268 index = np.argmax(hist[1])
269 u = hist[0][index]
270 if u < np.pi / 2:
271 u += np.pi
272 elif u > 3 * np.pi / 2:
273 u -= np.pi
275 # Fit model using same gaussian
276 x = hist[0]
278 # Call orientation_GMM3
279 from lmfit.models import GaussianModel
280 from lmfit import Model
281 def orientation_GMM3(x, u, sigma, alpha, bg):
282 mod = GaussianModel()
283 return mod.eval(x=x, amplitude=alpha, center=u, sigma=sigma) + \
284 mod.eval(x=x, amplitude=alpha, center=u-np.pi, sigma=sigma) + \
285 mod.eval(x=x, amplitude=alpha, center=u+np.pi, sigma=sigma) + bg
286 model = Model(orientation_GMM3, independent_vars='x')
287 max_height = np.max(hist[1])
289 model.set_param_hint('u', value=u, min=np.pi/2, max=3*np.pi/2)
290 model.set_param_hint('sigma', value=0.1, min=0, max=np.pi*2)
291 model.set_param_hint('alpha', value=max_height*0.1/0.3989423, min=0)
292 model.set_param_hint('bg', value=0, min=-1, max=max_height+1)
294 result = model.fit(data=hist[1], x=x, params=model.make_params())
295 errs = abs(result.best_fit - result.data)
296 weights = errs / errs.mean() + 1
297 weights[weights > 3.] = 0
298 result = model.fit(data=hist[1], x=x, params=result.params, weights=weights)
300 return result.values
302def HoF(hist, mode='f'):
303 """
304 Calculate Herman Orientation Factors
305 """
306 Ints = []
307 n_pi = len(hist) // 2 # number of hist unit in pi range
308 n_hpi = n_pi // 2 # number of hist unit in half pi range
309 for i in range(n_pi):
310 I = hist[i:(i+n_pi)].copy()
311 I[:i] += np.flipud(hist[:i])
312 I[i:] += np.flipud(hist[(i+n_pi):])
313 Ints.append(I)
314 rads = np.linspace(0, np.pi, n_pi + 1)[:-1]
315 denom = np.sin(rads)
316 numer = (np.cos(rads)**2) * denom
318 HoFs = np.zeros(hist.shape)
319 for i in range(len(hist)):
320 I = Ints[i] if i < n_pi else np.flipud(Ints[i - n_pi])
321 if mode == 'f':
322 HoFs[i] = ((I * numer).sum() / (I * denom).sum()) if i < n_pi else HoFs[i - n_pi]
323 else:
324 HoFs[i] = (I[:n_hpi] * numer[:n_hpi]).sum() / (I[:n_hpi] * denom[:n_hpi]).sum()
325 return (3 * HoFs - 1) / 2
327def getRadOfMaxHoF(HoFs, mode, ratio=0.05):
328 """
329 Get the radian of the maximum Herman Orientation Factor
330 :param HoFs:
331 :param mode:
332 """
333 nHoFs = len(HoFs)
334 num = int(nHoFs * ratio)
335 if mode == 'f':
336 HoFs = HoFs[:(nHoFs // 2)]
337 num //= 2
338 # get the indices of the top num largest HoFs
339 idxs = sorted(np.arange(len(HoFs)), key=lambda i: HoFs[i])[-num:]
340 idxs = sorted(idxs)
341 # group the indices
342 grps = [[idxs[0]]]
343 for idx in idxs[1:]:
344 if grps[-1][-1] == idx - 1:
345 grps[-1].append(idx)
346 else:
347 grps.append([idx])
348 # handle the round case
349 if len(grps) > 1 and grps[0][0] == 0 and grps[-1][-1] == len(HoFs) - 1:
350 grps[0] += [idx - len(HoFs) for idx in grps[-1]]
351 # find the groups of max number of indices
352 maxn = max(len(grp) for grp in grps)
353 grps = [grp for grp in grps if len(grp) == maxn]
354 opt_grp = sorted(grps, key=lambda g:HoFs[g].sum())[-1]
355 opt_idx = np.mean(opt_grp) % len(HoFs)
356 return 2 * np.pi * opt_idx / nHoFs
358def getRotationAngle(img, center, method=0):
359 """
360 Find rotation angle of the diffraction.
361 :param img: input image
362 :param center: center of the diffraction
363 :return: rotation angle in degree
364 """
365 ## Find init angle by apply thresholding and fit ellipse to the contour which has the maximum size
366 cimg = get8bitImage(copy.copy(img))
367 cimg = cv2.GaussianBlur(cimg, (5, 5), 0)
368 cimg = bkImg(copy.copy(cimg), 0.005, 50)
369 init_angle = None
370 contours = getContours(cimg)
371 cnt = max(contours, key=len)
372 if len(cnt) > 5:
373 ellipse = cv2.fitEllipse(cnt)
374 init_angle = (ellipse[2]+90.) % 180
375 init_angle = init_angle if init_angle <= 90. else 180. - init_angle
377 # Find angle with maximum intensity from Azimuthal integration
378 if img.shape == (1043, 981):
379 det = "pilatus1m"
380 else:
381 det = "agilent_titan"
383 corners = [(0, 0), (0, img.shape[1]), (img.shape[0], 0), (img.shape[0], img.shape[1])]
384 npt_rad = int(round(max([distance(center, c) for c in corners])))
385 ai = AzimuthalIntegrator(detector=det)
386 ai.setFit2D(200, center[0], center[1])
387 I2D, tth, _ = ai.integrate2d(img, npt_rad, 360, unit="r_mm", method="csr_ocl")
388 I2D = I2D[:, :int(len(tth)/3.)]
389 hist = np.sum(I2D, axis=1) # Find a histogram from 2D Azimuthal integrated histogram, the x-axis is degree and y-axis is intensity
390 sum_range = 0
392 # Find degree which has maximum intensity
393 if method == 1: # gmm
394 x = np.arange(0, 2 * np.pi, 2 * np.pi / 360)
395 model_pars = get_ring_model([x, hist])
396 max_degree = int(model_pars['u'] / np.pi * 180) % 180
397 elif 2 <= method <= 3: # 'hof_f' or 'hof_h'
398 HoFs = HoF(hist, 'f' if method == 2 else 'h')
399 max_degree = int(getRadOfMaxHoF(HoFs, 'f' if method == 2 else 'h') / np.pi * 180) % 180
400 else: # Find the best degree by its intensity
401 max_degree = max(np.arange(180), key=lambda d: np.sum(hist[d - sum_range:d + sum_range + 1]) + np.sum(
402 hist[d + 180 - sum_range:d + 181 + sum_range]))
404 # # If the degree and initial angle from ellipse are different, return ellipse angle instead
405 if init_angle is not None and abs(max_degree-init_angle) > 20. and abs(180 - max_degree - init_angle)>20:
406 return int(round(init_angle))
408 #If max degree is obtuse return the acute angle equivalent of the same
409 if max_degree > 90:
410 return -1*(180-max_degree)
411 if max_degree < -90:
412 return 180 + max_degree
414 # otherwise, return max degree
415 return max_degree
417def getCenterRemovedImage(img, center, rmin):
418 """
419 Remove center location in the image (replace by 0 (black value))
420 :param img: input image
421 :param center: center location (tuple or list)
422 :param rmin: radius of the circle
423 :return: image after center location is removed
424 """
425 center = (int(center[0]), int(center[1]))
426 mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
427 cv2.ellipse(mask, tuple(center), axes=(rmin, rmin), angle=0, startAngle=0,
428 endAngle=360, color=255,
429 thickness=-1) # Draw a circle in mask
430 img[mask > 0] = 0 # replace circle with 0
431 return img
433def getNewZoom(current, move, xmax, ymax, ymin=0):
434 """
435 Get new zoom location (x, and y ranges) by given current zoom, move vector and x,y maximum ranges
436 :param current: current zoom location
437 :param move: moving vector
438 :param xmax: maximum x
439 :param ymax: maximum y
440 :param ymin: minimum y
441 :return:
442 """
443 x1 = current[0][0] + move[0]
444 x2 = current[0][1] + move[0]
445 if x1 < 0:
446 x1 = 0
447 x2 = current[0][1] - current[0][0]
448 if x2 >= xmax:
449 x2 = xmax - 1
450 x1 = x2 - (current[0][1] - current[0][0])
452 y1 = current[1][0] + move[1]
453 y2 = current[1][1] + move[1]
454 if y1 < ymin:
455 y1 = ymin
456 y2 = y1 + (current[1][1] - current[1][0])
457 if y2 > ymax:
458 y2 = ymax
459 y1 = y2 - (current[1][1] - current[1][0])
461 return [(x1, x2), (y1, y2)]
463def rotateImage(img, center, angle, img_type, mask_thres = -999):
464 """
465 Get rotated image by angle.
466 :param img: input image
467 :param angle: rotation angle
468 :return: rotated image
469 """
470 if angle == 0:
471 return img, center, None
473 # M = cv2.getRotationMatrix2D(tuple(center), angle, 1)
474 # size = max(img.shape[0], img.shape[1])
475 # used for expanding the rotated image
476 # im_max_shape = max(img.shape[1], img.shape[0])
477 # print("max image shape: {}".format(im_max_shape))
478 # im_center = (im_max_shape/2, im_max_shape/2)
479 # translation = np.array(im_center) - np.array([img.shape[1]/2, img.shape[0]/2])
480 # print(translation)
481 # T = np.identity(3)
482 # # T[0:1,2] = translation
483 # T[0,2] = translation[0]
484 # T[1,2] = translation[1]
485 # M2 = np.identity(3)
486 # print("M: {}".format(M))
487 # M2[0:2,:] = M
488 # print("M2: {}".format(M2))
489 # M3 = np.dot(T, M2)
490 # print("M3: {}".format(M3))
491 # M1 = M3[0:2,:]
492 # print("M1: {}".format(M1))
494 if img_type == "PILATUS":
495 img = img.astype('float32')
496 if mask_thres == -999:
497 mask_thres = getMaskThreshold(img, img_type)
498 mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
499 mask[img <= mask_thres] = 255
500 rotated_img, center, rotMat = rotateNonSquareImage(img, angle, center)
501 rotated_mask, _, _ = rotateNonSquareImage(mask, angle, center)
502 rotated_mask[rotated_mask > 0.] = 255
503 rotated_img[rotated_mask > 0] = mask_thres
504 return rotated_img, center, rotMat
505 else:
506 return rotateNonSquareImage(img, angle, center)
508def rotateImageAboutPoint(img, point, angle, img_type, mask_thres = -999):
509 """
510 Get rotated image by angle about a given point.
511 :param img: input image
512 :param point: point to be rotated about
513 :param angle: rotation angle
514 :return: rotated image
515 """
516 if angle == 0:
517 return img
519 M = cv2.getRotationMatrix2D(tuple(point), angle, 1)
521 if img_type == "PILATUS":
522 img = img.astype('float32')
523 if mask_thres == -999:
524 mask_thres = getMaskThreshold(img, img_type)
525 mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
526 mask[img <= mask_thres] = 255
527 rotated_img = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
528 rotated_mask = cv2.warpAffine(mask, M, (img.shape[1], img.shape[0]))
529 rotated_mask[rotated_mask > 0.] = 255
530 rotated_img[rotated_mask > 0] = mask_thres
531 else:
532 rotated_img = cv2.warpAffine(img, M, (img.shape[1], img.shape[0]))
533 return rotated_img
535def rotatePoint(origin, point, angle):
536 """
537 Rotate a point counterclockwise by a given angle around a given origin.
538 The angle should be given in radians.
539 """
540 ox, oy = origin
541 px, py = point
543 qx = ox + np.cos(angle) * (px - ox) - np.sin(angle) * (py - oy)
544 qy = oy + np.sin(angle) * (px - ox) + np.cos(angle) * (py - oy)
545 return qx, qy
547def getMaskThreshold(img, img_type):
548 """
549 Compute the mask threshold for the image given
550 :param img, img_type:
551 :return: mask threshold
552 """
553 min_val = img.min()
554 if min_val < 0:
555 mask_thres = -0.01
556 else:
557 mask_thres = min_val
558 if img_type == "PILATUS":
559 hist = np.histogram(img, 3, (min_val, min_val+3))
560 max_ind = np.argmax(hist[0])
561 mask_thres = hist[1][max_ind]
562 return mask_thres
564###### White top hat image for Scanning Diffraction #########
565def gaussian(x, a, mean, sigma):
566 """
567 Find mean square error
568 """
569 return a*np.exp((-1.*(x-mean)**2)/(2*(sigma**2)))
571def getImgAfterWhiteTopHat(img, sigma=5):
572 """
573 Give the image after apply white to hat to it
574 """
575 tmpKernel = 1. / sigma ** 2 * np.ones((sigma, sigma))
576 dst = copy.copy(img)
577 dst = np.array(dst, np.float32)
578 for _ in range(2):
579 dst = cv2.filter2D(dst, cv2.CV_32F, tmpKernel, anchor=(-1, -1))
581 sigma = sigma * 6
582 x = np.array(range(-int(sigma + 1), int(sigma + 1) + 1, 1))
583 kernX = gaussian(x, 1, 0, sigma)
584 kernXY = np.outer(kernX, np.transpose(kernX))
585 tophat = white_tophat(dst, kernXY)
586 return tophat
588def kernelXY(sigma=5):
589 """
590 Give the kernel for XY
591 """
592 a = sigma * 6
593 x = np.array(range(-int(a + 1), int(a + 1) + 1, 1))
594 kernelX = 1. / (np.sqrt(2. * np.pi) * a) * np.exp(-(x - 0) ** 2. / (2. * a ** 2))
595 return np.outer(kernelX, np.transpose(kernelX))
598def display_test(img, name = "test", max_int = 100):
599 """
600 Display input image to screen. Just for test
601 :param img: input image
602 :param name: image name
603 :return: -
604 """
605 max_side = max(img.shape[:2])
606 ratio = 1.*650/max_side
607 size = (int(img.shape[1]*ratio),int(img.shape[0]*ratio))
608 img = get8bitImage(img, min=0.0, max=max_int)
609 img = cv2.resize(img, size)
610 cv2.imshow(name, img)
612def averageImages(file_list, rotate=False, preprocessed=False):
613 """
614 open images and average them all
615 WARNING: file_list is a list of string without preprocessed but it is a list of images with prepocessed
616 :param file_list: list of image path (str)
617 :return:
618 """
619 all_imgs = []
620 dims_match, max_dim, max_img_center = checkDimensionsMatch(file_list, preprocessed=preprocessed)
621 if not dims_match:
622 return expandAndAverageImages(file_list, max_dim, max_img_center, rotate, preprocessed=preprocessed)
623 for f in file_list:
624 if preprocessed:
625 img = f
626 else:
627 img = fabio.open(f).data
628 if img.shape == (1043, 981):
629 img_type = "PILATUS"
630 else:
631 img_type = "NORMAL"
632 if rotate:
633 print(f'Rotating and centering {f}')
634 center = getCenter(img)
635 angle = getRotationAngle(img, center, method=0)
636 img, center, _ = rotateImage(img, center, angle, img_type, mask_thres = -999)
637 all_imgs.append(img)
639 return np.mean(all_imgs, axis=0)
641def expandAndAverageImages(file_list, max_dim, max_img_center, rotate, preprocessed=False):
642 """
643 open images, expand to largest size and average them all
644 :param file_list: list of image path (str)
645 :param max_dim: dimension of largest image
646 :param max_img_center: center of largest image
647 :return:
648 """
649 all_imgs=[]
650 for f in file_list:
651 if preprocessed:
652 img = f
653 else:
654 img = fabio.open(f).data
656 if img.shape == (1043, 981):
657 img_type = "PILATUS"
658 else:
659 img_type = "NORMAL"
661 # Expand Image to max size by padding the surrounding by zeros and center of all image coincides
662 center = getCenter(img)
663 expanded_img = np.zeros(max_dim)
664 b, l = img.shape
665 expanded_img[0:b, 0:l] = img
666 transx = int((max_img_center[0] - center[0]))
667 transy = int((max_img_center[1] - center[1]))
668 M = np.float32([[1, 0, transx], [0, 1, transy]])
669 img = cv2.warpAffine(expanded_img, M, max_dim)
671 if rotate:
672 print(f'Rotating and centering {f}')
673 angle = getRotationAngle(img, max_img_center, method=0)
674 img, center, _ = rotateImage(img, max_img_center, angle, img_type, mask_thres = -999)
675 all_imgs.append(img)
677 return np.mean(all_imgs, axis=0)
679def checkDimensionsMatch(file_list, preprocessed=False):
680 """
681 Check whether dimensions of all the images match
682 :param file_list: list of image path (str)
683 :return: True if dimensions match
684 """
685 dims = []
686 for f in file_list:
687 if preprocessed:
688 img = f
689 else:
690 img = fabio.open(f).data
691 dims.append(img.shape)
692 max_dim = max(dims)
693 index = dims.index(max_dim)
694 if preprocessed:
695 max_img = file_list[index]
696 else:
697 max_img = fabio.open(file_list[index]).data
698 center = getCenter(max_img)
700 return dims.count(dims[0]) == len(dims), max_dim, center
702def processImageForIntCenter(img, center, img_type, mask_thres = -999):
703 """
704 Translate image such that the new center is an integer
705 :param file_list: original image and its center with decimals
706 :return: translated image and nearest integer center
707 """
708 img=img.astype('float32')
709 int_Center = (round(center[0]), round(center[1]))
710 tx = int_Center[0] - center[0]
711 ty = int_Center[1] - center[1]
712 M = np.float32([[1,0,tx],[0,1,ty]])
713 print("In process Image int center, translating original image by tx = " + str(tx) + " and ty = " + str(ty))
714 rows,cols = img.shape
716 if img_type == "PILATUS":
717 if mask_thres == -999:
718 mask_thres = getMaskThreshold(img, img_type)
719 mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
720 mask[img <= mask_thres] = 255
721 translated_Img = cv2.warpAffine(img, M, (cols,rows))
722 translated_mask = cv2.warpAffine(mask, M, (cols,rows))
723 translated_mask[translated_mask > 0.] = 255
724 translated_Img[translated_mask > 0] = mask_thres
725 else:
726 translated_Img = cv2.warpAffine(img,M,(cols,rows))
728 return (translated_Img, int_Center)
730def rotateNonSquareImage(img, angle, center1):
731 """
732 Rotates a non square image by first determining the appropriate square image and then rotating the image.
733 :param file_list: original non square image, angle of rotation and center
734 :return: rotated image and center with respect to new coordinate system
735 """
736 height, width = img.shape
737 center = (width/2, height/2)
739 rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.)
741 # rotation calculates the cos and sin, taking absolutes of those.
742 abs_cos = abs(rotation_mat[0,0])
743 abs_sin = abs(rotation_mat[0,1])
745 # find the new width and height bounds
746 bound_w = int(height * abs_sin + width * abs_cos)
747 bound_h = int(height * abs_cos + width * abs_sin)
749 # subtract old image center (bringing image back to origo) and adding the new image center coordinates
750 rotation_mat[0, 2] += bound_w/2 - center[0]
751 rotation_mat[1, 2] += bound_h/2 - center[1]
753 maxB = max(bound_h, bound_w)
755 center1 = [center1[0], center1[1], 1]
756 center1 = np.dot(rotation_mat, center1)
757 center2 = (int(center1[0]), int(center1[1]))
759 # rotate image with the new bounds and translated rotation matrix
760 rotated_img = cv2.warpAffine(img, rotation_mat, (maxB, maxB))
761 return rotated_img, center2, rotation_mat
763def mean_square_error(y_predict, y):
764 """
765 Find mean square error
766 """
767 loss = (y_predict - y)
768 mse = np.dot(loss.transpose(), loss) / y.shape[0]
769 rmse = np.sqrt(mse)
770 return rmse/(max(y)-min(y))