Commit 03a4b6be authored by Edward Andò's avatar Edward Andò
Browse files

Resolves #206, default RS trusted in regularStrains is now 2. Filter F option...

Resolves #206, default RS trusted in regularStrains is now 2. Filter F option in filterPhiField now properly 'no'
parent 713f37e1
Pipeline #67446 passed with stages
in 27 minutes and 23 seconds
...@@ -215,7 +215,7 @@ if args.CINT: ...@@ -215,7 +215,7 @@ if args.CINT:
print("\tYou asked to correct with local QC fitting with -clqf, but only one correciton mode is supported") print("\tYou asked to correct with local QC fitting with -clqf, but only one correciton mode is supported")
elif args.CLQF: elif args.CLQF:
if args.FILTER_F is not 'disp': if args.FILTER_F != 'no':
print("WARNING: non-displacement quadratic coherency correction not implemented, only doing displacements, and returning F=eye(3)\n") print("WARNING: non-displacement quadratic coherency correction not implemented, only doing displacements, and returning F=eye(3)\n")
print("\n\nCorrection based on local quadratic coherency") print("\n\nCorrection based on local quadratic coherency")
...@@ -271,7 +271,7 @@ if args.FILTER_MEDIAN: ...@@ -271,7 +271,7 @@ if args.FILTER_MEDIAN:
print("\t9/9") print("\t9/9")
outputPhiField[:, 2, 2] = scipy.ndimage.generic_filter(inputPhiField[:, 2, 2].reshape(inputNodesDim), numpy.nanmedian, size=(2 * filterPointsRadius + 1)).ravel() outputPhiField[:, 2, 2] = scipy.ndimage.generic_filter(inputPhiField[:, 2, 2].reshape(inputNodesDim), numpy.nanmedian, size=(2 * filterPointsRadius + 1)).ravel()
if args.FILTER_F == 'disp': if args.FILTER_F == 'no':
for n in range(inputNodePositions.shape[0]): for n in range(inputNodePositions.shape[0]):
outputPhiField[n] = numpy.eye(4) outputPhiField[n] = numpy.eye(4)
......
...@@ -24,8 +24,9 @@ os.environ['OMP_NUM_THREADS'] = '1' ...@@ -24,8 +24,9 @@ os.environ['OMP_NUM_THREADS'] = '1'
import argparse import argparse
import tifffile import tifffile
import numpy
import multiprocessing import multiprocessing
import numpy
numpy.seterr(all='ignore')
import spam.helpers import spam.helpers
import spam.DIC import spam.DIC
...@@ -76,21 +77,29 @@ dispFlat = f["PhiField"][:,:3,-1] ...@@ -76,21 +77,29 @@ dispFlat = f["PhiField"][:,:3,-1]
# Check if a mask of points (based on return status of the correlation) is asked # Check if a mask of points (based on return status of the correlation) is asked
if args.MASK: if args.MASK:
mask = f["returnStatus"] < -4 mask = f["returnStatus"] < args.RETURN_STATUS_THRESHOLD
print(f"\nspam-regularStrain: excluding points based on return threshold < {args.RETURN_STATUS_THRESHOLD} (excluded {100*(1-numpy.mean(mask)):2.1f}%)")
dispFlat[mask] = numpy.nan dispFlat[mask] = numpy.nan
disp = dispFlat.reshape(dims[0], dims[1], dims[2], 3) disp = dispFlat.reshape(dims[0], dims[1], dims[2], 3)
print("\nspam-regularStrain: Computing F=I+du/dx") print("\nspam-regularStrain: Computing F=I+du/dx")
if not args.Q8: if args.Q8:
Ffield = spam.deformation.FfieldRegularGeers(disp, Ffield = spam.deformation.FfieldRegularQ8( disp,
nodeSpacing=nodeSpacing, nodeSpacing=nodeSpacing,
neighbourRadius=args.STRAIN_NEIGHBOUR_RADIUS,
nProcesses=args.PROCESSES, nProcesses=args.PROCESSES,
verbose=True) verbose=True)
elif args.RAW:
# Just take it straight form the file
Ffield = f["PhiField"][:,:3,:3]
if args.MASK:
Ffield[mask] = numpy.nan
Ffield = Ffield.reshape(dims[0], dims[1], dims[2], 3, 3)
else: else:
Ffield = spam.deformation.FfieldRegularQ8( disp, Ffield = spam.deformation.FfieldRegularGeers(disp,
nodeSpacing=nodeSpacing, nodeSpacing=nodeSpacing,
neighbourRadius=args.STRAIN_NEIGHBOUR_RADIUS,
nProcesses=args.PROCESSES, nProcesses=args.PROCESSES,
verbose=True) verbose=True)
...@@ -104,30 +113,28 @@ decomposedFfield = spam.deformation.decomposeFfield(Ffield, ...@@ -104,30 +113,28 @@ decomposedFfield = spam.deformation.decomposeFfield(Ffield,
# Define base fileName # Define base fileName
if not args.Q8: if args.Q8:
fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Geers"
mode = "Geers"
else:
if twoD: if twoD:
fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Q4" fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Q4"
else: else:
fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Q8" fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Q8"
mode = "Q8" mode = "Q8"
elif args.RAW:
fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-raw"
mode = "raw"
else:
fileNameBase = args.OUT_DIR+"/"+args.PREFIX+"-strain-Geers"
mode = "Geers"
# Save strain fields # Save strain fields
print("\nspam-regularStrain: Saving strain fields...") print("\nspam-regularStrain: Saving strain fields...")
if args.TSV: if args.TSV:
# Positions for Geers are the measurement points
if not args.Q8:
if twoD:
outputPositions = fieldCoords.copy().reshape(1, dims[1], dims[2], 3)
else:
outputPositions = fieldCoords.copy().reshape(dims[0], dims[1], dims[2], 3)
# Positions for the centres of the Q8 elements are between the measurement points # Positions for the centres of the Q8 elements are between the measurement points
# (so there is one number fewer compared to measurement points # (so there is one number fewer compared to measurement points
# so we strip off last node points -- not Z ones in twoD for Q8 mode # so we strip off last node points -- not Z ones in twoD for Q8 mode
else: if args.Q8:
if twoD: if twoD:
outputPositions = fieldCoords.copy().reshape(1, dims[1], dims[2], 3)[:, 0:-1, 0:-1, :] outputPositions = fieldCoords.copy().reshape(1, dims[1], dims[2], 3)[:, 0:-1, 0:-1, :]
else: else:
...@@ -136,6 +143,12 @@ if args.TSV: ...@@ -136,6 +143,12 @@ if args.TSV:
outputPositions[:, :, :, 0] += nodeSpacing[0] / 2.0 outputPositions[:, :, :, 0] += nodeSpacing[0] / 2.0
outputPositions[:, :, :, 1] += nodeSpacing[1] / 2.0 outputPositions[:, :, :, 1] += nodeSpacing[1] / 2.0
outputPositions[:, :, :, 2] += nodeSpacing[2] / 2.0 outputPositions[:, :, :, 2] += nodeSpacing[2] / 2.0
else:
# Positions for Geers and "raw" are the measurement points
if twoD:
outputPositions = fieldCoords.copy().reshape(1, dims[1], dims[2], 3)
else:
outputPositions = fieldCoords.copy().reshape(dims[0], dims[1], dims[2], 3)
# Here we want to pass an Nx3 matrix of poitions: # Here we want to pass an Nx3 matrix of poitions:
spam.helpers.writeStrainTSV(fileNameBase+".tsv", outputPositions.reshape(-1,3), decomposedFfield, firstColumn="StrainPointNumber") spam.helpers.writeStrainTSV(fileNameBase+".tsv", outputPositions.reshape(-1,3), decomposedFfield, firstColumn="StrainPointNumber")
......
...@@ -560,6 +560,28 @@ class testAll(unittest.TestCase): ...@@ -560,6 +560,28 @@ class testAll(unittest.TestCase):
#for component in ["U", "e", "vol", "volss", "dev", "devss"]: #for component in ["U", "e", "vol", "volss", "dev", "devss"]:
#self.assertTrue(numpy.nanmean(strain6b[component]), numpy.nanmean(VTK6b[component]), places=2) #self.assertTrue(numpy.nanmean(strain6b[component]), numpy.nanmean(VTK6b[component]), places=2)
### Step 6b Now let's calculate small and large strains with Q8 elements and TSV output
exitCode = subprocess.call(["spam-regularStrain", "-raw",
testFolder + "snow-grid-5a-ldic.tsv",
"-comp", "U", "e", "vol", "volss", "dev", "devss",
"-vtk",
"-od", testFolder, "-pre", "snow-grid-6b"],
stdout=FNULL, stderr=FNULL)
self.assertEqual(exitCode, 0)
strain6b = spam.helpers.readStrainTSV(testFolder + "snow-grid-6b-strain-raw.tsv")
dims6b = [strain6b['fieldDims'][0], strain6b['fieldDims'][1], strain6b['fieldDims'][2], 3, 3]
# Check small strains, should be ~0
self.assertTrue(numpy.allclose(numpy.nanmean(strain6b['e'].reshape(dims6b)[2:-2, 2:-2, 2:-2], axis=(0,1,2)), numpy.zeros((3,3)), atol=0.02))
Umean6b = numpy.nanmean(strain6b['U'].reshape(dims6b)[2:-2, 2:-2, 2:-2], axis=(0, 1, 2))
self.assertTrue(numpy.allclose(Umean6b,numpy.eye(3), atol=0.02))
# dev and vol both ~0
self.assertTrue(numpy.isclose(numpy.nanmean(strain6b['vol'].reshape( strain6b['fieldDims'])[2:-2, 2:-2, 2:-2]), 0, atol=0.02))
self.assertTrue(numpy.isclose(numpy.nanmean(strain6b['volss'].reshape(strain6b['fieldDims'])[2:-2, 2:-2, 2:-2]), 0, atol=0.02))
self.assertTrue(numpy.isclose(numpy.nanmean(strain6b['dev'].reshape( strain6b['fieldDims'])[2:-2, 2:-2, 2:-2]), 0, atol=0.02))
self.assertTrue(numpy.isclose(numpy.nanmean(strain6b['devss'].reshape(strain6b['fieldDims'])[2:-2, 2:-2, 2:-2]), 0, atol=0.02))
def test_ITKwatershed(self): def test_ITKwatershed(self):
# Run on snow data # Run on snow data
# load 3D image # load 3D image
......
...@@ -1556,7 +1556,14 @@ def regularStrainParser(parser): ...@@ -1556,7 +1556,14 @@ def regularStrainParser(parser):
'--nomask', '--nomask',
action="store_false", action="store_false",
dest='MASK', dest='MASK',
help="Don't mask correlation points in background according to return status (i.e., include RS=-5 or less)") help="Don't mask correlation points according to return status (use everything)")
parser.add_argument('-rst',
'--return-status-threshold',
type=int,
default=2,
dest='RETURN_STATUS_THRESHOLD',
help='Lowest return status value to preserve in input PhiField. Default = 2')
parser.add_argument('-r', parser.add_argument('-r',
'--neighbourhood-radius-for-strain-calculation', '--neighbourhood-radius-for-strain-calculation',
...@@ -1566,12 +1573,19 @@ def regularStrainParser(parser): ...@@ -1566,12 +1573,19 @@ def regularStrainParser(parser):
help="Radius (in units of nodeSpacing) inside which to select neighbours for displacement gradient calculation. Ignored if -cub is set. Default = 1.5") help="Radius (in units of nodeSpacing) inside which to select neighbours for displacement gradient calculation. Ignored if -cub is set. Default = 1.5")
parser.add_argument('-cub', parser.add_argument('-cub',
'-Q8',
'--cubic-element', '--cubic-element',
'--Q8', '--Q8',
action="store_true", action="store_true",
dest='Q8', dest='Q8',
help='Use Q8 element interpolation? More noisy and strain values not centred on displacement points') help='Use Q8 element interpolation? More noisy and strain values not centred on displacement points')
parser.add_argument('-raw',
'--no-shape-function',
action="store_true",
dest='RAW',
help='Just use F straight from the correlation windows instead of computing it from the displacement field.')
parser.add_argument('-od', parser.add_argument('-od',
'--out-dir', '--out-dir',
type=str, type=str,
...@@ -1579,6 +1593,8 @@ def regularStrainParser(parser): ...@@ -1579,6 +1593,8 @@ def regularStrainParser(parser):
dest='OUT_DIR', dest='OUT_DIR',
help='Output directory, default is the dirname of input file') help='Output directory, default is the dirname of input file')
parser.add_argument('-pre', parser.add_argument('-pre',
'--prefix', '--prefix',
type=str, type=str,
...@@ -1632,6 +1648,10 @@ def regularStrainParser(parser): ...@@ -1632,6 +1648,10 @@ def regularStrainParser(parser):
if args.PREFIX is None: if args.PREFIX is None:
args.PREFIX = os.path.splitext(os.path.basename(args.inFile.name))[0] args.PREFIX = os.path.splitext(os.path.basename(args.inFile.name))[0]
if args.RAW and args.Q8:
print("You can't ask for both F-from-correlation and F-from-Q8")
exit()
# Make sure at least one output format has been asked for # Make sure at least one output format has been asked for
if args.VTK + args.TIFF + args.TSV == 0: if args.VTK + args.TIFF + args.TSV == 0:
print("#############################################################") print("#############################################################")
...@@ -2832,6 +2852,10 @@ def filterPhiField(parser): ...@@ -2832,6 +2852,10 @@ def filterPhiField(parser):
print("WARINING: you can't ask for an overall median filter and a correction") print("WARINING: you can't ask for an overall median filter and a correction")
exit() exit()
if args.FILTER_F not in ['all', 'rigid', 'no']:
print("-F option must be either 'all', 'rigid' or 'no'")
exit()
# Output file name prefix # Output file name prefix
if args.PREFIX is None: if args.PREFIX is None:
args.PREFIX = os.path.splitext(os.path.basename(args.PHIFILE.name))[0] + "-filtered" args.PREFIX = os.path.splitext(os.path.basename(args.PHIFILE.name))[0] + "-filtered"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment