Coverage for modules/ProjectionProcessor.py: 10%

393 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 pickle 

31from os.path import exists, isfile 

32import numpy as np 

33from lmfit import Model, Parameters 

34from lmfit.models import GaussianModel, VoigtModel 

35from sklearn.metrics import r2_score 

36import fabio 

37from musclex import __version__ 

38try: 

39 from ..utils.file_manager import fullPath, createFolder, ifHdfReadConvertless 

40 from ..utils.histogram_processor import movePeaks, getPeakInformations, convexHull 

41 from ..utils.image_processor import * 

42except: # for coverage 

43 from utils.file_manager import fullPath, createFolder, ifHdfReadConvertless 

44 from utils.histogram_processor import movePeaks, getPeakInformations, convexHull 

45 from utils.image_processor import * 

46 

47class ProjectionProcessor: 

48 """ 

49 A class for Bio-Muscle processing - go to process() to see all processing steps 

50 """ 

51 def __init__(self, dir_path, file_name, file_list=None, extension=''): 

52 self.dir_path = dir_path 

53 self.filename = file_name 

54 if extension in ('.hdf5', '.h5'): 

55 index = next((i for i, item in enumerate(file_list[0]) if item == file_name), 0) 

56 img = file_list[1][index] 

57 else: 

58 img = fabio.open(fullPath(dir_path, file_name)).data 

59 # if img.shape[1] > img.shape[0]: # image is longer than it is wide 

60 # img = cv2.copyMakeBorder(img, top=int((img.shape[1]-img.shape[0])/2), bottom=int((img.shape[1]-img.shape[0])/2), left=0, right=0, borderType=cv2.BORDER_CONSTANT) 

61 # else: 

62 # img = cv2.copyMakeBorder(img, top=0, bottom=0, left=int((img.shape[0]-img.shape[1])/2), right=int((img.shape[0]-img.shape[1])/2), borderType=cv2.BORDER_CONSTANT) 

63 # img -= img.min() 

64 self.orig_img = img 

65 self.orig_img = ifHdfReadConvertless(self.filename, self.orig_img) 

66 if self.orig_img.shape == (1043, 981): 

67 self.img_type = "PILATUS" 

68 else: 

69 self.img_type = "NORMAL" 

70 self.rotated_img = None 

71 self.rotated = False 

72 self.version = __version__ 

73 cache = self.loadCache() 

74 self.rotMat = None # store the rotation matrix used so that any point specified in current co-ordinate system can be transformed to the base (original image) co-ordinate system 

75 if cache is None: 

76 # info dictionary will save all results 

77 self.info = { 

78 'box_names' : set(), 

79 'boxes' : {}, 

80 'types' : {}, 

81 'hists' : {}, 

82 'peaks' : {}, 

83 'bgsubs' : {}, 

84 'hull_ranges':{}, 

85 'hists2': {}, 

86 'fit_results':{}, 

87 'subtracted_hists' : {}, 

88 'moved_peaks':{}, 

89 'baselines':{}, 

90 'centroids':{}, 

91 'widths': {}, 

92 'centerx': self.orig_img.shape[0] / 2 - 0.5, 

93 'centery': self.orig_img.shape[1] / 2 - 0.5, 

94 'rotationAngle' : 0 

95 } 

96 else: 

97 self.info = cache 

98 

99 def addBox(self, name, box, typ, bgsub): 

100 """ 

101 Add a box to info. If it exists and it changed, clear all old result 

102 :param name: box name 

103 :param box: box coordinates 

104 :param typ: box typ 'v' ad vertical, 'h' as horizontal 

105 :param bgsub: background subtraction method 0 = fitting gaussians, 1 = convex hull 

106 :return: 

107 """ 

108 box_names = self.info['box_names'] 

109 if name in box_names and typ =='oriented' and self.info['boxes'][name][:-1] != box[-1]: 

110 self.removeInfo(name) 

111 self.addBox(name, box, typ, bgsub) 

112 elif name in box_names and self.info['boxes'][name] != box: 

113 self.removeInfo(name) 

