Coverage for /usr/local/lib/python3.10/site-packages/spam/helpers/tsvio.py: 97%

353 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-24 17:26 +0000

1""" 

2Library of SPAM functions for reading and writing TSV files. 

3Copyright (C) 2020 SPAM Contributors 

4 

5This program is free software: you can redistribute it and/or modify it 

6under the terms of the GNU General Public License as published by the Free 

7Software Foundation, either version 3 of the License, or (at your option) 

8any later version. 

9 

10This program is distributed in the hope that it will be useful, but WITHOUT 

11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 

12FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 

13more details. 

14 

15You should have received a copy of the GNU General Public License along with 

16this program. If not, see <http://www.gnu.org/licenses/>. 

17""" 

18 

19import os 

20 

21import numpy 

22 

23 

24def writeRegistrationTSV(fileName, regCentre, regReturns): 

25 """ 

26 This function writes a correctly formatted TSV file from the result of a single `register()` call, allowing it to be used as an initial registration. 

27 

28 Parameters 

29 ---------- 

30 fileName : string 

31 The file name for output, suggestion: it should probably end with ".tsv" 

32 

33 regCentre : 3-component list 

34 A list containing the point at which `Phi` has been measured. 

35 This is typically the middle of the image, and can be obtained as follows: 

36 (numpy.array( im.shape )-1)/2.0 

37 The conversion to a numpy array is necessary, since tuples cannot be divided by a number directly. 

38 

39 regReturns : dictionary 

40 This should be the return dictionary from `register`. 

41 From this dictionary will be extracted: 'Phi', 'error', 'iterations', 'returnStatus', 'deltaPhiNorm' 

42 

43 """ 

44 try: 

45 regPhi = regReturns["Phi"] 

46 except: 

47 print("spam.helpers.tsvio.writeRegistrationTSV(): Attempting to read old format") 

48 regPhi = regReturns["PhiCentre"] 

49 

50 # catch 2D images 

51 if len(regCentre) == 2: 

52 regCentre = [0, regCentre[0], regCentre[1]] 

53 

54 # Make one big array for writing: 

55 header = "NodeNumber\tZpos\tYpos\tXpos\tFzz\tFzy\tFzx\tZdisp\tFyz\tFyy\tFyx\tYdisp\tFxz\tFxy\tFxx\tXdisp\terror\titerations\treturnStatus\tdeltaPhiNorm" 

56 try: 

57 outMatrix = numpy.array( 

58 [ 

59 [1], 

60 [regCentre[0]], 

61 [regCentre[1]], 

62 [regCentre[2]], 

63 [regPhi[0, 0]], 

64 [regPhi[0, 1]], 

65 [regPhi[0, 2]], 

66 [regPhi[0, 3]], 

67 [regPhi[1, 0]], 

68 [regPhi[1, 1]], 

69 [regPhi[1, 2]], 

70 [regPhi[1, 3]], 

71 [regPhi[2, 0]], 

72 [regPhi[2, 1]], 

73 [regPhi[2, 2]], 

74 [regPhi[2, 3]], 

75 [regReturns["error"]], 

76 [regReturns["iterations"]], 

77 [regReturns["returnStatus"]], 

78 [regReturns["deltaPhiNorm"]], 

79 ] 

80 ) 

81 except: 

82 print("spam.helpers.tsvio.writeRegistrationTSV(): Attempting to read old format") 

83 outMatrix = numpy.array( 

84 [ 

85 [1], 

86 [regCentre[0]], 

87 [regCentre[1]], 

88 [regCentre[2]], 

89 [regPhi[0, 0]], 

90 [regPhi[0, 1]], 

91 [regPhi[0, 2]], 

92 [regPhi[0, 3]], 

93 [regPhi[1, 0]], 

94 [regPhi[1, 1]], 

95 [regPhi[1, 2]], 

96 [regPhi[1, 3]], 

97 [regPhi[2, 0]], 

98 [regPhi[2, 1]], 

99 [regPhi[2, 2]], 

100 [regPhi[2, 3]], 

101 [regReturns["error"]], 

102 [regReturns["iterationNumber"]], 

103 [regReturns["returnStatus"]], 

104 [regReturns["deltaPhiNorm"]], 

105 ] 

106 ) 

