diff --git a/py4DSTEM/process/digitaldarkfield/DDF.py b/py4DSTEM/process/digitaldarkfield/DDF.py index ee5efaad9..2d0e8d100 100644 --- a/py4DSTEM/process/digitaldarkfield/DDF.py +++ b/py4DSTEM/process/digitaldarkfield/DDF.py @@ -23,28 +23,43 @@ def aperture_array_generator( n2lims=(-5, 5), returns="both", ): - # shape is a tuple describing the shape of the diffraction patterns - # center is a tuple of the centre position (vertical, horizontal) - # pad is - # mode tells what kind of calculation is desired. Which parameters are required depends on this choice: - #'single': just one aperture at a specified position: - # g1 and g2, lattice vectors (non-colinear) - # s1 and s2, multiples of these used to find the required lattice position - # i.e. aperture placed at s1*g1 + s2*g2 - # r1, r2 unused - #'2-beam': just a line of apertures along spots for a 2-beam condition: - # g1: lattice vector - # n1lims: tuple of integers giving the largest multiples of this lattice vector to be used, - # negative and positive - # r1 and r2, inner and outerradii in pixels over which aperture points will be found (optional) - #'array': an array defined by g1 and g2 centred on s1*g1+s2*g2 - # r1 and r2, inner and outerradii in pixels over which aperture points will be found (optional) - # n1lims and n2lims: tuple of integers giving the largest multiples of each lattice vector to be used - # r1 set to a small but non-zero value (a few pixels) can be used to exclude the primary beam - # returns sets whether the function returns: - #'both' = a centered array and an array in raw pixel numbers (uncentered) - #'centered' = just the version centered on [0,0] - # in all cases, the return is a list of (Qx,Qy) tuples + """ + shape is a tuple describing the shape of the diffraction patterns + + center is a tuple of the centre position (vertical, horizontal) + + pad is any edge boundary desired (i.e. no aperture positions within pad pixels of edge) + + mode tells what kind of calculation is desired. Which parameters are required and exactly what they mean + depends on this choice: + + 'single': just one aperture at a specified position: + g1 and g2, lattice vectors (non-colinear) + s1 and s2, multiples of these used to find the required lattice position + i.e. aperture placed at s1*g1 + s2*g2 + r1, r2 unused + + '2-beam': just a line of apertures along spots for a 2-beam condition: + g1: lattice vector + g2: unused + n1lims: tuple of integers giving the largest multiples of this lattice vector to be used, + negative and positive + r1 and r2, inner and outerradii in pixels over which aperture points will be found (optional) + this is a good way to exclude the central spot by setting r > disc radius + + 'array': an array defined by g1 and g2 centred on s1*g1+s2*g2 + r1 and r2, inner and outerradii in pixels as for '2-beam' + n1lims and n2lims: tuple of integers giving the largest multiples of each lattice vector to be used, + as for 2-beam + + returns sets whether the function returns: + + 'both' = a centered array and an array in raw pixel numbers (uncentered) + + 'centered' = just the version centered on [0,0] + + in all cases, the return is a list of (Qx,Qy) tuples + """ V, H = shape[0], shape[1] @@ -104,13 +119,24 @@ def aperture_array_generator( else: print("incorrect mode selection") - def pointlist_to_array(bplist, idim, jdim): - # This function turns the py4dstem pointslist object to a simple array that is more - # convenient for rapid array processing in numpy + """ + This function turns the py4dstem pointslist object to a simple numpy array that is more + convenient for rapid array processing in numpy + + idim and jdim are the dimensions in the Rx and Ry directions + + returns an array called pointsarray + This will be an 2D numpy array of n points x 5 columns: + qx + qy + I + Rx + Ry + """ for i, j in tqdmnd(idim, jdim): if i == j == 0: - ps = np.array( + pointsarray = np.array( [ bplist.cal[i, j].qx, bplist.cal[i, j].qy, @@ -129,43 +155,46 @@ def pointlist_to_array(bplist, idim, jdim): bplist.cal[i, j].qx.shape[0] * [j], ] ).T - ps = np.vstack((ps, nps)) - return ps # as a numpy array - # ps will be an 2D numpy array of n points x 5 columns: - # qx - # qy - # I - # Rx - # Ry - - + pointsarray = np.vstack((pointsarray, nps)) + return pointsarray + def pointlist_differences(apertureposition, pointsarray): - # calculates differences between a specific aperture position - # and a whole list of detected points for a dataset - # returns the Euclidean distances as a 1D array + """ + calculates differences between a specific aperture position + and a whole list of detected points for a dataset (as an array) + + returns the Euclidean distances as a 1D numpy array + """ subtractor = np.array( [[apertureposition[0], apertureposition[1]] * pointsarray.shape[0]] ).reshape((pointsarray.shape[0], 2)) diff = ((pointsarray[:, :2] - subtractor) ** 2).sum(axis=1) ** 0.5 - return diff # as a numpy array - - -def DDFimage(dataset, points, aperturearray, tol=1): - # dataset is a 4DSTEM dataset as a numpy array, only used to get the size of image - # points is an array of points as calculated by pointlist_to_array above - # centerarray is a list of tuples of aperture centers, - # as defined by aperture_array_generator above - # tol is the tolerance for a displacement between points and centers (in pixels) - # this does rely on the pointslist_differences function + return diff + + +def DDFimage(dataset, pointsarray, aperturearray, tol=1): + """ + dataset is a 4DSTEM dataset as a numpy array, only used to get the size of image + + pointsarray is an array of points as calculated by pointlist_to_array + + aperturearray is a list of tuples of aperture centers generated by aperture_array_generator + + tol is the tolerance for a displacement between points and centers (in pixels) + + this does rely on the pointslist_differences function + + returns a the DDF image as a 2D numpy array + """ image = np.zeros_like(dataset[:, :, 0, 0]) for aperture_index in tqdmnd(len(aperturearray)): apertureposition = aperturearray[aperture_index] intensities = np.vstack( - (points[:, 2:].T, pointlist_differences(apertureposition, points)) + (pointsarray[:, 2:].T, pointlist_differences(apertureposition, pointsarray)) ).T intensities2 = np.delete(intensities, np.where(intensities[:, 3] > tol), axis=0) for row in range(intensities2[:, 0].shape[0]): image[ intensities2[row, 1].astype(int), intensities2[row, 2].astype(int) ] += intensities2[row, 0] - return image # as a 2D numpy array + return image