114 self.addBox(name, box, typ, bgsub) 

115 else: 

116 box_names.add(name) 

117 self.info['boxes'][name] = box 

118 self.info['types'][name] = typ 

119 self.info['bgsubs'][name] = bgsub 

120 

121 def addPeaks(self, name, peaks): 

122 """ 

123 Add peaks to a box. 

124 :param name: box name 

125 :param peaks: peaks 

126 :return: 

127 """ 

128 box_names = self.info['box_names'] 

129 if name in box_names: 

130 all_peaks = self.info['peaks'] 

131 if name in all_peaks and all_peaks[name] == peaks: 

132 return 

133 all_peaks[name] = peaks 

134 skip_list = ['box_names', 'boxes', 'types', 'peaks', 'hists', 'bgsubs'] 

135 for k in self.info.keys(): 

136 if k not in skip_list: 

137 self.removeInfo(name, k) 

138 else: 

139 print("Warning : box name is invalid.") 

140 

141 def removePeaks(self, name): 

142 """ 

143 Remove all peaks from a box 

144 :param name: box name 

145 :return: 

146 """ 

147 skip_list = ['box_names', 'boxes', 'types', 'bgsubs'] 

148 for k in self.info.keys(): 

149 if k not in skip_list: 

150 self.removeInfo(name, k) 

151 

152 def process(self, settings={}): 

153 """ 

154 All processing steps - all settings are provided by Projection Traces app as a dictionary 

155 """ 

156 self.updateSettings(settings) 

157 self.getHistograms() 

158 self.applyConvexhull() 

159 self.updateRotationAngle() 

160 self.fitModel() 

161 # self.getOtherResults() 

162 self.getBackgroundSubtractedHistograms() 

163 self.getPeakInfos() 

164 if 'no_cache' not in settings: 

165 self.cacheInfo() 

166 

167 def updateSettings(self, settings): 

168 """ 

169 Update info dict using settings 

170 :param settings: calibration settings 

171 :return: - 

172 """ 

173 if 'boxes' in settings: 

174 new_boxes = settings['boxes'] 

175 types = settings['types'] 

176 bgsubs = settings['bgsubs'] 

177 old_boxes = self.info['boxes'] 

178 all_name = list(new_boxes.keys()) 

179 all_name.extend(list(old_boxes.keys())) 

180 all_name = set(all_name) 

181 for name in all_name: 

182 if name in new_boxes.keys(): 

183 if 'refit' in settings: 

184 self.removeInfo(name, 'fit_results') 

185 self.addBox(name, new_boxes[name], types[name], bgsubs[name]) 

186 else: 

187 self.removeInfo(name) 

188 del settings['boxes'] 

189 del settings['types'] 

190 del settings['bgsubs'] 

191 

192 if 'peaks' in settings: 

193 new_peaks = settings['peaks'] 

194 old_peaks = self.info['peaks'] 

195 all_name = list(new_peaks.keys()) 

196 all_name.extend(list(old_peaks.keys())) 

197 all_name = set(all_name) 

198 for name in all_name: 

199 if name in new_peaks.keys(): 

200 self.addPeaks(name, new_peaks[name]) 

201 else: 

202 self.removePeaks(name) 

203 del settings['peaks'] 

204 

205 if 'hull_ranges' in settings: 

206 new = settings['hull_ranges'] 

207 current = self.info['hull_ranges'] 

208 current.update(new) 

209 del settings['hull_ranges'] 

210 

211 if 'rotated' in settings: 

212 self.rotated = settings['rotated'] 

213 else: 

214 self.rotated = False 

215 

216 self.info.update(settings) 

217 

218 if 'centerx' in self.info and 'centery' in self.info: 

219 if self.rotMat is not None: 

220 center = (self.info['centerx'], self.info['centery']) 

221 center = np.dot(cv2.invertAffineTransform(self.rotMat), [center[0], center[1], 1]) 

222 self.info['centerx'], self.info['centery'] = center[0], center[1] 

223 self.info['orig_center'] = (center[0], center[1]) 

224 else: 