107 

108 numpy.savetxt(fileName, outMatrix.T, fmt="%.7f", delimiter="\t", newline="\n", comments="", header=header) 

109 

110 

111def writeStrainTSV(fileName, points, decomposedFfield, firstColumn="StrainPointNumber", startRow=0): 

112 """ 

113 This function writes strains to a TSV file, hiding the complexity of counting and naming columns 

114 

115 Parameters 

116 ---------- 

117 fileName : string 

118 fileName including full path and .tsv at the end to write 

119 

120 points : Nx3 numpy array 

121 Points at which the strain is defined 

122 

123 decomposedFfield : dictionary 

124 Dictionary containing strain components as per output from spam.deformation.FfieldRegularQ8, FfieldRegularGeers or FfieldBagi 

125 

126 firstColumn : string, optional 

127 How to name the first column (series number) of the TSV 

128 Default = "StrainPointNumber" 

129 

130 startRow : int, optional 

131 Are your points and strains offset from zero? Offset TSV by adding blank lines, don't use this if you don't know what you're doing 

132 Default = 0 

133 

134 Returns 

135 ------- 

136 None 

137 """ 

138 # This is the minimum header for everyone 

139 header = "{}\tZpos\tYpos\tXpos".format(firstColumn) 

140 

141 # Allocate minimum output array 

142 outMatrix = numpy.array([numpy.arange(points.shape[0]), points[:, 0], points[:, 1], points[:, 2]]).T 

143 

144 nCols = 4 

145 

146 for component in decomposedFfield.keys(): 

147 if component == "vol" or component == "dev" or component == "volss" or component == "devss": 

148 header = header + "\t{}".format(component) 

149 outMatrix = numpy.hstack([outMatrix, numpy.array([decomposedFfield[component].ravel()]).T]) 

150 nCols += 1 

151 

152 if component == "r" or component == "z": 

153 for n, di in enumerate(["z", "y", "x"]): 

154 header = header + "\t{}{}".format(component, di) 

155 outMatrix = numpy.hstack([outMatrix, numpy.array([decomposedFfield[component].reshape(-1, 3)[:, n].ravel()]).T]) 

156 nCols += 1 

157 

158 if component == "e" or component == "U": 

159 for n, di in enumerate(["z", "y", "x"]): 

160 for m, dj in enumerate(["z", "y", "x"]): 

161 if m >= n: 

162 header = header + "\t{}{}{}".format(component, di, dj) 

163 outMatrix = numpy.hstack([outMatrix, numpy.array([decomposedFfield[component].reshape(-1, 3, 3)[:, n, m].ravel()]).T]) 

164 nCols += 1 

165 

166 # This is mostly for discrete Strains, where we can avoid or not the zero-numbered grain 

167 if startRow > 0: 

168 for i in range(startRow): 

169 header = header + "\n0.0" 

170 for j in range(1, nCols): 

171 header = header + "\t0.0" 

172 

173 numpy.savetxt(fileName, outMatrix, delimiter="\t", fmt="%.7f", newline="\n", comments="", header=header) 

174 

175 

176def readCorrelationTSV(fileName, fieldBinRatio=1.0, readOnlyDisplacements=False, readConvergence=True, readPixelSearchCC=False, readError=False, readLabelDilate=False, verbose=True): 

