SPAM: Software for Practical Analysis of Materials

Olga Stamati & Emmanuel Roubin & Edward Andò

Laboratoire 3SR, Grenoble, France

What is SPAM?

    First of all a “Canned pork meat product” (wp)

What is SPAM?

    Then a Monty Python Sketch

    Dailymotion
  • ...which was used to describe junk emails...
  • Now a python package:
    Software for Practical Analysis of Materials

“Software for the Practical Analysis of Materials”

Core developers:

  • Olga Stamati
  • Edward Andò
  • Emmanuel Roubin
  • Rémi Cailletaud

It is Library of material science/mechanics tools:

  • Many functions for image correlation, mesh projection etc.
  • More complex command-line scripts are also provided (example coming up)
  • Nice online documentation

Today we'll talk about image Registration

Introduction to registration

  • We are trying to align two images
  • This means finding a single deformation function to move one image onto the other
  • We use a 4×4 Φ matrix which encodes translations, rotations and homogeneous deformations

$$\Phi = \begin{pmatrix}F11 & F12 & F13 & T1\\\ F21 & F22 & F23 & T2 \\\ F31 & F32 & F33 & T3 \\\ 0 & 0 & 0 & 1\end{pmatrix} \vec{X} = \begin{pmatrix}X \\\ Y \\\ Z \\\ 1\\\ \end{pmatrix}$$

$$I\cdot\vec{X} = \vec{X}$$

$$\Phi\cdot\vec{X} = \vec{X'}$$

What are we trying to minimise?

  • With two x-ray images of different (but close) states...
  • The units of the field (µx) are commensurable
  • We take the SSQD to express similarity

$$\eta = (im1(\vec{X}) - im2(\vec{X}))^2$$

$$\eta = (im1(\Phi\cdot\vec{X}) - im2(\vec{X}))^2$$

How is this implemented?

  • Can't show too many details...
  • We solve for Φ iteratively
  • Let's see an example:
im1 im2 im1-im2
(grey = µx) (grey = µx) white is zero
im1 = tifffile.imread("snow.tif")
im2 = tifffile.imread("snow-def.tif")
R = spam.DIC.lucasKanade(im1, im2, verbose=True, imShowProgress='Z')
        

What about different modalities?

2D histogram for all pairs of voxels

Initial data -- where are the voids?

2D histogram for all pairs of voxels

Eye-lignment -- there they are!

Fitting

Fitting - some distance (98% cov)

Fitting - no distance

Now we correlate...

  • We now have a way to make greylevels correspond
  • We add these ingredients (fitted points)...
  • ...into image correlation algorithm
    (warning: may contain maths)


  • We run it first with downscaled image...
  • ...then work our way back up to full scale
  • Result:

Success! Vector-valued field!

Final, fitted joint histogram

Since we have a classification...

Let's summarise graphically:

  • Φ (Phi) deformation function
  • im1(Φ.X)-im2(X)
  • Iterative solution
  • Two-modalities of same state
  • Joint histogram
  • Fitting and correlating
  • Now: Graphical interface! (Tom photo)

Gallery of examples

What do tests look like?


          class TestFunctionDVC(unittest.TestCase):

              def tearDown(self):
                  try:
                      pass
                      os.remove("spamPhiFieldCF-corrected-N12.tsv")
                      os.remove("spamPhiFieldCF-corrected-N12-filteredRad3.tsv")
                      os.remove("spamPhiFieldCF-ignoreBadPoints.tsv")
                      os.remove("spamPhiFieldCFDel.tsv")
                  except OSError:
                      pass

              def test_computePhi(self):
                  trans1 = {'t': [0.0, 3.0, 3.0]}
                  trans2 = {'r': [-5.0, 0.0, 0.0]}
                  trans3 = {'z': [2, 2, 2]}
                  trans4 = {'s': [0.9, 0.8, 0.7]}
                  Phi1 = transf.computePhi(trans1)
                  self.assertEqual(numpy.sum([Phi1[0, -1], Phi1[1, -1], Phi1[2, -1]]), 6)
                  Phi2 = transf.computePhi(trans2)
                  self.assertEqual(numpy.sum([Phi2[0, -1], Phi2[1, -1], Phi2[2, -1]]), 0)
                  Phi3 = transf.computePhi(trans2, PhiCentre=[50.0, 50.0, 50.0], PhiPoint=[50.0, 16.0, 84.0])
                  self.assertAlmostEqual(numpy.sum([Phi3[0, -1], Phi3[1, -1], Phi3[2, -1]]), 5.926, places=2)
                  Phi4 = transf.computePhi(trans3)
                  self.assertEqual([Phi4[0, 0], Phi4[1, 1], Phi4[2, 2]], [2., 2., 2.])
                  Phi5 = transf.computePhi(trans4)
                  self.assertEqual(Phi5[0, 1], Phi5[1, 0], 0.9)
                  self.assertEqual(Phi5[0, 2], Phi5[2, 0], 0.8)
                  self.assertEqual(Phi5[1, 3], Phi5[3, 1], 0.7)
      

Testing - the concept of coverage

How many lines of the code are executed by the tests?
100% Coverage:
credits: @bloerwald

Testing - the concept of coverage

Gitlab CI of doc & testing


          image: remche/docker-ttk

          stages:
              - build
              - test
              - deploy
              - pages

          build:
            stage: build
            script:
              - pip install -r requirements.txt
              - python setup.py install

          test:
            stage: test
            script:
              - pip install -r requirements.txt
              - pip install -r requirements-dev.txt
              - python setup.py install
              - python setup.py test
              - coverage run -m unittest discover
              - coverage report

          deploy:
            stage: deploy
            image: remche/spam-manylinux
            only:
              - /^version-.*$/
            script:
              - ./build-wheels.sh
              - /opt/python/cp27-cp27mu/bin/twine upload /wheelhouse/spam-*-manylinux1_x86_64.whl

          pages:
            variables:
              GIT_SUBMODULE_STRATEGY: normal
            stage: pages
            script:
              - pip install -r requirements.txt
              - pip install -r requirements-dev.txt
              - python setup.py install
              - python setup.py build_sphinx
              - mkdir public
              - mv build/sphinx/html/* public
              - coverage run -m unittest discover
              - coverage html
              - mv coverage public
            artifacts:
              paths:
                - public
            only:
                - master
        

Gitlab CI of doc & testing

https://gricad-gitlab.univ-grenoble-alpes.fr/ttk/spam/pipelines

Conclusion

  1. First and foremost EA: Thank you RC!!
  2. Documentation is a great way to keep knowledge together
  3. A beautiful and automatic system keeps good practice alive
  4. Tests mean that code is professional
  5. Avoids a lot of conneries
  6. Thanks to gricad this is available easily!

Thanks!