Commit 6294cbd5 authored by Edward Andò's avatar Edward Andò
Browse files

multiprocessing parallelisation of spam-pixelSearch

parent 24bc5f77
Pipeline #56069 failed with stages
in 11 minutes and 47 seconds
......@@ -30,8 +30,7 @@ import spam.label
import spam.DIC
import spam.deformation
import queue as queue
import multiprocessing
import progressbar
import tifffile
import os
......@@ -336,6 +335,81 @@ def getImagettes(nodePosition, Phi, searchRange, boundingBox, im1, im2, im1mask,
'pixelSearchOffset': searchRange[0::2]
}
def pixelSearchOneNode(nodeNumber):
"""
Function to be called by multiprocessing parallelisation for pixel search in one position.
This function will call getImagettes, or the equivalent for labels and perform the pixel search
Parameters
----------
nodeNumber : int
node number to work on
Returns
-------
List with:
- nodeNumber (needed to write result in right place)
- displacement vector
- NCC value
- error value
- return Status
"""
# All global variables, we will return a list with:
# nodeNumber,
if args.LAB1 is not None:
imagetteReturns = {}
gottenLabel = spam.label.getLabel(lab1, nodeNumber, boundingBoxes=boundingBoxes, centresOfMass=nodePositions, maskOtherLabels=True, labelDilate=args.LABEL_DILATE, margin=1+args.LABEL_DILATE)
if gottenLabel is not None:
# 2020-07-05 try applying F to im1 this is expected to help with pixel searching
PhiNoDisp = PhiField[nodeNumber].copy()
PhiNoDisp[0:3,-1] = 0.0
initialDisplacement = numpy.round(PhiField[nodeNumber, 0:3, 3]).astype(int)
imagette1 = spam.helpers.slicePadded(im1, gottenLabel['boundingBox'] + numpy.array([0,1,0,1,0,1]))
imagette1def = spam.DIC.applyPhi(imagette1, PhiNoDisp, PhiPoint=gottenLabel['centreOfMassREL'])
maskette1def = spam.DIC.applyPhi(gottenLabel['subvol'], PhiNoDisp, PhiPoint=gottenLabel['centreOfMassREL'], interpolationOrder=0)
imagette1def[maskette1def == 0] = numpy.nan
imagetteReturns['imagette1'] = imagette1def
imagetteReturns['imagette1mask'] = gottenLabel['subvol']
# 2020-09-25 OS and EA: Prepare startStop array for imagette 2 to be extracted with new slicePadded
startStopIm2 = [int(gottenLabel['boundingBox'][0] - max(args.LABEL_DILATE, 0) + initialDisplacement[0] + searchRange[0] ),
int(gottenLabel['boundingBox'][1] + max(args.LABEL_DILATE, 0) + initialDisplacement[0] + searchRange[1] + 1),
int(gottenLabel['boundingBox'][2] - max(args.LABEL_DILATE, 0) + initialDisplacement[1] + searchRange[2] ),
int(gottenLabel['boundingBox'][3] + max(args.LABEL_DILATE, 0) + initialDisplacement[1] + searchRange[3] + 1),
int(gottenLabel['boundingBox'][4] - max(args.LABEL_DILATE, 0) + initialDisplacement[2] + searchRange[4] ),
int(gottenLabel['boundingBox'][5] + max(args.LABEL_DILATE, 0) + initialDisplacement[2] + searchRange[5] + 1)]
imagetteReturns['imagette2'] = spam.helpers.slicePadded(im2, startStopIm2)
imagetteReturns['pixelSearchOffset'] = searchRange[0::2] - numpy.array([max(args.LABEL_DILATE, 0)]*3)
imagetteReturns['returnStatus'] = 1
else:
imagetteReturns['returnStatus'] = 0
else:
imagetteReturns = getImagettes(nodePositions[nodeNumber], PhiField[nodeNumber].copy(), searchRange.copy(), boundingBoxes[nodeNumber], im1, im2, im1mask, args.MASK_COVERAGE, greyThreshold)
# If getImagettes was successful (size check and mask coverage check)
if imagetteReturns['returnStatus'] == 1:
PSreturns = spam.DIC.correlate.pixelSearch(imagetteReturns['imagette1'],
imagetteReturns['imagette2'],
imagette1mask = imagetteReturns['imagette1mask'],
returnError = True)
pixelSearchOffset = imagetteReturns['pixelSearchOffset']
writeReturns = True
#
return nodeNumber, PSreturns[0] + pixelSearchOffset, PSreturns[1], PSreturns[2], imagetteReturns['returnStatus']
# Failed to extract imagettes or something
else:
return nodeNumber, numpy.array([numpy.nan]*3), 0.0, numpy.inf, imagetteReturns['returnStatus']
# Create pixelSearchCC vector
pixelSearchCC = numpy.zeros((numberOfNodes), dtype=float)
......@@ -345,104 +419,36 @@ returnStatus = numpy.ones((numberOfNodes), dtype=int)
deltaPhiNorm = numpy.ones((numberOfNodes), dtype=int)
iterations = numpy.ones((numberOfNodes), dtype=int)
# Add nodes to a queue -- mostly useful for MPI
q = queue.Queue()
if args.LAB1 is not None: firstNode = 1; finishedNodes = 1
else: firstNode = 0; finishedNodes = 0
for node in range(firstNode, numberOfNodes):
q.put(node)
if args.PROCESSES is None: args.PROCESSES = multiprocessing.cpu_count()
writeReturns = False
print("\n\tStarting Pixel search (with {} process{})".format(args.PROCESSES, 'es' if args.PROCESSES > 1 else ''))
print("\n\tStarting Pixel search")
widgets = [progressbar.FormatLabel(''), ' ', progressbar.Bar(), ' ', progressbar.AdaptiveETA()]
pbar = progressbar.ProgressBar(widgets=widgets, maxval=numberOfNodes)
pbar.start()
while finishedNodes != numberOfNodes:
# If there are workers not working, satify their requests...
if not q.empty():
# Get the next node off the queue
nodeNumber = q.get()
if args.LAB1 is not None:
imagetteReturns = {}
gottenLabel = spam.label.getLabel(lab1, nodeNumber, boundingBoxes=boundingBoxes, centresOfMass=nodePositions, maskOtherLabels=True, labelDilate=args.LABEL_DILATE, margin=1+args.LABEL_DILATE)
if gottenLabel is not None:
# 2020-07-05 try applying F to im1 this is expected to help with pixel searching
PhiNoDisp = PhiField[nodeNumber].copy()
PhiNoDisp[0:3,-1] = 0.0
initialDisplacement = numpy.round(PhiField[nodeNumber, 0:3, 3]).astype(int)
imagette1 = spam.helpers.slicePadded(im1, gottenLabel['boundingBox'] + numpy.array([0,1,0,1,0,1]))
imagette1def = spam.DIC.applyPhi(imagette1, PhiNoDisp, PhiPoint=gottenLabel['centreOfMassREL'])
maskette1def = spam.DIC.applyPhi(gottenLabel['subvol'], PhiNoDisp, PhiPoint=gottenLabel['centreOfMassREL'], interpolationOrder=0)
imagette1def[maskette1def == 0] = numpy.nan
imagetteReturns['imagette1'] = imagette1def
imagetteReturns['imagette1mask'] = gottenLabel['subvol']
# 2020-09-25 OS and EA: Prepare startStop array for imagette 2 to be extracted with new slicePadded
startStopIm2 = [int(gottenLabel['boundingBox'][0] - max(args.LABEL_DILATE, 0) + initialDisplacement[0] + searchRange[0] ),
int(gottenLabel['boundingBox'][1] + max(args.LABEL_DILATE, 0) + initialDisplacement[0] + searchRange[1] + 1),
int(gottenLabel['boundingBox'][2] - max(args.LABEL_DILATE, 0) + initialDisplacement[1] + searchRange[2] ),
int(gottenLabel['boundingBox'][3] + max(args.LABEL_DILATE, 0) + initialDisplacement[1] + searchRange[3] + 1),
int(gottenLabel['boundingBox'][4] - max(args.LABEL_DILATE, 0) + initialDisplacement[2] + searchRange[4] ),
int(gottenLabel['boundingBox'][5] + max(args.LABEL_DILATE, 0) + initialDisplacement[2] + searchRange[5] + 1)]
imagetteReturns['imagette2'] = spam.helpers.slicePadded(im2, startStopIm2)
imagetteReturns['pixelSearchOffset'] = searchRange[0::2] - numpy.array([max(args.LABEL_DILATE, 0)]*3)
imagetteReturns['returnStatus'] = 1
#print(imagetteReturns['imagette1'].shape)
#print(imagetteReturns['imagette1mask'].shape)
#print(imagetteReturns['imagette2'].shape)
#print(gottenLabel['boundingBox'])
#print(startStopIm2)
else:
imagetteReturns['returnStatus'] = 0
finishedNodes = 0
else:
imagetteReturns = getImagettes(nodePositions[nodeNumber], PhiField[nodeNumber].copy(), searchRange.copy(), boundingBoxes[nodeNumber], im1, im2, im1mask, args.MASK_COVERAGE, greyThreshold)
# If getImagettes was successful (size check and mask coverage check)
if imagetteReturns['returnStatus'] == 1:
returns = spam.DIC.correlate.pixelSearch(imagetteReturns['imagette1'],
imagetteReturns['imagette2'],
imagette1mask = imagetteReturns['imagette1mask'],
returnError = True)
pixelSearchOffset = imagetteReturns['pixelSearchOffset']
writeReturns = True
# Failed to extract imagettes or something
else:
pixelSearchCC[nodeNumber] = 0.0
finishedNodes += 1
PhiField[nodeNumber, 0:3, 0:3] = numpy.eye(3)
PhiField[nodeNumber, 0:3, 3] = numpy.nan
returnStatus[nodeNumber] = imagetteReturns['returnStatus']
# Otherwise spend time looking waiting for replies from workers
#elif mpi:
#message = mpiComm.recv(source=mpi4py.MPI.ANY_SOURCE, tag=2, status=mpiStatus)
# If we have new DVC returns, save them in our output matrices
if writeReturns:
with multiprocessing.Pool(processes=args.PROCESSES) as pool:
for returns in pool.imap_unordered(pixelSearchOneNode, range(firstNode, numberOfNodes)):
finishedNodes += 1
writeReturns = False
# set translation for this node
PhiField[nodeNumber, 0:3, 3] += numpy.array(returns[0]) + pixelSearchOffset
pixelSearchCC[nodeNumber] = returns[1]
error[nodeNumber] = returns[2]
widgets[0] = progressbar.FormatLabel(" CC={:0>7.5f} ".format(pixelSearchCC[nodeNumber]))
pbar.update(finishedNodes)
# Update progres bar if point is not skipped
if returns[4] > 0:
widgets[0] = progressbar.FormatLabel(" CC={:0>7.5f} ".format(returns[2]))
pbar.update(finishedNodes)
PhiField[returns[0], 0:3, 3] += returns[1]
# Create pixelSearchCC vector
pixelSearchCC[returns[0]] = returns[2]
error[returns[0]] = returns[3]
returnStatus[returns[0]] = returns[4]
pbar.finish()
print("\n")
......
......@@ -154,7 +154,7 @@ class testAll(unittest.TestCase):
# And the z-displacement is low
self.assertTrue(numpy.isclose(0, PSresult['PhiField'][PSresult['returnStatus']==1,0,-1].mean(), atol=1.0))
exit()
# Check output results with TSV output, bin1 registration + TIFFs
# Decreasing node spacing in order to have more points for Geers strain calculation
# exitCode = subprocess.call(["spam-ldic", "-Ffile", testFolder+"snow-ref-snow-def-bin2-registration.tsv", "-Ffb", "2", "-hws", "15", testFolder+"snow-ref.tif", testFolder+"snow-def.tif", "-od", testFolder+"", "-tsv"])
......
......@@ -2095,6 +2095,13 @@ def pixelSearch(parser):
type=argparse.FileType('r'),
help="Path to tiff file containing a labelled image 1 that defines zones to correlate. Disactivates -hws and -ns options")
parser.add_argument('-np',
'--number-of-processes',
default=None,
type=int,
dest='PROCESSES',
help="Number of parallel processes to use. Default = multiprocessing.cpu_count()")
parser.add_argument('-ld',
'--label-dilate',
type=int,
......
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