177 """ 

178 This function reads a TSV file containing a field of deformation functions **Phi** at one or a number of points. 

179 This is typically the output of the spam-ldic and spam-ddic scripts, 

180 or anything written by `writeRegistrationTSV`. 

181 

182 Parameters 

183 ---------- 

184 fileName : string 

185 Name of the file 

186 

187 fieldBinRatio : int, optional 

188 if the input field is refer to a binned version of the image 

189 `e.g.`, if ``fieldBinRatio = 2`` the field_name values have been calculated 

190 for an image half the size of what the returned PhiField is referring to 

191 Default = 1.0 

192 

193 readOnlyDisplacements : bool, optional 

194 Read "zDisp", "yDisp", "xDisp", displacements from the TSV file, and not the rest of the Phi matrix? 

195 Default = False 

196 

197 readConvergence : bool, optional 

198 Read "returnStatus", "deltaPhiNorm", "iterations", from file 

199 Default = True 

200 

201 readPixelSearchCC : bool, optional 

202 Read "pixelSearchCC" from file 

203 Default = False 

204 

205 readError : bool, optional 

206 Read '"error"from file 

207 Default = False 

208 

209 readLabelDilate : bool, optional 

210 Read "LabelDilate" from file 

211 Default = False 

212 

213 Returns 

214 ------- 

215 Dictionary containing: 

216 fieldDims: 1x3 array of the field dimensions (ZYX) (for a regular grid DIC result) 

217 

218 numberOfLabels: number of labels (for a discrete DIC result) 

219 

220 fieldCoords: nx3 array of n points coordinates (ZYX) 

221 

222 PhiField: nx4x4 array of n points transformation operators 

223 

224 returnStatus: nx1 array of n points returnStatus from the correlation 

225 

226 deltaPhiNorm: nx1 array of n points deltaPhiNorm from the correlation 

227 

228 iterations: nx1 array of n points iterations from the correlation 

229 

230 pixelSearchCC: nx1 array of n points pixelSearchCC from the correlation 

231 

232 error: nx1 array of n points error from the correlation 

233 

234 labelDilate: nx1 array of n points labelDilate from the correlation 

235 

236 """ 

237 if not os.path.isfile(fileName): 

238 print("\n\tspam.tsvio.readCorrelationTSV(): {} is not a file. Exiting.".format(fileName)) 

239 return 

240 

241 f = numpy.genfromtxt(fileName, delimiter="\t", names=True) 

242 # RS = [] 

243 # deltaPhiNorm = [] 

244 # pixelSearchCC = [] 

245 

246 # If this is a one-line TSV file (an initial registration for example) 

247 if f.size == 1: 

248 # print("\tspam.tsvio.readCorrelationTSV(): {} seems only to have one line.".format(fileName)) 

249 nPoints = 1 

250 numberOfLabels = 1 

251 fieldDims = [1, 1, 1] 

252 

253 # Sort out the field coordinates 

254 fieldCoords = numpy.zeros((nPoints, 3)) 

255 fieldCoords[:, 0] = f["Zpos"] * fieldBinRatio 

256 fieldCoords[:, 1] = f["Ypos"] * fieldBinRatio 

257 fieldCoords[:, 2] = f["Xpos"] * fieldBinRatio 

258 

259 # Sort out the components of Phi 

260 PhiField = numpy.zeros((nPoints, 4, 4)) 

261 PhiField[0] = numpy.eye(4) 

262 

263 # Fill in displacements 

264 try: 

265 PhiField[0, 0, 3] = f["Zdisp"] * fieldBinRatio 

266 PhiField[0, 1, 3] = f["Ydisp"] * fieldBinRatio 

267 PhiField[0, 2, 3] = f["Xdisp"] * fieldBinRatio 

268 except ValueError: 

269 PhiField[0, 0, 3] = f["F14"] * fieldBinRatio 

270 PhiField[0, 1, 3] = f["F24"] * fieldBinRatio 

271 PhiField[0, 2, 3] = f["F34"] * fieldBinRatio 

272 

273 if not readOnlyDisplacements: 

274 try: 

275 # Get non-displacement components 

276 PhiField[0, 0, 0] = f["Fzz"] 

277 PhiField[0, 0, 1] = f["Fzy"] 

278 PhiField[0, 0, 2] = f["Fzx"] 

279 PhiField[0, 1, 0] = f["Fyz"] 

280 PhiField[0, 1, 1] = f["Fyy"] 

281 PhiField[0, 1, 2] = f["Fyx"] 

282 PhiField[0, 2, 0] = f["Fxz"] 

283 PhiField[0, 2, 1] = f["Fxy"] 

284 PhiField[0, 2, 2] = f["Fxx"] 

285 except: 

286 print("spam.helpers.tsvio.readCorrelationTSV(): Attempting to read old format, please update your TSV file (F11 should be Fzz and so on)") 

287 # Get non-displacement components 

288 PhiField[0, 0, 0] = f["F11"] 

