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

1""" 

2Copyright 1999 Illinois Institute of Technology 

3 

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: 

11 

12The above copyright notice and this permission notice shall be 

13included in all copies or substantial portions of the Software. 

14 

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. 

22 

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""" 

28 

29import copy 

30import cv2 

31import numpy as np 

32import fabio 

33from skimage.morphology import white_tophat 

34from pyFAI.azimuthalIntegrator import AzimuthalIntegrator 

35 

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) 

44 

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') 

58 

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 

71 

72 if min is None: 

73 min = img.min() 

74 cimg[cimg < min] = min 

75 

76 cimg -= min 

77 min = 0 

78 max = cimg.max() 

79 

80 if max <= min: 

81 img8bit = (cimg * 0.).astype('uint8') 

82 else: 

83 alpha = 255. / (max) 

84 img8bit = cv2.convertScaleAbs(cimg, alpha=alpha) 

85 

86 return img8bit 

87 

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 

96 

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) 

107 

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 

120 

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 

132 

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 

146 

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) 

156 

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 

169 

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 

179 

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) 

182 

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]) 

188 

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) 

193 

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])) 

205 

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 

219 

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) 

226 

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 

238 

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'])) 

247 

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]) 

254 

255 # If there's no method working return center of the image 

256 return (img.shape[1] / 2, img.shape[0] / 2) 

257 

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) 

267 

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 

274 

275 # Fit model using same gaussian 

276 x = hist[0] 

277 

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]) 

288 

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) 

293 

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) 

299 

300 return result.values 

301 

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 

317 

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 

326 

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 

357 

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 

376 

377 # Find angle with maximum intensity from Azimuthal integration 

378 if img.shape == (1043, 981): 

379 det = "pilatus1m" 

380 else: 

381 det = "agilent_titan" 

382 

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 

391 

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])) 

403 

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)) 

407 

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 

413 

414 # otherwise, return max degree 

415 return max_degree 

416 

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 

432 

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]) 

451 

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]) 

460 

461 return [(x1, x2), (y1, y2)] 

462 

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 

472 

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)) 

493 

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) 

507 

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 

518 

519 M = cv2.getRotationMatrix2D(tuple(point), angle, 1) 

520 

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 

534 

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 

542 

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 

546 

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 

563 

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))) 

570 

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)) 

580 

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 

587 

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)) 

596 

597 

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) 

611 

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) 

638 

639 return np.mean(all_imgs, axis=0) 

640 

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 

655 

656 if img.shape == (1043, 981): 

657 img_type = "PILATUS" 

658 else: 

659 img_type = "NORMAL" 

660 

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) 

670 

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) 

676 

677 return np.mean(all_imgs, axis=0) 

678 

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) 

699 

700 return dims.count(dims[0]) == len(dims), max_dim, center 

701 

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 

715 

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)) 

727 

728 return (translated_Img, int_Center) 

729 

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) 

738 

739 rotation_mat = cv2.getRotationMatrix2D(center, angle, 1.) 

740 

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]) 

744 

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) 

748 

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] 

752 

753 maxB = max(bound_h, bound_w) 

754 

755 center1 = [center1[0], center1[1], 1] 

756 center1 = np.dot(rotation_mat, center1) 

757 center2 = (int(center1[0]), int(center1[1])) 

758 

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 

762 

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))