Skip to content
Snippets Groups Projects

Much faster atomic column integration implementation

Merged Thomas Aarholt requested to merge thomasaarholt/atomap:INT into master

In similar fashion to !47 (merged), I have sped up the atomic column integration implementation. Particularly so for the Voronoi cell integration method.

For a 512px by 512px, the old code took 124 seconds. The new version takes 3.6 seconds. The new version makes it reasonable to perform Voronoi integration on 1024px (45 sec) and 2048px images (half-an-hour ish) as well, where the old version took a LOT longer.

I have achieved the speed-boost partly by rewriting some code and moving things that did not need to be inside loops, outside loops. But the main speed-increase has come from implementing the numba jit (just-in-time) compilation decorator around the function that calculates the shortest distance between points for the point-record image.

In addition, I have added a function that removes any cells that are near the edges of the image/dataset. These are often annoying in that they get mess up the contrast of the displayed integrated_record image, and they are complicated to manually remove in order to calculate correct statistics (think, mean of the atomic column intensity).

Finally, I have updated a few things on watershed, including switching to the new location for watershed in the skimage toolkit (previous was being deprecated).

Attached is an example file and example result, showing the raw data, Voronoi integration and integration with removed border cells.

%matplotlib widget
import atomap.api as am
import hyperspy.api as hs
import matplotlib.pyplot as plt

s = hs.load("20181005_2006_STEM_HAADF_2.7_Mx.tif")
s2 = s.isig[:1024,:1024] # change to 256, 256 for speed
#nav = hs.signals.Signal2D([[1,2,3], [4,5,6]]).T
S = s2#nav*s2.T
points_x, points_y = am.get_atom_positions(s2).T

i, ir, pr = am.integrate(S, points_x, points_y, method='Voronoi', remove_edge_cells=True, edge_pixels=5)

# Or alternatively, with `remove_edge_cells=False` in previous line
# i, ir, pr = remove_integrated_edge_cells(i, ir, pr, pixels=5)
fig = plt.figure(dpi=200)
hs.plot.plot_images([s2, ir, pr], fig=fig, colorbar=False)

Comparison

20181005_2006_STEM_HAADF_2.7_Mx.tif

Edited by Thomas Aarholt

Merge request reports