289 PhiField[0, 0, 1] = f["F12"] 

290 PhiField[0, 0, 2] = f["F13"] 

291 PhiField[0, 1, 0] = f["F21"] 

292 PhiField[0, 1, 1] = f["F22"] 

293 PhiField[0, 1, 2] = f["F23"] 

294 PhiField[0, 2, 0] = f["F31"] 

295 PhiField[0, 2, 1] = f["F32"] 

296 PhiField[0, 2, 2] = f["F33"] 

297 

298 if readConvergence: 

299 try: 

300 # Return ReturnStatus, SubPixelDeltaFnorm, SubPixelIterations 

301 RS = f["returnStatus"] 

302 deltaPhiNorm = f["deltaPhiNorm"] 

303 iterations = f["iterations"] 

304 except: 

305 print("spam.helpers.tsvio.readCorrelationTSV(): Attempting to read old format, please update your TSV file (SubPix should be )") 

306 # Return ReturnStatus, SubPixelDeltaFnorm, SubPixelIterations 

307 RS = f["SubPixReturnStat"] 

308 deltaPhiNorm = f["SubPixDeltaPhiNorm"] 

309 iterations = f["SubPixIterations"] 

310 if readError: 

311 try: 

312 error = f["error"] 

313 except ValueError: 

314 pass 

315 # Return pixelSearchCC 

316 if readPixelSearchCC: 

317 pixelSearchCC = numpy.zeros(nPoints) 

318 try: 

319 pixelSearchCC = f["pixelSearchCC"] 

320 except ValueError: 

321 pass 

322 

323 # Return error 

324 if readError: 

325 error = numpy.zeros(nPoints) 

326 try: 

327 error = f["error"] 

328 except ValueError: 

329 pass 

330 # Return labelDilate 

331 if readLabelDilate: 

332 labelDilate = numpy.zeros(nPoints) 

333 try: 

334 labelDilate = f["LabelDilate"] 

335 except ValueError: 

336 pass 

337 

338 pixelSearchCC = 0 

339 

340 # there is more than one line in the TSV file -- a field -- typical case 

341 else: 

342 nPoints = f.size 

343 

344 # check if it is a ddic or ldic result 

345 try: 

346 f["NodeNumber"] 

347 # it's a local DIC result with grid points regularly spaced 

348 DICgrid = True 

349 DICdiscrete = False 

350 except ValueError: 

351 # it's a discrete DIC result with values in each label's centre of mass 

352 DICdiscrete = True 

353 DICgrid = False 

354 

355 # Sort out the field coordinates 

356 fieldCoords = numpy.zeros((nPoints, 3)) 

357 fieldCoords[:, 0] = f["Zpos"] * fieldBinRatio 

358 fieldCoords[:, 1] = f["Ypos"] * fieldBinRatio 

359 fieldCoords[:, 2] = f["Xpos"] * fieldBinRatio 

360 

361 if DICgrid: 

362 fieldDims = numpy.array([len(numpy.unique(f["Zpos"])), len(numpy.unique(f["Ypos"])), len(numpy.unique(f["Xpos"]))]) 

363 numberOfLabels = 0 

364 if verbose: 

365 print("\tspam.tsvio.readCorrelationTSV(): Field dimensions: {}".format(fieldDims)) 

366 elif DICdiscrete: 

367 numberOfLabels = len(f["Label"]) 

368 fieldDims = [0, 0, 0] 

369 if verbose: 

370 print("\tspam.tsvio.readCorrelationTSV(): Number of labels: {}".format(numberOfLabels)) 

371 

372 # create ReturnStatus and deltaPhiNorm matrices if asked 

373 if readConvergence: 

374 try: 

375 RS = numpy.zeros(nPoints) 

376 RS[:] = f[:]["returnStatus"] 

377 deltaPhiNorm = numpy.zeros(nPoints) 

378 deltaPhiNorm = f[:]["deltaPhiNorm"] 

379 iterations = numpy.zeros(nPoints) 

380 iterations = f[:]["iterations"] 

381 except: 

382 print("spam.helpers.tsvio.readCorrelationTSV(): Attempting to read old format, please update your TSV file") 

383 RS = numpy.zeros(nPoints) 