225 self.info['centerx'] = self.orig_img.shape[0] / 2 - 0.5 

226 self.info['centery'] = self.orig_img.shape[1] / 2 - 0.5 

227 

228 def getHistograms(self): 

229 """ 

230 Obtain projected intensity for each box 

231 """ 

232 box_names = self.info['box_names'] 

233 if len(box_names) > 0: 

234 boxes = self.info['boxes'] 

235 types = self.info['types'] 

236 hists = self.info['hists'] 

237 for name in box_names: 

238 t = types[name] 

239 if self.rotated: 

240 img = self.getRotatedImage() 

241 else: 

242 img = copy.copy(self.orig_img) 

243 

244 if name not in hists: 

245 b = boxes[name] 

246 

247 if t == 'oriented': 

248 # rotate bottom left to new origin, then get top right 

249 # the box center 

250 cx, cy = b[6] 

251 rot_angle = b[5] 

252 img = rotateImageAboutPoint(img, (cx, cy), rot_angle, self.img_type) 

253 

254 # y is shape[0], x is shape[1]? 

255 x1 = np.max((int(b[0][0]), 0)) 

256 x2 = np.min((int(b[0][1]), img.shape[1])) 

257 y1 = np.max((int(b[1][0]), 0)) 

258 y2 = np.min((int(b[1][1]), img.shape[0])) 

259 

260 area = img[y1:y2+1, x1:x2+1] 

261 if t in ('h', 'oriented'): 

262 hist = np.sum(area, axis=0) 

263 else: 

264 hist = np.sum(area, axis=1) 

265 

266 hists[name] = hist 

267 

268 def applyConvexhull(self): 

269 """ 

270 Apply Convex hull to the projected intensity if background subtraction method is 1 (Convex hull) 

271 :return: 

272 """ 

273 box_names = self.info['box_names'] 

274 if len(box_names) > 0: 

275 boxes = self.info['boxes'] 

276 all_peaks = self.info['peaks'] 

277 hists = self.info['hists'] 

278 bgsubs = self.info['bgsubs'] 

279 hists2 = self.info['hists2'] 

280 types = self.info['types'] 

281 hull_ranges = self.info['hull_ranges'] 

282 for name in box_names: 

283 if name in hists2: 

284 continue 

285 

286 if bgsubs[name] == 1 and name in all_peaks and len(all_peaks[name]) > 0: 

287 # apply convex hull to the left and right if peaks are specified 

288 box = boxes[name] 

289 hist = hists[name] 

290 peaks = all_peaks[name] 

291 start_x = box[0][0] 

292 start_y = box[1][0] 

293 if types[name] == 'h': 

294 centerX = self.info['centerx'] - start_x 

295 elif types[name] == 'oriented': 

296 centerX = box[6][0] - start_x 

297 else: 

298 centerX = self.info['centery'] - start_y 

299 centerX = int(round(centerX)) 

300 right_hist = hist[centerX:] 

301 left_hist = hist[:centerX][::-1] 

302 min_len = min(len(right_hist), len(left_hist)) 

303 

304 if name not in hull_ranges: 

305 start = max(min(peaks) - 15, 10) 

306 end = min(max(peaks) + 15, min_len) 

307 hull_ranges[name] = (start, end) 

308 

309 # find start and end points 

310 (start, end) = hull_ranges[name] 

311 

312 left_hull = convexHull(left_hist, start, end)[::-1] 

313 right_hull = convexHull(right_hist, start, end) 

314 

315 hists2[name] = np.append(left_hull, right_hull) 

316 else: 

317 # use original histogram 

318 hists2[name] = copy.copy(hists[name]) 

319 

320 self.removeInfo(name, 'fit_results') 

321 

322 def updateRotationAngle(self): 

323 """ 

324 Find rotation angle of the diffraction. Turn the diffraction equator to be horizontal. 

325 The angle will be kept in self.info["rotationAngle"] 

326 """ 

327 if 'rotationAngle' not in self.info: 

328 center = (self.info['centerx'], self.info['centery']) 

