TrueEBSD when the map and the images are in different frames edit page

Authors: Vivian Tong; Stefan Olovsjö, Seco Tools AB, R&D Materials and Technology, 737 82 Fagersta, Sweden. Contact: vivian.tong@extern.tu-freiberg.de

Sometimes the EBSD map and the SEM images are not stored the same way round. Rotate the stage between two acquisitions, load a map from software that writes its scan lines in the other direction, or import images taken in a separate session, and the two no longer agree about which way the sample points.

TrueEBSD will not guess at this, because guessing wrong does not fail loudly — the matching still runs, still fits, and hands back a finished-looking result that is quietly nonsense. So it asks, and this page shows how to answer.

What a reference frame is, what a layout is and how gridify and imageFrame put a map in the order a picture is stored in is MTEX rather than TrueEBSD, and is explained on Maps and Images. This page assumes it and gets on with the workflow.

The short WC-Co page is the ordinary case, where everything was collected in one session and there is nothing to say. Read that one first; this page is the same workflow with one extra step.

Everything here is part of MTEX - nothing has to be installed alongside it.

The data

The same cut-down WC-Co dataset as the short page: an EBSD map and four SEM images of the same area.

No plotting convention is set here. A convention decides how things are drawn; it has no bearing on how they are stored, and it is the storage that matters when two pictures are compared pixel by pixel.

mtexdata trueEbsdWCCoSmall

img = ebsd.opt.trueEbsdImgs;
ebsd = EBSDsquare (y↓→x, row↓→col)
 
 Phase  Orientations     Mineral         Color  Symmetry  Crystal reference frame
     0    89 (0.72%)  notIndexed          none                                   
     1   11180 (91%)         W C  LightSkyBlue      -6m2        X||a*, Y||b, Z||c
     2    991 (8.1%)      Co-fcc  DarkSeaGreen      m-3m                         
     3    28 (0.23%)      Co-hcp     Goldenrod     6/mmm        X||a*, Y||b, Z||c
 
 Properties: bc, bs, bands, MAD, quality, oldId
 Scan unit : um
 X × Y × Z : [10 → 30] × [8 → 23] × [0 → 0]
 Normal vector: (0,0,1)
 Square grid  :96 × 128
 source: trueEbsdWCCo, centre half of the field of view, coarsened 4x
 madeBy: trueEBSD tools/makeWCCoSmall.m

Turn the map, to have something to fix

To get a map and images that disagree, turn the map through 90°. This is what a stage rotation between acquisitions would leave you with: the map now shows the specimen at right angles to what the detectors saw.

ebsd = rotate(ebsd,90*degree);

Build the sequence

Exactly as on the short page. Nothing here mentions the rotation — as far as this code knows, these are just five pictures of one sample.

imgList = [mapImage(ebsd.bc, ebsd,         'name','bcImg'), ...
  mapImage(img.fsdB3,  'dxy',img.pixSzImg, 'name','fsdB3'), ...
  mapImage(img.fsdT3,  'dxy',img.pixSzImg, 'name','fsdT3'), ...
  mapImage(img.fsdT1,  'dxy',img.pixSzImg, 'name','fsdT1'), ...
  mapImage(img.fsdT10, 'dxy',img.pixSzImg, 'name','fsdT10')];

% some simple filtering
imgList(2:end) = rescale(imboxfilt(imgList(2:end),3));

% the same four distortions as the short page, one per hop
T = [spatialTransformShift + spatialTransformDrift, ...
     spatialTransformId, ...
     spatialTransformShift, ...
     spatialTransformTilt];

Plotting the sequence shows the problem at once: the EBSD map is on its side relative to the four images.

plot(imgList)

Fix it by turning the map's frame until the plots agree with each other — here the map needs 'x↑→y' and the images keep the default. This is the part only you can supply: it is a fact about how the data was collected, and no amount of looking at the numbers will recover it.

ebsdFrame = imgList(1).frame;
ebsdFrame.how2plot = 'x↑→y';
plot(imgList)

All five now agree on screen, which is the assertion TrueEBSD needs, and 'byScreenAlignment' restates it as a rotation.

imgList = transformReferenceFrame(imgList, ebsdFrame, 'byScreenAlignment')

