Guide to TIA scripting

24
Guide to TIA Scripting M. Otten

Transcript of Guide to TIA scripting

Page 1: Guide to TIA scripting

Guide to TIA Scripting

M. Otten

Page 2: Guide to TIA scripting

2

TIA Scripting

•Powerful but complex scripting language allows detailed control

• Acquire images, line scans, etc.

• Process data

•Works from the outside (not from a text editor inside TIA)

• You must choose a development language (C++, C#, JScript, Delphi)

• Protocol is COM (similar to TEM Scripting)

• Full debugging from development language available

•All examples shown are from Delphi (most familiar to me)

Page 3: Guide to TIA scripting

3

Short on Delphi

If you are unfamiliar with Pascal:

•begin … end is equivalent to C { … }

•strings are between single quotes ' ', not double " "

•try … except … end is equivalent to try {} … catch {}

•result in a function (procedure with return value) is return value

•Delphi is case insensitive

•In code examples red behind // is comment

Page 4: Guide to TIA scripting

4

Difference between DM and TIA Scripting

DigitalMicrograph

image FrontImage := GetFrontImage()

number min,max

GetLimits(FrontImage,min,max)

result(min+"\n")

Microscope

Control of DM

No (or very limited)

control of TEM

Your application

TIA

TEM

Scripting

TIA

Scripting

Page 5: Guide to TIA scripting

5

Getting started (1) Import type library

Import type library (more info for

other languages, see TEM Scripting

help)

That is the one to use

Page 6: Guide to TIA scripting

6

Getting started (2) Declarations

•Strong type : Define each variable exactly. Errors in TIA Scripting

become compile-time errors.

•Weak type : Define variables as OLE variant. Easier to use but errors

only pop up at run time.

FTia,

FDisplayWindow,

FDisplayWindowName,

FDisplay,

FImage : OleVariant;

Typical weak-type declarations

FTia : IDispatch;

FDisplayWindow : DisplayWindow;

FDisplay : IDispatch

FImage : Image;

FDisplayWindowName : WideString;

Typical strong-type declarations

Page 7: Guide to TIA scripting

7

Getting started (3) Different ways of coding

// Declaration

FCcdServer, IntTimeRange : OleVariant;

// Use

FCcdServer := FTia.CcdServer

FCcdServer.AcquireMode := esSingleAcquire;

try

IntTimeRange := FCcdServer.GetIntegrationTimeRange;

CcdMaxReadout := IntTimeRange.End;

CcdMinReadout := IntTimeRange.Start;

except

CcdMaxReadout := 60;

CcdMinReadout := 0.05;

end;

// Use

FTia.CcdServer.AcquireMode := esSingleAcquire;

try

CcdMaxReadout := FTia.CcdServer.GetIntegrationTimeRange.End;

CcdMinReadout := FTia.CcdServer.GetIntegrationTimeRange.Start;

except

CcdMaxReadout := 60;

CcdMinReadout := 0.05;

end;

Page 8: Guide to TIA scripting

8

Getting started (4) Connect to TIA

•If not already started, will start automatically (standard COM behavior)

•If not desirable (e.g. DM must be started first for access to CCD’s),

check if TIA is running (esvision.exe) through Windows PSAPI

FTia := CreateOleObject('ESVision.Application');

Page 9: Guide to TIA scripting

9

Working with TIA

•Everything you work with will be located in a DisplayWindow.

You first have to find or create the relevant items.

•If you are looking for a DisplayWindow with a known name.

•If you want the topmost DisplayWindow.

•Bringing you DisplayWindow to the top.

FDisplayWindow := FTia.FindDisplayWindow('My displaywindow name');

FDisplayWindow := FTia.ActiveDisplayWindow;

FTia.ActivateDisplayWindow('My displaywindow name'’);

Page 10: Guide to TIA scripting

10

Example : Creating an FFT of an image

// Declaration

var DisplayNames,FDisplay : Olevariant;

indx : integer;

// Use

FDisplayWindow := FTia.ActiveDisplayWindow; // take the topmost displaywindow

FDisplay := Unassigned; // clear anything we had before

DisplayNames := FDisplayWindow.DisplayNames;

for indx := 0 to DisplayNames.Count-1 do // find an image display

begin

FDisplay := FDisplayWindow.FindDisplay(DisplayNames.Item[indx]);

if FDisplay.Type = esImageDisplay then break; // we got it

end;

if not VarIsEmpty(FDisplay) then // else there is nothing

begin

FImage := FDisplay.Image;

if not VarIsEmpty(FImage) then // else there was no image

begin

FFftDisplay := FDisplayWindow.AddDisplay('FFT Display',esImageDisplay,esImageDisplayType,

esSplitRight,0.5,VarAsType(FDisplay,varDispatch)); // add a new display, see footnote

FFft := FFftDisplay.AddImage('FFT',512,512,FTia.Calibration2D(0,0,2,2,256,256));

// parameters defined aren’t really relevant, correct one will come from the processing

FFft := FTia.ProcessingSystem.FFT(FImage);

FFft.ScaleMarker := false; // no scale marker displayed in FFT

FDisplay.AutoScale(esAutoScaleAll); // autoscale displays

FFftDisplay.AutoScale(esAutoScaleAll);

FFftDisplay.Legend := true; // show legend in FFT display

end;

end;

Footnote: Delphi sometimes converts IDispatch to an OLE variant of the IDispatch and then TIA does not

recognize it. The “VarAsType(FDisplay,varDispatch)” forces Delphi to keep it an IDispatch.

Page 11: Guide to TIA scripting

11

Example : Changing the image calibration

//General note: use the example on page 10 on how to find the image

// Declaration

var TempImage,Calibration : Olevariant;

// Use

if not VarIsEmpty(FImage) then

begin

Calibration := FImage.Data.Calibration; // retrieve the existing calibration

Calibration.OffsetX := Cal.OffsetX / 2;

Calibration.OffsetY := Cal.OffsetY / 2;

Calibration.DeltaX := Cal.DeltaX / 2;

Calibration.DeltaY := Cal.DeltaY / 2; // we ignore the CalIndexX and Y, generally 0

TempImage := FImage.Data; // effectively creates a copy

TempImage.Calibration := Calibration; // set the copy’s calibration to the modified

FImage.Data := FTia.ProcessingSystem.Mul(TempImage,FTia.ComplexNumber(1,0));

// using the multiplication by 1 (does nothing to image) forces the recalibrated image in

end;

Note: You cannot simply get the calibration of the image, change it and force it back in. If you do that nothing happens. You have to work through a copy.

Page 12: Guide to TIA scripting

12

Example : Changing an image - slow

//General note: use the example on page 10 on how to find the image

// Declaration

var TempImage : Olevariant;

pixx,pixy : integer;

// Use

if not VarIsEmpty(FImage) then

begin

pixx := FImage.Data.PixelsX div 2; // gets us to the center of the image

pixy := FImage.Data.PixelsY div 2;

TempImage := FImage.Data; // effectively creates a copy

TempImage.PixelIntensity(pixx,pixy) := FTia.ComplexNumber(TempImg.PixelIntensity(pixx+1,pixy+1),0);

// makes the center pixel equal to the one diagonally above it

// note that in TIA images the y direction runs from bottom to top

FImage.Data := VarAsType(TempImage,VarDispatch); // see again the footnote on page 10

end;

Note: You cannot simply modify an existing the image. You first make a copy, change that and force it back in.

Note: This way of working is slow because for each PixelIntensity call you cross the COM boundary (you are working on an image in TIA). Fine for a few changes but not otherwise.

Page 13: Guide to TIA scripting

13

Example : Changing an image - fast

//General note: use the example on page 10 on how to find the image

// Declaration

var TempImage : Olevariant;

Arr,Transfer : Variant;

indx,indy,DataType,ImSizX,ImSizY : integer;

// Use

if not VarIsEmpty(FImage) then

begin

Arr := FImage.Data.Array; // gets the whole image (safearray)

DataType := VarType(Arr[1,1]);

ImSizx := VarArrayHighBound(Arr,1)-VarArrayLowBound(Arr,1)+1; // get image dimensions

// alternative ImSizX := FImage.Data.PixelsX;

ImSizy := VarArrayHighBound(Arr,2)-VarArrayLowBound(Arr,2)+1;

Transfer := VarArrayCreate([0,ImSizx-1,0,ImSizy-1],DataType); // create a copy array

for indy := 0 to ImSizY-1 do

begin

for indx := 0 to ImSizX-1 do

Transfer[indx,indy] := Arr[indx,ImSizY-indy-1]; // flip the image upside down

end;

TempImage := FImage.Data;

TempImage.Array := VarAsType(Transfer,VarType(Transfer)); // point the contents of TempImage

FImage.Data := VarAsType(TempImage,VarType(TempImage)); // and copy it back in

end;

Note: This way of working is fast because you first get the whole image over, process that locally and then push it back.

Page 14: Guide to TIA scripting

14

Changing an image - caveat

If the image you are working with is part of a series, you cannot

modify the image, change the image number in the series and go

back to the image you modified.

TIA does not keep the series in memory but loads them from the

.ser file. This file does not get modified when you change an

individual image.

The only way of modifying such images is by :

• Make sure DisplayWindow is saved.

• Close DisplayWindow (otherwise you cannot change .ser file).

• Modify .ser file (public file format).

• Re-open DisplayWindow.

Page 15: Guide to TIA scripting

15

Example : Getting image information

//General note: use the example on page 10 on how to find the image

// Declaration

var PixelSize,Mean,Sd,Max,Min,IntensityRangeMin,IntensityRangeMax : double;

// Use

if not VarIsEmpty(FImage) then

begin

// getting stuff is easy

Mean := FTia.ProcessingSystem.Mean(FImage.Data).Real; // do no forget the .Real

Sd := sqrt(FTia.ProcessingSystem.Variance(FImage.Data).Real);

Max := FTia.ProcessingSystem.Max(FImage.Data).Real;

Min := FTia.ProcessingSystem.Min(FImage.Data).Real;

PixelSize := FImage.Data.Calibration.DeltaX;

IntensityRangeMin := FImage.DisplayIntensityRange.Start;

IntensityRangeMax := FImage.DisplayIntensityRange.End;

// setting is slightly more elaborate

FImage.DisplayIntensityRange := FTia.Range1D(IntensityRangeMin / 2,IntensityRangeMax * 2)

end;

Page 16: Guide to TIA scripting

16

Example : Processing an image

//General note: use the example on page 10 on how to find the image

// Declaration

var TempImage : Olevariant;

Mean,Min : double;

// Use

if not VarIsEmpty(FImage) then

begin

// for numerical processing, always use the FTia.ComplexNumber

FImage := FTia.ProcessingSystem.Mul(FImage,FTia.ComplexNumber(2,0)); // multiply

FImage := FTia.ProcessingSystem.Div(FImage,FTia.ComplexNumber(3.4,0)); // divide

FImage := FTia.ProcessingSystem.sqr(FImage); // square

Min := FTia.ProcessingSystem.Min(FImage.Data).Real;

// prevent problems with negative values with square root

if Min <= 0 then FImage := FTia.ProcessingSystem.Add(FImage,FTia.ComplexNumber(-Min,0));

FImage := FTia.ProcessingSystem.sqrt(FImage); // square root

// alternative, use absolute value

FImage := FTia.ProcessingSystem.sqrt(FTia.ProcessingSystem.abs(FImage));

Mean := FTia.ProcessingSystem.Mean(FImage.Data).Real;

TempImage := FTia.ProcessingSystem.Div(FImage,FTia.ComplexNumber(Mean,0)); // Normalize

FImage := FTia.ProcessingSystem.CrossCorrelation(TempImage,TempImage); // Autocorrelation

end;

Page 17: Guide to TIA scripting

17

Example : Create a duplicate image

//General note: use the example on page 10 on how to find the image

// Declaration

var FDisplay2,FImage2 : double;

// Use

if not VarIsEmpty(FImage) then

begin

FDisplay2 := FDisplayWindow.AddDisplay('Image 2 Display',esImageDisplay,esImageDisplayType,

esSplitRight,0.5,VarAsType(FDisplay,varDispatch)); // add a new display

FImage2 := FDisplay.AddImage('Image 2',512,512,FTia.Calibration2D(0,0,2,2,256,256));

FImage2.Data := VarAsType(FImage.Data,VarDispatch); // copy the data

FImage2.ObjectInfo := FImage.ObjectInfo; // copy the info

end;

Note: When you copy an image, the image itself is copied, along with its calibration. What is not copied automatically is the information about acquisition conditions (visible when opening “Info”in popup menu).

Page 18: Guide to TIA scripting

18

Retrieving information : CCD camera name

//General note: use the example on page 10 on how to find the image

function GetCameraNameFromInfo: string;

var p : integer;

st,st2 : string;

begin

st := FImage.ObjectInfo.xml; // that’s where the info is

p := pos('<AcquireInfo>',st); // find <AcquireInfo> in the string

if p <> 0 then

begin

delete(st,1,p+length('<AcquireInfo>')-1); // delete the string up to the end of <AcquireInfo>

p := pos('<CameraNamePath>',st); // find <CameraNamePath> in the remainder

if p <> 0 then

begin

delete(st,1,p+length('<CameraNamePath>')-1); // delete part of the string again

p := pos('<',st); // find the closing < of the xml

if p <> 0 then

begin

st2 := copy(st,1,p-1); // copy what is between there

if st2 <> '' then result := st2; // and that is the name of the CCD

end;

end;

end;

end;

Note: The information about acquisition conditions is present in the ObjectInfo.xml

Page 19: Guide to TIA scripting

19

Retrieving information: Binning

//General note: use the example on page 10 on how to find the image

function GetBinningFromInfo: integer;

var p,i : integer;

st2 : string;

begin

st := FImage.ObjectInfo.xml; // that’s where the info is

p := pos('<AcquireInfo>',st); // find <AcquireInfo> in the string

if p <> 0 then

begin

delete(st,1,p+length('<AcquireInfo>')-1); // delete the string up to the end of <AcquireInfo>

p := pos('<Binning>',st); // find <Binning> in the remainder

if p <> 0 then

begin

delete(st,1,p+length('<Binning>')-1); // delete part of the string again

p := pos('<',st); // find the closing < of the xml

if p <> 0 then

begin

st2 := copy(st,1,p-1); // copy what is between there

if st2 <> '' then result := strtoint(st2); // convert to integer and we have the binning

end;

end;

end;

end;

Note: The information about acquisition conditions is present in the ObjectInfo.xml

Page 20: Guide to TIA scripting

20

Retrieving information : Magnification

//General note: use the example on page 10 on how to find the image

function GetMagnificationFromInfo: double;

var p : integer;

st,st2 : string;

d : double;

begin

st := FImage.ObjectInfo.xml; // that’s where the info is

p := pos('<AcquireInfo>',st); // find <AcquireInfo> in the string

if p <> 0 then

begin

delete(st,1,p+length('<AcquireInfo>')-1); // delete the string up to the end of <AcquireInfo>

p := pos('<Magnification>',st); // find <Magnification> in the remainder

if p <> 0 then

begin

delete(st,1,p+length('<Magnification>')-1); // delete part of the string again

p := pos('X',st); // find the "X" at the end of the value

if p <> 0 then

begin

st2 := copy(st,1,p-1); // copy what is between there

if st2 <> '' then

begin

if st2[length(st2)] = ' ' then delete(st2,length(st2),1); // remove spaces

result := strtofloat(st2); // convert to a floating point value

end;

end;

end;

end;

end;

Note: The information about acquisition conditions is present in the ObjectInfo.xml

Page 21: Guide to TIA scripting

21

Acquiring an image : CCD// Declarations are skipped here

FDisplayWindow := FTia.AddDisplayWindow;

FDisplayWindow.Name := 'My displaywindow';

FDisplay := FDisplayWindow.AddDisplay('Acquire CCD Image Display',esImageDisplay,esImageDisplayType,

esSplitRight,1); // for diffraction patterns use esRecImageDisplayType instead of esImageDisplayType !

ImageSizeX := EndX-StartX; // Make sure all parameters fit, get from CcdServer.GetTotalPixelReadoutRange

ImageSizeY := EndY-StartY;

FImage := FDisplay.AddImage('Acquire CCD Image',ImageSizeX,ImageSizeY,FTia.Calibration2D(0,0,2,2,256,256));

if FTia.AcquisitionManager.IsAcquiring then FTia.AcquisitionManager.Stop; // should check that stop worked !

if not FTia.AcquisitionManager.DoesSetupExist('MySetupName') then // this setup stuff is necessary

FTia.AcquisitionManager.AddSetup('MySetupName');

FTia.AcquisitionManager.SelectSetup('MySetupName');

FTia.AcquisitionManager.UnlinkAllSignals;

FTia.AcquisitionManager.LinkSignal('CCD',FImage); // "CCD" is generic, independent of the actual CCD name

FTia.CcdServer.IntegrationTime := Time;

FTia.CcdServer.Binning := Binning;

FTia.CcdServer.PixelReadOutRange := FTia.Range2D(StartX*Bin,StartY*Bin,EndX*Bin,EndY*Bin);

if FTia.CcdServer.IsCameraRetractable then

begin

if not FTia.CcdServer.CameraInserted then

begin

FTia.CcdServer.CameraInserted := true;

Sleep(5000); // have to wait until it is in

end;

end;

FTia.CcdServer.AcquireMode := esSingleAcquire;

if not VarIsEmpty(FTia.ScanningServer) then FTia.ScanningServer.ScanMode := esSpotMode;

// important: check on ScanningServer. If the system is not a STEM, Scanningserver does not exist

// also for CCD must be in SpotMode

FTia.AcquisitionManager.Start; // now wait until image is in.

// Either monitor AcquisitionManager.IsAcquiring or use acquisition events

Page 22: Guide to TIA scripting

22

Acquiring an image : STEM// Most declarations are skipped here

var SignalNames, TotalScanRange : OleVariant; DetectorName : string; resol : double;

// Use

FDisplayWindow := FTia.AddDisplayWindow;

FDisplayWindow.Name := 'My displaywindow';

FDisplay := FDisplayWindow.AddDisplay('Acquire STEM Image Display',esImageDisplay,esImageDisplayType,

esSplitRight,1);

ImageSizeX := EndX-StartX; // Make sure all parameters fit, from ScanningServer.GetTotalPixelReadoutRange

ImageSizeY := EndY-StartY;

FImage := FDisplay.AddImage('Acquire STEM Image',ImageSizeX,ImageSizeY,FTia.Calibration2D(0,0,2,2,256,256));

if FTia.AcquisitionManager.IsAcquiring then FTia.AcquisitionManager.Stop; // should check that stop worked !

if not FTia.AcquisitionManager.DoesSetupExist('MySetupName') then // this setup stuff is necessary

FTia.AcquisitionManager.AddSetup('MySetupName');

FTia.AcquisitionManager.SelectSetup('MySetupName');

FTia.AcquisitionManager.UnlinkAllSignals;

// this part is a bit problematic, you have to know on which channel the detector is

SignalNames := FTia.AcquisitionManager.TypedSignalNames(esAnalogImageSignal);

DetectorName := SignalNames.Item[0]; // here hardcoded to channel 1 but you may need something different

FTia.AcquisitionManager.LinkSignal(DetectorName,FImage);

TotalScanRange := FTia.ScanningServer.GetTotalScanRange;

resol := (TotalScanRange.EndX-TotalScanRange.StartX)/StemMaxXSize; // StemMaxSize can be 2048 or 4096 !

FTia.ScanningServer.ScanResolution := resol * PixelSkipping; // PixelSkipping is like binning on CCD

xrange := (EndX-StartX)*(PixelSkipping*(TotalScanRange.EndX-TotalScanRange.StartX)/StemMaxXSize)/2;

yrange := (EndY-StartY)*(PixelSkipping*(TotalScanRange.EndY-TotalScanRange.StartY)/StemMaxYSize)/2;

FTia.ScanningServer.ScanRange := FTia.Range2D(-xrange,-yrange,xrange,yrange);

FTia.ScanningServer.DwellTime := FrameTime/(1.2*((EndX-StartX)*(EndY-StartY)));

FTia.ScanningServer.AcquireMode := esSingleAcquire;

FTia.ScanningServer.ScanMode := esFrameMode;

FTia.AcquisitionManager.Start; // now wait until image is in.

// Either monitor AcquisitionManager.IsAcquiring or use acquisition events

Page 23: Guide to TIA scripting

23

Working with series

•You can acquire data in series. If it is a time-series (same

acquisition on the same position), you can just define the number

of items in the series and leave the calibration at 0.

•For a line scan, define a 1D series, for a map a 2D series. In

these cases the calibration of the series defines where TIA

considers the data to be and you can simply use the existing tools

in TIA to process data in series (like extract profile/map).

// Add a spectrum to a created spectrum display

FSpectrum := FSpectrumDisplay.AddSpectrum('EDX spectrum',ex,FTia.Calibration1D(ex/2,ev,0));

// we need to define where the series file goes

tdir := FTia.Directory(esTempDirectory);

if tdir[length(tdir)] <> '\' then tdir := tdir + '\'; // make sure we have a trailing '\'

FSpectrum.Set2DFileSeries(tdir+'SpectrumSeries.ser',SizeX,SizeY,

FTia.Calibration2D(-CalX*sx/2,-CalX*sy/2,CalX,CalX,0,0));

// for the calibration you could e.g. coly the values from the range of an image selection marker

Page 24: Guide to TIA scripting

24

Beam position

•TIA has two “beam position” elements:

• In the ScanningServer

• In BeamControl

•ScanningServer BeamPosition displaces the scan frame (within limits),

e.g. for drift correction. So this cannt be used for user-defined scan

patterns.

•BeamControl allows definition of a scan pattern. But note: positions are

defined through a PositionCollection. You add positions one by one. For

each new position you cross the COM boundary so do not expect very

high transfer speeds! Once positions are defined, you can scan at any

rate (dwell time) supported by the scan board.