329 img = copy.copy(self.orig_img) 

330 self.info['rotationAngle'] = getRotationAngle(img, center) 

331 

332 def fitModel(self): 

333 """ 

334 Fit model to histogram 

335 Fit results will be kept in self.info["fit_results"]. 

336 """ 

337 box_names = self.info['box_names'] 

338 all_hists = self.info['hists2'] 

339 bgsubs = self.info['bgsubs'] 

340 all_peaks = self.info['peaks'] 

341 all_boxes = self.info['boxes'] 

342 fit_results = self.info['fit_results'] 

343 

344 for name in box_names: 

345 hist = np.array(all_hists[name]) 

346 

347 if name not in all_peaks or len(all_peaks[name]) == 0 or name in fit_results: 

348 continue 

349 

350 peaks = all_peaks[name] 

351 box = all_boxes[name] 

352 start_x = box[0][0] 

353 start_y = box[1][0] 

354 

355 x = np.arange(0, len(hist)) 

356 

357 int_vars = { 

358 'x' : x 

359 } 

360 

361 # Initial Parameters 

362 params = Parameters() 

363 

364 # Init Center X 

365 if self.info['types'][name] == 'h': 

366 # init_center = self.orig_img.shape[1] / 2 - 0.5 - start_x 

367 init_center = self.info['centerx'] - start_x 

368 elif self.info['types'][name] == 'oriented': 

369 init_center = box[6][0] - start_x 

370 else: 

371 # init_center = self.orig_img.shape[0] / 2 - 0.5 - start_y 

372 init_center = self.info['centery'] - start_y 

373 

374 init_center = int(round(init_center)) 

375 params.add('centerX', init_center, min=init_center - 1., max=init_center + 1.) 

376 

377 if bgsubs[name] == 1: 

378 # Convex hull has been applied, so we don't need to fit 3 gaussian anymore 

379 int_vars['bg_line'] = 0 

380 int_vars['bg_sigma'] = 1 

381 int_vars['bg_amplitude'] = 0 

382 int_vars['center_sigma1'] = 1 

383 int_vars['center_amplitude1'] = 0 

384 int_vars['center_sigma2'] = 1 

385 int_vars['center_amplitude2'] = 0 

386 else: 

387 # Init linear background 

388 # params.add('bg_line', 0, min=0) 

389 int_vars['bg_line'] = 0 

390 

391 # Init background params 

392 params.add('bg_sigma', len(hist)/3., min=1, max=len(hist)*2+1.) 

393 params.add('bg_amplitude', 0, min=-1, max=sum(hist)+1.) 

394 

395 # Init Meridian params1 

396 params.add('center_sigma1', 15, min=1, max=len(hist)+1.) 

397 params.add('center_amplitude1', sum(hist) / 20., min=-1, max=sum(hist) + 1.) 

398 

399 # Init Meridian params2 

400 params.add('center_sigma2',5 , min=1, max=len(hist)+1.) 

401 params.add('center_amplitude2', sum(hist) / 20., min=-1, max=sum(hist)+1.) 

402 

403 # Init peaks params 

404 for j,p in enumerate(peaks): 

405 params.add('p_' + str(j), p, min=p - 10., max=p + 10.) 

406 params.add('sigma' + str(j), 10, min=1, max=50.) 

407 params.add('amplitude' + str(j), sum(hist)/10., min=-1) 

408 # params.add('gamma' + str(j), 0. , min=0., max=30) 

409 

410 # Fit model 

411 model = Model(layerlineModel, nan_policy='propagate', independent_vars=int_vars.keys()) 

412 result = model.fit(hist, verbose=False, params=params, **int_vars) 

413 if result is not None: 

414 result_dict = result.values 

415 int_vars.pop('x') 

416 result_dict.update(int_vars) 

417 result_dict['error'] = 1. - r2_score(hist, layerlineModel(x, **result_dict)) 

418 self.info['fit_results'][name] = result_dict 

419 self.removeInfo(name, 'subtracted_hists') 

420 print("Box : "+ str(name)) 