Loading
Loading

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
1124 1187 return (integrated_intensity, s_intensity_record, point_record.T)
1125 1188
1126 1189
1190 def _border_elems(image, pixels=1):
1191 """
1192 Return the values of the edges along the border of the image, with
1193 border width `pixels`.
1194 """
1195 arr = np.ones_like(image, dtype=bool)
1196 arr[pixels:-1-(pixels-1), pixels:-1-(pixels-1)] = False
1197 return image[arr]
1198
1199
1200 def remove_integrated_edge_cells(i_points, i_record, p_record,
  • 994 def find_smallest_distance(i, j, points):
    995 '''
    996 Finds the smallest distance between coordinates (i, j)
    997 and a list of coordinates.
    998
    999 Parameters
    1000 ----------
    1001 i : Integer
    1002 j : Integer
    1003 points : array like of shape (2,N)
    1004
    1005 Returns
    1006 -------
    1007 distMin : Minimum distance
    1008 minIndex : Index of minimum distance in points
    1009 '''
  • 9 9 import hyperspy.api as hs
    10 10 from hyperspy.signals import Signal1D, Signal2D
    11 11 from skimage.morphology import watershed
    12
    12 import numba as nb
  • 990 990 return self
    991 991
    992 992
    993 @nb.jit(nopython=True)
  • 1217 The output of the atomap integrate function or method
    1218
    1219 Returns
    1220 -------
    1221 i_points : NumPy array
    1222 Modified list of integrated intensities with either np.nan or 0
    1223 on the removed values, which preserves the atom index.
    1224 i_record : HyperSpy signal
    1225 Modified integrated intensity record, with either np.nan or 0
    1226 on the removed values, which preserves the atom index
    1227 p_record : NumPy array, same size as image
    1228 Modified points record, where removed areas have value = -1.
    1229
    1230 Example
    1231 -------
    1232 points_x, points_y = am.get_atom_positions(s).T
  • 1029 -------
    1030 point_record : Voronoi array where equal values belong to
    1031 the same Voronoi cell
    1032 '''
    1033 for i, j in tqdm(np.ndindex(point_record.shape),
    1034 desc="Calculating Voronoi",
    1035 total=np.prod(point_record.shape)):
    1036 minIndex, distMin = find_smallest_distance(i, j, points)
    1037 if distMin >= max_radius:
    1038 point_record[i][j] = 0
    1039 else:
    1040 point_record[i][j] = minIndex + 1
    1041 return point_record
    1042
    1043
    1044 def get_integrated_intensity(point_record, image, point_index):
  • Very nice! I added some comments in the code, but I'm not certain what you mean with "live dataset" for the tests.

  • By live dataset, I basically mean that I'm unsure which approach I should use to create a dataset with positions OR sublattice to use to run the integration tests on.

  • Thomas Aarholt added 1 commit

    added 1 commit

    Compare with previous version

  • Thomas Aarholt added 1 commit

    added 1 commit

    • 6ddb9ed6 - fixed for all dimensions - changed convention to take Signal2Ds only

    Compare with previous version

  • Thomas Aarholt added 37 commits

    added 37 commits

    • 6ddb9ed6...518fa616 - 16 commits from branch atomap:master
    • 7558decc - fix bug when input is 4D!
    • ab48fc7c - implement faster voronoi
    • 8a89d3e4 - removed unnecessary line
    • bb56be22 - faster doing one argmin and indexing than argmin and min
    • a02e805b - much much faster with numba jit
    • c0c51d77 - jittable code for 2D arrays with two tests
    • c270b93e - added docstrings and removed timings
    • 06186897 - Added progress bar descriptions and made values that are masked away = np.nan
    • 0841006f - cleaning up
    • 7b102de3 - faster masking by getting rid of loop
    • f3154eff - cast watershed points to int, but by rounding first
    • dc2b8568 - Added convenience function for removing cells near the edge of the image
    • 7ae5acb4 - clarified docstring
    • 0f99840f - flake8
    • 642790aa - Add test
    • 574278e4 - removed commented-out jit
    • 6aa62e93 - fix test
    • f0fc7ed6 - fixed rotated int
    • 2b6acbdb - fixed for all dimensions - changed convention to take Signal2Ds only
    • c5a6a35b - Merge branch 'INT' of gitlab.com:thomasaarholt/atomap into INT
    • a9f1d186 - fix multidimensional edge removal

    Compare with previous version

  • Thomas Aarholt added 1 commit

    added 1 commit

    Compare with previous version

  • For generating test data I'd recommend the MakeTestData class: https://gitlab.com/atomap/atomap/blob/master/atomap/testing_tools.py#L10

  • Thomas Aarholt added 4 commits

    added 4 commits

    • 340766d9 - fix reversing of x and y
    • 4ee155a3 - remove progressbars
    • c1589a52 - tqdm auto
    • a2faa2c6 - Merge branch 'INT' of gitlab.com:thomasaarholt/atomap into INT

    Compare with previous version

  • Thomas Aarholt added 160 commits

    added 160 commits

    Compare with previous version

  • Thomas Aarholt changed the description

    changed the description

  • Thomas Aarholt added 1 commit

    added 1 commit

    • 865bcd02 - added docs and image-generation for docs

    Compare with previous version

  • Okay @magnunor! One year later, I've added your suggested changes, rebased the whole thing (force push), and added documentation and fixed tests. I added image generation for the docs in a python function in the same way you've done - I'm gonna assume that they run automatically if they're in the doc/images folder.

    All tests pass locally here.

  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Loading
  • Please register or sign in to reply
    Loading