plot(imgList)
imgList = mapImage (x↑→y)
 
            image    pixel  frame    layout    name                 EBSD
 1       96 × 128  0.16 um   x↑→y  row↓→col   bcImg  W C, Co-fcc, Co-hcp
 2  192 × 256 × 3  0.08 um   x↑→y  row↓→col   fsdB3                    -
 3      192 × 256  0.08 um   x↑→y  row↓→col   fsdT3                    -
 4      192 × 256  0.08 um   x↑→y  row↓→col   fsdT1                    -
 5      192 × 256  0.08 um   x↑→y  row↓→col  fsdT10                    -

Run the workflow

From here nothing is different. The sequence is in one frame, so the rest is the ordinary workflow — see the short page for what each step does.

job = trueEbsd2(imgList,T);

job.pixelSizeMatch

job.setOptions('numROI',16)
job.setOptions(3:5,'registerOn','raw')

job.calcDistortion('fitErr')
using default pixel size of 0.079501 um, minimum from imgList
 
ans = trueEbsd2 (pixel size matched)
 
      name          image   distortion  shift, px  residual, px
 1   bcImg      192 × 256  shift-drift          -             -
 2   fsdB3  192 × 256 × 3     identity          -             -
 3   fsdT3      192 × 256        shift          -             -
 4   fsdT1      192 × 256         tilt          -             -
 5  fsdT10      192 × 256                       -             -
 
 common grid: 192 × 256 at 0.08 µm
 
 
ans = trueEbsd2 (pixel size matched)
 
      name          image   distortion  shift, px  residual, px
 1   bcImg      192 × 256  shift-drift          -             -
 2   fsdB3  192 × 256 × 3     identity          -             -
 3   fsdT3      192 × 256        shift          -             -
 4   fsdT1      192 × 256         tilt          -             -
 5  fsdT10      192 × 256                       -             -
 
 common grid: 192 × 256 at 0.08 µm
 
 
ans = trueEbsd2 (pixel size matched)
 
      name          image   distortion  shift, px  residual, px
 1   bcImg      192 × 256  shift-drift          -             -
 2   fsdB3  192 × 256 × 3     identity          -             -
 3   fsdT3      192 × 256        shift          -             -
 4   fsdT1      192 × 256         tilt          -             -
 5  fsdT10      192 × 256                       -             -
 
 common grid: 192 × 256 at 0.08 µm
 

 ◆ measured settings, override with setOptions
   ▸ edgeWidth  per map  4 4 5 5 5 px
   ▸ roiSize    per hop  32 64 64 64 px   ← shifts 5.0 1.2 3.2 3.5 px, features 4 7 5 5 px

 ◆ distortion across 5 maps, 4 hops

    distortion   stage              ROI          shift, px       residual, px
    ──────────── ───────────── ──────── ────────────────── ──────────────────
  ● bcImg
  │ shift-drift  shift            32 px 4.19 (+0.99,+0.12)
  │              drift            32 px 3.72 (+0.33,+0.34)
  │              ↳ residual                                3.36 (+0.26,+0.19)
  │ ↻ residual over 2 px, retrying with a 64 px ROI
  │ shift-drift  shift            64 px 3.64 (+2.20,+1.84)
  │              drift            64 px 2.04 (+0.23,+0.49)
  │              ↳ residual                                1.53 (+0.07,+0.09)
  ▼
  ● fsdB3
  │ identity     ·                    · 0.00 (+0.00,+0.00)
  │              ↳ difference                              1.20 (-0.11,-1.02)
  ▼
  ● fsdT3
  │ shift        shift            64 px 3.18 (-2.67,-1.73)
  │              ↳ residual                                0.33 (+0.12,+0.23)
  ▼
  ● fsdT1
  │ tilt         projective       64 px 3.63 (+2.84,-2.25)
  │              poly11           64 px 0.29 (+0.07,-0.12)
  │              poly22           64 px 0.29 (+0.07,-0.12)
  │              ↳ residual                                0.28 (+0.06,-0.12)
  ▼
  ● fsdT10

 
ans = trueEbsd2 (shifts calculated)
 
      name          image   distortion           shift, px        residual, px
 1   bcImg      192 × 256  shift-drift  1.08 (+0.20,+0.08)  1.53 (+0.07,+0.09)
 2   fsdB3  192 × 256 × 3     identity  0.00 (+0.00,+0.00)  1.20 (-0.11,-1.02)
 3   fsdT3      192 × 256        shift  3.18 (-2.67,-1.73)  0.33 (+0.12,+0.23)
 4   fsdT1      192 × 256         tilt  0.35 (+0.16,-0.03)  0.28 (+0.06,-0.12)
 5  fsdT10      192 × 256                                -                   -
 
 common grid: 192 × 256 at 0.08 µm