421 print("Fitting Result : " + str(self.info['fit_results'][name])) 

422 print("Fitting Error : " + str(self.info['fit_results'][name]['error'])) 

423 print("---") 

424 

425 

426 def getBackgroundSubtractedHistograms(self): 

427 """ 

428 Get Background Subtracted Histograms by subtract the original histogram by background from fitting model 

429 :return: 

430 """ 

431 box_names = self.info['box_names'] 

432 all_hists = self.info['hists2'] 

433 fit_results = self.info['fit_results'] 

434 subt_hists = self.info['subtracted_hists'] 

435 bgsubs = self.info['bgsubs'] 

436 for name in box_names: 

437 if name in subt_hists or name not in fit_results: 

438 continue 

439 if bgsubs[name] == 2: # no background, so there should be no subtraction 

440 subt_hists[name] = all_hists[name] 

441 self.removeInfo(name, 'moved_peaks') 

442 else: 

443 # Get subtracted histogram if fit result exists 

444 fit_result = fit_results[name] 

445 hist = all_hists[name] 

446 xs = np.arange(0, len(hist)) 

447 background = layerlineModelBackground(xs, **fit_result) 

448 subt_hists[name] = hist-background 

449 self.removeInfo(name, 'moved_peaks') 

450 

451 def getPeakInfos(self): 

452 """ 

453 Get peaks' infomation including baseline and centroids 

454 :return: 

455 """ 

456 box_names = self.info['box_names'] 

457 all_hists = self.info['subtracted_hists'] 

458 fit_results = self.info['fit_results'] 

459 moved_peaks = self.info['moved_peaks'] 

460 all_baselines = self.info['baselines'] 

461 all_centroids = self.info['centroids'] 

462 all_widths = self.info['widths'] 

463 

464 for name in box_names: 

465 if name not in all_hists: 

466 continue 

467 

468 ### Find real peak locations in the box (not distance from center) 

469 model = fit_results[name] 

470 hist = all_hists[name] 

471 if name not in moved_peaks: 

472 peaks = self.info['peaks'][name] 

473 moved = [] 

474 i = 0 

475 for p in peaks: 

476 globalpeak = int(round(model['centerX']+p)) 

477 if globalpeak <= len(hist): 

478 moved.append(globalpeak) 

479 else: 

480 moved.append(int(round(model['centerX']-p))) 

481 i+=1 

482 moved = movePeaks(hist, moved, 10) 

483 moved_peaks[name] = moved 

484 self.removeInfo(name, 'baselines') 

485 

486 peaks = moved_peaks[name] 

487 

488 ### Calculate Baselines 

489 if name not in all_baselines: 

490 baselines = [] 

491 for p in peaks: 

492 baselines.append(hist[p]*0.5) 

493 all_baselines[name] = baselines 

494 self.removeInfo(name, 'centroids') 

495 

496 baselines = all_baselines[name] 

497 

498 if name not in all_centroids: 

499 results = getPeakInformations(hist, peaks, baselines) 

500 all_centroids[name] = results['centroids'] - model['centerX'] 

501 all_widths[name] = results['widths'] 

502 

503 def setBaseline(self, box_name, peak_num, new_baseline): 

504 """ 

505 Change baseline and clear centroid and width for the specific box and peak 

506 :param box_name: box name (str) 

507 :param peak_num: peak name (int) 

508 :param new_baseline: new baseline value or percentage (str) 

509 :return: 

510 """ 

511 new_baseline = str(new_baseline) 

512 baselines = self.info['baselines'][box_name] 

513 peak = self.info['moved_peaks'][box_name][peak_num] 

514 hist = self.info['subtracted_hists'][box_name] 

515 height = hist[peak] 

516 if "%" in new_baseline: 

517 # if new_baseline contain "%", baseline value will use this as percent of peak height 

518 percent = float(new_baseline.rstrip("%")) 

519 baseline = height * percent / 100. 

520 elif len(new_baseline) == 0: 

521 # if new_baseline is empty, baseline will by half-height 

522 baseline = float(height * .5) 

523 else: 