384 RS[:] = f[:]["SubPixReturnStat"] 

385 deltaPhiNorm = numpy.zeros(nPoints) 

386 deltaPhiNorm = f[:]["SubPixDeltaPhiNorm"] 

387 iterations = numpy.zeros(nPoints) 

388 iterations = f[:]["SubPixIterations"] 

389 

390 # Return pixelSearchCC 

391 if readPixelSearchCC: 

392 pixelSearchCC = numpy.zeros(nPoints) 

393 try: 

394 pixelSearchCC = f[:]["pixelSearchCC"] 

395 except ValueError: 

396 pass 

397 

398 # Return error 

399 if readError: 

400 error = numpy.zeros(nPoints) 

401 try: 

402 error = f[:]["error"] 

403 except ValueError: 

404 pass 

405 # Return labelDilate 

406 if readLabelDilate: 

407 labelDilate = numpy.zeros(nPoints) 

408 try: 

409 labelDilate = f[:]["LabelDilate"] 

410 except ValueError: 

411 pass 

412 

413 # Sort out the components of Phi 

414 PhiField = numpy.zeros((nPoints, 4, 4)) 

415 for n in range(nPoints): 

416 # Initialise with Identity matrix 

417 PhiField[n] = numpy.eye(4) 

418 

419 # Fill in displacements 

420 try: 

421 PhiField[n, 0, 3] = f[n]["Zdisp"] * fieldBinRatio 

422 PhiField[n, 1, 3] = f[n]["Ydisp"] * fieldBinRatio 

423 PhiField[n, 2, 3] = f[n]["Xdisp"] * fieldBinRatio 

424 except ValueError: 

425 PhiField[n, 0, 3] = f[n]["F14"] * fieldBinRatio 

426 PhiField[n, 1, 3] = f[n]["F24"] * fieldBinRatio 

427 PhiField[n, 2, 3] = f[n]["F34"] * fieldBinRatio 

428 

429 if not readOnlyDisplacements: 

430 try: 

431 # Get non-displacement components 

432 PhiField[n, 0, 0] = f[n]["Fzz"] 

433 PhiField[n, 0, 1] = f[n]["Fzy"] 

434 PhiField[n, 0, 2] = f[n]["Fzx"] 

435 PhiField[n, 1, 0] = f[n]["Fyz"] 

436 PhiField[n, 1, 1] = f[n]["Fyy"] 

437 PhiField[n, 1, 2] = f[n]["Fyx"] 

438 PhiField[n, 2, 0] = f[n]["Fxz"] 

439 PhiField[n, 2, 1] = f[n]["Fxy"] 

440 PhiField[n, 2, 2] = f[n]["Fxx"] 

441 except: 

442 print("spam.helpers.tsvio.readCorrelationTSV(): Attempting to read old format, please update your TSV file (F11 should be Fzz and so on)") 

443 # Get non-displacement components 

444 PhiField[n, 0, 0] = f[n]["F11"] 

445 PhiField[n, 0, 1] = f[n]["F12"] 

446 PhiField[n, 0, 2] = f[n]["F13"] 

447 PhiField[n, 1, 0] = f[n]["F21"] 

448 PhiField[n, 1, 1] = f[n]["F22"] 

449 PhiField[n, 1, 2] = f[n]["F23"] 

450 PhiField[n, 2, 0] = f[n]["F31"] 

451 PhiField[n, 2, 1] = f[n]["F32"] 

452 PhiField[n, 2, 2] = f[n]["F33"] 

453 

454 output = {"fieldDims": fieldDims, "numberOfLabels": numberOfLabels, "fieldCoords": fieldCoords} 

455 if readConvergence: 

456 output.update({"returnStatus": RS, "deltaPhiNorm": deltaPhiNorm, "iterations": iterations}) 

457 if readError: 

458 output.update({"error": error}) 

459 if readPixelSearchCC: 

460 output.update({"pixelSearchCC": pixelSearchCC}) 

461 if readLabelDilate: 

462 output.update({"LabelDilate": labelDilate}) 

463 

464 if readOnlyDisplacements: 

465 output.update({"displacements": PhiField[:, 0:3, -1]}) 

