Guide to TIA scripting
Transcript of Guide to TIA scripting
Guide to TIA Scripting
M. Otten
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)
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
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
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
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
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;
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');
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'’);
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.
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.
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.
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.
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.
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;
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;
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).
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
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
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
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
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
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
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.