524 baseline = float(new_baseline) 

525 

526 if height > baseline: 

527 baselines[peak_num] = baseline 

528 self.removeInfo(box_name, 'centroids') 

529 self.removeInfo(box_name, 'widths') 

530 

531 def getRotatedImage(self, img=None, angle=None): 

532 """ 

533 Get rotated image by angle. If the input params are not specified. image = original input image, angle = self.info["rotationAngle"] 

534 :param img: input image 

535 :param angle: rotation angle 

536 :return: rotated image 

537 """ 

538 if img is None: 

539 img = copy.copy(self.orig_img) 

540 if angle is None: 

541 angle = self.info['rotationAngle'] 

542 if '90rotation' in self.info and self.info['90rotation'] is True: 

543 angle = angle - 90 if angle > 90 else angle + 90 

544 

545 if self.rotated_img is None or self.rotated_img[0] != (self.info["centerx"], self.info["centery"]) or self.rotated_img[1] != self.info["rotationAngle"] or (self.rotated_img[2] != img).any(): 

546 # encapsulate rotated image for using later as a list of [center, angle, original image, rotated image[ 

547 center = (self.info["centerx"], self.info["centery"]) 

548 if "orig_center" in self.info: 

549 center = self.info["orig_center"] 

550 else: 

551 self.info["orig_center"] = center 

552 

553 rotImg, (self.info["centerx"], self.info["centery"]), self.rotMat = rotateImage(img, center, angle, self.img_type) 

554 self.rotated_img = [(self.info["centerx"], self.info["centery"]), angle, img, rotImg] 

555 

556 return self.rotated_img[3] 

557 

558 def removeInfo(self, name, k=None): 

559 """ 

560 Remove information from info dictionary by k as a key. If k is None, remove all information in the dictionary 

561 :param name: box name 

562 :param k: key of dictionary 

563 :return: - 

564 """ 

565 if k is not None and k in self.info: 

566 d = self.info[k] 

567 if isinstance(d, dict) and name in d: 

568 del d[name] 

569 

570 if k is None: 

571 keys = list(self.info.keys()) 

572 for key in keys: 

573 d = self.info[key] 

574 if key == 'box_names': 

575 d.remove(name) 

576 else: 

577 self.removeInfo(name, key) 

578 

579 def loadCache(self): 

580 """ 

581 Load info dict from cache. Cache file will be filename.info in folder "pt_cache" 

582 :return: cached info (dict) 

583 """ 

584 cache_path = fullPath(self.dir_path, "pt_cache") 

585 cache_file = fullPath(cache_path, self.filename + '.info') 

586 if exists(cache_path) and isfile(cache_file): 

587 cinfo = pickle.load(open(cache_file, "rb")) 

588 if cinfo is not None: 

589 if cinfo['program_version'] == self.version: 

590 return cinfo 

591 print("Cache version " + cinfo['program_version'] + " did not match with Program version " + self.version) 

592 print("Invalidating cache and reprocessing the image") 

593 return None 

594 

595 def cacheInfo(self): 

596 """ 

597 Save info dict to cache. Cache file will be save as filename.info in folder "pt_cache" 

598 :return: - 

599 """ 

600 cache_path = fullPath(self.dir_path, 'pt_cache') 

601 createFolder(cache_path) 

602 cache_file = fullPath(cache_path, self.filename + '.info') 

603 

604 self.info["program_version"] = self.version 

605 pickle.dump(self.info, open(cache_file, "wb")) 

606 

607 

608def layerlineModel(x, centerX, bg_line, bg_sigma, bg_amplitude, center_sigma1, center_amplitude1, 

609 center_sigma2, center_amplitude2, **kwargs): 

610 """ 

611 Model for fitting layer line pattern 

612 :param x: x axis 

613 :param centerX: center of x axis 

614 :param bg_line: linear background 

615 :param bg_sigma: background sigma 

616 :param bg_amplitude: background amplitude 

617 :param center_sigma1: meridian background sigma 

618 :param center_amplitude1: meridian background amplitude 

619 :param center_sigma2: meridian sigma 

620 :param center_amplitude2: meridian amplitude 

621 :param kwargs: other peaks properties 

622 :return: 

623 """ 