466 else: 

467 output.update({"PhiField": PhiField}) 

468 

469 return output 

470 

471 

472def readStrainTSV(fileName): 

473 """ 

474 This function reads a strain TSV file written by `spam-discreteStrain` or `spam-regularStrain` 

475 

476 Parameters 

477 ---------- 

478 fileName : string 

479 Name of the file 

480 

481 Returns 

482 ------- 

483 Dictionary containing: 

484 

485 fieldDims: 1x3 array of the field dimensions (ZYX) 

486 

487 fieldCoords : nx3 array of the field coordinates (ZYX) 

488 

489 numberOfLabels: number of labels (for a discrete strain result) 

490 

491 vol: nx1 array of n points with volumetric strain computed under the hypotesis of large strains (if computed) 

492 

493 dev: nx1 array of n points with deviatoric strain computed under the hypotesis of large strains (if computed) 

494 

495 volss: nx1 array of n points with volumetric strain computed under the hypotesis of small strains (if computed) 

496 

497 devss: nx1 array of n points with deviatoric strain computed under the hypotesis of small strains (if computed) 

498 

499 r : nx3 array of n points with the components of the rotation vector (if computed) 

500 

501 z : nx3 array of n points with the components of the zoom vector (if computed) 

502 

503 U : nx3x3 array of n points with the components of the right-hand stretch tensor (if computed) 

504 

505 e : nx3x3 array of n points with the components of the strain tensor in small strains (if computed) 

506 

507 """ 

508 

509 if not os.path.isfile(fileName): 

510 print("\n\tspam.tsvio.readStrainTSV(): {} is not a file. Exiting.".format(fileName)) 

511 return 

512 # Read the TSV 

513 f = numpy.genfromtxt(fileName, delimiter="\t", names=True) 

514 

515 # Number of points 

516 nPoints = f.size 

517 

518 # Get keys from file 

519 keys = f.dtype.names 

520 

521 # Create empyt dictionary to be filled 

522 output = {} 

523 

524 # Read and add the label coordinates 

525 fieldCoords = numpy.zeros((nPoints, 3)) 

526 fieldCoords[:, 0] = f["Zpos"] 

527 fieldCoords[:, 1] = f["Ypos"] 

528 fieldCoords[:, 2] = f["Xpos"] 

529 output["fieldCoords"] = fieldCoords 

530 

531 # Check if we are working with a regular grid or discrete 

532 grid = False 

533 if numpy.abs(fieldCoords[2, 0] - fieldCoords[3, 0]) == 0: 

534 grid = True 

535 else: 

536 pass 

537 

538 if grid: 

539 fieldDims = numpy.array([len(numpy.unique(f["Zpos"])), len(numpy.unique(f["Ypos"])), len(numpy.unique(f["Xpos"]))]) 

540 output["fieldDims"] = fieldDims 

541 output["numberOfLabels"] = 0 

542 else: 

543 output["fieldDims"] = [0, 0, 0] 

544 output["numberOfLabels"] = nPoints 

545 

546 # Check for all the possible keys 

547 if "vol" in keys: 

548 volStrain = numpy.zeros((nPoints, 1)) 

549 volStrain[:, 0] = f["vol"] 

550 output["vol"] = volStrain 

551 

552 if "dev" in keys: 

553 devStrain = numpy.zeros((nPoints, 1)) 

554 devStrain[:, 0] = f["dev"] 

555 output["dev"] = devStrain 

556 

557 if "volss" in keys: 

558 volss = numpy.zeros((nPoints, 1)) 

559 volss[:, 0] = f["volss"] 

560 output["volss"] = volss 

561 

562 if "devss" in keys: 

563 devss = numpy.zeros((nPoints, 1)) 

564 devss[:, 0] = f["devss"] 

565 output["devss"] = devss 

566 

567 if "rz" in keys: 

568 r = numpy.zeros((nPoints, 3)) 

569 r[:, 0] = f["rz"] 

570 r[:, 1] = f["ry"] 

571 r[:, 2] = f["rx"] 

572 output["r"] = r 

573 

574 if "zz" in keys: 

575 # Zooms, these are very badly named like this 