job.undistort

plot(job.undistortedList)
ans = trueEbsd2 (undistorted)
 
      name          image   distortion           shift, px        residual, px
 1   bcImg      192 × 256  shift-drift  1.08 (+0.20,+0.08)  1.53 (+0.07,+0.09)
 2   fsdB3  192 × 256 × 3     identity  0.00 (+0.00,+0.00)  1.20 (-0.11,-1.02)
 3   fsdT3      192 × 256        shift  3.18 (-2.67,-1.73)  0.33 (+0.12,+0.23)
 4   fsdT1      192 × 256         tilt  0.35 (+0.16,-0.03)  0.28 (+0.06,-0.12)
 5  fsdT10      192 × 256                                -                   -
 
 common grid: 192 × 256 at 0.08 µm

Check the result

The aligned images are properties of the map, exactly as on the short page.

The map and the images come out the same way round and the same shape. That is the point of having said which frame everything is in: gridify takes a layout, so the map is rebuilt in the frame you asked for rather than in MTEX's standard one, and ebsd.bc can then be put beside an aligned image pixel for pixel with nothing in between.

ebsdOut = job.undistortedList(1).ebsd;

fprintf('aligned image: %d × %d\n', size(job.undistortedList(1).img,1), size(job.undistortedList(1).img,2));
fprintf('EBSD object  : %d × %d\n', size(ebsdOut,1), size(ebsdOut,2));

figure
nextAxis
plot(ebsdOut('W C'), ebsdOut('W C').orientations, 'coordinates','on')
title('Undistorted MTEX EBSD map (WC IPF out of screen)','Color','k')

nextAxis
im = ebsdOut.fsdT1;
if size(im,3) > 1, im = mean(im,3); end
plot(ebsdOut, im, 'coordinates','on')
mtexColorMap gray
title('Undistorted fsdT1','Color','k')
aligned image: 192 × 256
EBSD object  : 192 × 256

The real test is whether the alignment worked. Compare the map's band contrast against the image aligned to it — they should track each other closely, and they do, just as well as on the short page where no rotation was involved. Saying which way round the map was cost nothing in quality; it only made an otherwise impossible case possible.

a = double(ebsdOut.bc);
b = double(job.undistortedList(1).img);
k = isfinite(a) & isfinite(b) & a > 0;
r = corrcoef(a(k),b(k));
fprintf('corr(ebsd.bc, aligned image) = %.4f\n', r(1,2));
corr(ebsd.bc, aligned image) = 0.9305

Finish

Everything this page added to the short workflow was two lines: turning the map's frame until the plots agreed, and one call to transformReferenceFrame to say so. The sequence, the distortions, the box settings and the result are identical.

If your map and images were collected the same way up you need none of it. Reach for this when the plots disagree, or when building the job stops with trueEbsd:frameMismatch — which is TrueEBSD telling you it has spotted the problem rather than guessing past it.

Citing this page. This page is part of the documentation of MTEX, a free and open source MATLAB toolbox for analyzing and modeling crystallographic textures. It was written by The MTEX Developers and is published at https://mtex-toolbox.github.io/example_WCCoFrames_2.html. If you use MTEX, or reuse text or figures from this page, in your research, please cite

F. Bachmann, R. Hielscher, H. Schaeben: Texture Analysis with MTEX - Free and Open Source Software Toolbox, Solid State Phenomena 160 (2010), 63-68. 10.4028/www.scientific.net/SSP.160.63

BibTeX
@article{bachmann2010mtex,
  author  = {F. Bachmann and R. Hielscher and H. Schaeben},
  title   = {Texture Analysis with MTEX - Free and Open Source Software Toolbox},
  journal = {Solid State Phenomena},
  volume  = {160},
  pages   = {63-68},
  year    = {2010},
  doi     = {10.4028/www.scientific.net/SSP.160.63},
  url     = {https://doi.org/10.4028/www.scientific.net/SSP.160.63}
}

Other papers describing specific MTEX methods are listed under Publications — please cite the one that best fits your application. The MTEX source code is licensed under the GNU General Public License v2.0; the text and figures of this documentation are licensed under CC BY 4.0, which permits reuse — including by automated systems — provided The MTEX Developers and this page are credited.