624 #### Background and Meridian 

625 result = layerlineModelBackground(x, centerX, bg_line, bg_sigma, bg_amplitude, center_sigma1, center_amplitude1, center_sigma2, center_amplitude2,**kwargs) 

626 #### Other peaks 

627 i = 0 

628 while 'p_'+str(i) in kwargs: 

629 p = kwargs['p_'+str(i)] 

630 sigma = kwargs['sigma'+str(i)] 

631 amplitude = kwargs['amplitude' + str(i)] 

632 if 'gamma' + str(i) in kwargs: 

633 gamma = kwargs['gamma' + str(i)] 

634 

635 mod = VoigtModel() 

636 result += mod.eval(x=x, amplitude=amplitude, center=centerX + p, sigma=sigma, gamma=gamma) 

637 result += mod.eval(x=x, amplitude=amplitude, center=centerX - p, sigma=sigma, gamma=-gamma) 

638 else: 

639 mod = GaussianModel() 

640 result += mod.eval(x=x, amplitude=amplitude, center=centerX + p, sigma=sigma) 

641 result += mod.eval(x=x, amplitude=amplitude, center=centerX - p, sigma=sigma) 

642 

643 i += 1 

644 return result 

645 

646def layerlineModelBackground(x, centerX, bg_line, bg_sigma, bg_amplitude, center_sigma1, center_amplitude1, 

647 center_sigma2, center_amplitude2,**kwargs): 

648 """ 

649 Model for fitting layer line pattern 

650 :param x: x axis 

651 :param centerX: center of x axis 

652 :param bg_line: linear background 

653 :param bg_sigma: background sigma 

654 :param bg_amplitude: background amplitude 

655 :param center_sigma1: meridian background sigma 

656 :param center_amplitude1: meridian background amplitude 

657 :param center_sigma2: meridian sigma 

658 :param center_amplitude2: meridian amplitude 

659 :param kwargs: nothing 

660 :return: 

661 """ 

662 return layerlineBackground(x, centerX, bg_line, bg_sigma, bg_amplitude) + \ 

663 meridianBackground(x, centerX, center_sigma1, center_amplitude1) + \ 

664 meridianGauss(x, centerX, center_sigma2, center_amplitude2) 

665 

666 

667def layerlineBackground(x, centerX, bg_line, bg_sigma, bg_amplitude, **kwargs): 

668 """ 

669 Model for largest background of layer line pattern 

670 :param x: x axis 

671 :param centerX: center of x axis 

672 :param bg_line: linear background 

673 :param bg_sigma: background sigma 

674 :param bg_amplitude: background amplitude 

675 :param kwargs: nothing 

676 :return: 

677 """ 

678 mod = GaussianModel() 

679 return mod.eval(x=x, amplitude=bg_amplitude, center=centerX, sigma=bg_sigma) + bg_line 

680 

681def meridianBackground(x, centerX, center_sigma1, center_amplitude1, **kwargs): 

682 """ 

683 Model for background of meridian of layer line pattern 

684 :param x: x axis 

685 :param centerX: center of x axis 

686 :param center_sigma1: meridian background sigma 

687 :param center_amplitude1: meridian background amplitude 

688 :param kwargs: nothing 

689 :return: 

690 """ 

691 mod = GaussianModel() 

692 return mod.eval(x=x, amplitude=center_amplitude1, center=centerX, sigma=center_sigma1) 

693 

694def meridianGauss(x, centerX, center_sigma2, center_amplitude2, **kwargs): 

695 """ 

696 Model for background of layer line pattern 

697 :param x: x axis 

698 :param centerX: center of x axis 

699 :param center_sigma2: meridian sigma 

700 :param center_amplitude2: meridian amplitude 

701 :param kwargs: 

702 :return: 

703 """ 

704 mod = GaussianModel() 

705 return mod.eval(x=x, amplitude=center_amplitude2, center=centerX, sigma=center_sigma2)