576 z = numpy.zeros((nPoints, 3)) 

577 z[:, 0] = f["zz"] 

578 z[:, 1] = f["zy"] 

579 z[:, 2] = f["zx"] 

580 output["z"] = z 

581 

582 if "Uzz" in keys: 

583 # Symmetric, so fill in both sides 

584 U = numpy.zeros((nPoints, 3, 3)) 

585 U[:, 0, 0] = f["Uzz"] 

586 U[:, 1, 1] = f["Uyy"] 

587 U[:, 2, 2] = f["Uxx"] 

588 U[:, 0, 1] = f["Uzy"] 

589 U[:, 1, 0] = f["Uzy"] 

590 U[:, 0, 2] = f["Uzx"] 

591 U[:, 2, 0] = f["Uzx"] 

592 U[:, 1, 2] = f["Uyx"] 

593 U[:, 2, 1] = f["Uyx"] 

594 output["U"] = U 

595 

596 if "ezz" in keys: 

597 # Symmetric, so fill in both sides 

598 e = numpy.zeros((nPoints, 3, 3)) 

599 e[:, 0, 0] = f["ezz"] 

600 e[:, 1, 1] = f["eyy"] 

601 e[:, 2, 2] = f["exx"] 

602 e[:, 0, 1] = f["ezy"] 

603 e[:, 1, 0] = f["ezy"] 

604 e[:, 0, 2] = f["ezx"] 

605 e[:, 2, 0] = f["ezx"] 

606 e[:, 1, 2] = f["eyx"] 

607 e[:, 2, 1] = f["eyx"] 

608 output["e"] = e 

609 

610 return output 

611 

612 

613def TSVtoTIFF(fileName, fieldBinRatio=1.0, lab=None, returnRS=False, outDir=None, prefix=None): 

614 """ 

615 This function converts a TSV file (typically the output of spam-ldic and spam-ddic scripts) 

616 to a tiff file for visualising the deformation field. 

617 

618 Parameters 

619 ---------- 

620 fileName : string 

621 Name of the file 

622 

623 fieldBinRatio : int, optional 

624 if the input field is refer to a binned version of the image 

625 `e.g.`, if ``fieldBinRatio = 2`` the field_name values have been calculated 

626 for an image half the size of what the returned PhiField is referring to 

627 Default = 1.0 

628 

629 lab : 3D numpy array, optional 

630 The labelled image of the reference state. Highly recommended argument in case of a discrete correlation result. 

631 Default = None 

632 

633 returnRS : bool, optional 

634 if True: will return the returnStatus of the correlation as a tiff file 

635 Default = False 

636 

637 outDir : string, optional 

638 Output directory 

639 Default is directory of the input field file 

640 

641 prefix : string, optional 

642 Prefix for output files 

643 Default is the basename of the input field file (without extension) 

644 """ 

645 

646 import tifffile 

647 

648 # use the helper function to read the TSV file 

649 fi = readCorrelationTSV(fileName, fieldBinRatio=fieldBinRatio, readOnlyDisplacements=True, readConvergence=returnRS) 

650 displacements = fi["displacements"] 

651 PhiComponents = [["Zdisp", 0], ["Ydisp", 1], ["Xdisp", 2]] 

652 

653 # set output directory if none 

654 if outDir is None: 

655 if os.path.dirname(fileName) == "": 

656 outDir = "./" 

657 else: 

658 outDir = os.path.dirname(fileName) 

659 else: 

660 os.makedirs(outDir) 

661 

662 # output file name prefix 

663 if prefix is None: 

664 prefix = os.path.splitext(os.path.basename(fileName))[0] 

665 

666 # check if it is a ddic result 

667 if fi["numberOfLabels"] != 0: 

668 if lab: 

669 labelled = tifffile.imread(lab) 

670 import spam.label 

671 

672 for component in PhiComponents: 

673 tifffile.imwrite("{}/{}-{}.tif".format(outDir, prefix, component[0]), spam.label.convertLabelToFloat(labelled, displacements[:, component[1]]).astype("<f4")) 

674 if returnRS: 

675 tifffile.imwrite("{}/{}-RS.tif".format(outDir, prefix), spam.label.convertLabelToFloat(labelled, fi["returnStatus"]).astype("<f4")) 

676 else: 

677 print("\tspam.tsvio.TSVtoTIFF(): The labelled image of the reference state is needed as input. Exiting.") 

678 return 

679 

680 # if not, is a ldic result 

681 else: 

682 dims = fi["fieldDims"] 

683 

684 for component in PhiComponents: 

685 tifffile.imwrite("{}/{}-{}.tif".format(outDir, prefix, component[0]), displacements[:, component[1]].reshape(dims).astype("<f4")) 

686 

687 if returnRS: 

688 tifffile.imwrite("{}/{}-RS.tif".format(outDir, prefix), fi["returnStatus"].reshape(dims).astype("<f4")) 

689 

690 

691def TSVtoVTK(fileName, fieldBinRatio=1.0, pixelSize=1.0, returnRS=False, outDir=None, prefix=None): 

692 """ 

693 This function converts a TSV file (typically the output of the ldic and ddic scripts) 

694 to a VTK file for visualising the deformation field. 

695 

696 Parameters 

697 ---------- 

698 fileName : string 

699 Name of the file 

700 

701 fieldBinRatio : int, optional 

702 if the input field is refer to a binned version of the image 

703 `e.g.`, if ``fieldBinRatio = 2`` the field values have been calculated 

704 for an image half the size of what the returned PhiField is referring to 

705 Default = 1.0 

706 

707 pixelSize: float 

708 physical size of a pixel (i.e. 1mm/px) 

709 Default = 1.0 

710 

711 returnRS : bool, optional 

712 if True: will return the SubPixelReturnStatus of the correlation 

713 Default = False 

714 

715 outDir : string 

716 Output directory 

717 Default is directory of the input field file 

718 

719 prefix : string 

720 Prefix for output files 

721 Default is the basename of the input field file (without extension) 

722 """ 

723 import spam.helpers 

724 

725 # use the helper function to read the TSV file 

726 fi = readCorrelationTSV(fileName, fieldBinRatio=fieldBinRatio) 

727 PhiField = fi["PhiField"] 

728 

729 # set output directory if none 

730 if outDir is None: 

731 if os.path.dirname(fileName) == "": 

732 outDir = "./" 

733 else: 

734 outDir = os.path.dirname(fileName) 

735 else: 

736 os.makedirs(outDir) 

737 

738 # output file name prefix 

739 if prefix is None: 

740 prefix = os.path.splitext(os.path.basename(fileName))[0] 

741 

742 # check if it is a ddic result 

743 if fi["numberOfLabels"] != 0: 

744 coords = fi["fieldCoords"][1:] * pixelSize 

745 if not returnRS: 

746 pointData = {"displacements": PhiField[1:, :-1, 3] * pixelSize} 

747 

748 else: 

749 pointData = {"displacements": PhiField[1:, :-1, 3] * pixelSize, "returnStatus": fi["returnStatus"][1:]} 

750 

751 spam.helpers.writeGlyphsVTK(coords, pointData, fileName="{}/{}.vtk".format(outDir, prefix)) 

752 

753 # if not, is a ldic result 

754 else: 

755 dims = fi["fieldDims"] 

756 coords = fi["fieldCoords"] * pixelSize 

757 aspectRatio = numpy.array([numpy.unique(coords[:, i])[1] - numpy.unique(coords[:, i])[0] if len(numpy.unique(coords[:, i])) > 1 else numpy.unique(coords[:, i])[0] for i in range(3)]) 

758 origin = coords[0] - aspectRatio / 2.0 

759 

760 if not returnRS: 

761 cellData = {"displacements": (PhiField[:, :-1, 3] * pixelSize).reshape((dims[0], dims[1], dims[2], 3))} 

762 

763 else: 

764 cellData = {"displacements": (PhiField[:, :-1, 3] * pixelSize).reshape((dims[0], dims[1], dims[2], 3)), "returnStatus": fi["returnStatus"].reshape(dims[0], dims[1], dims[2])} 

765 

766 spam.helpers.writeStructuredVTK(aspectRatio=aspectRatio, origin=origin, cellData=cellData, fileName="{}/{}.vtk".format(outDir, prefix))