Navigating a 4D dataset - dm-script

For a 3D dataset, GMS 3 will assign a slice tool to navigate the 3rd dimension - typically used to navigate EELS and EDS SIs.
For a 4D dataset, this is not the case - in fact, no slider appears at all, nevermind two.
I would like to create a small GUI with sliders for each additional dimension beyond the two "image" dimensions, but I'm not sure what command is used to change the visible slice on the screen. I'd appreciate it if someone could help.
As for creating a small 4D image, it can be done with the collowing code:
image A = IntegerImage("", 2, 0, 16, 16, 16, 16)
ShowImage(A)

The slice tool for 4D is a licensed tool - then it works out of the box.
But if you want to build a proxy by script, you can do this.
The commands for getting/setting the "displayed" slices in a 3D and 4D display are the following:
void ImageDisplayGetDisplayedLayers( ImageDisplay imgDisp, NumberVariable start, NumberVariable end )
void ImageDisplayGetDisplayedLayers( ImageDisplay imgDisp, NumberVariable start1, NumberVariable end1, NumberVariable start2, NumberVariable end2 )
void ImageDisplaySetDisplayedLayers( ImageDisplay imgDisp, Number start, Number end )
void ImageDisplaySetDisplayedLayers( ImageDisplay imgDisp, Number start1, Number end1, Number start2, Number end2 )
Here is an example of how to use them:
image Img := realimage("4D Data", 4, 3, 5, 7, 9 )
Img = 10000 + idimindex(0) + 10 * idimindex(1) + 100 * idimindex(2) + 1000 * idimindex(3)
Img.ShowImage()
imageDisplay disp = A.ImageGetImageDisplay(0)
number start1,start2
number end1,end2
disp.ImageDisplayGetDisplayedLayers(start1,end1,start2,end2)
ClearResults()
Result( "Current Display Sliders are:" )
Result( "\n 3rd dim:" + start1 + " - " + end1 )
Result( "\n 4th dim:" + start2 + " - " + end2 )
OKDialog( "Now setting..." )
disp.ImageDisplaySetDisplayedLayers(start1,end1,start2+2,end2+2)
disp.ImageDisplayGetDisplayedLayers(start1,end1,start2,end2)
Result( "Current Display Sliders are now:" )
Result( "\n 3rd dim:" + start1 + " - " + end1 )
Result( "\n 4th dim:" + start2 + " - " + end2 )
However, I have just checked the free version of GMS 3.2.2 as downloaded from Gatan's webpage.
If you use the SI Viewer license (which is free), then the 4D slice-tool works just fine. As does the 4D picker-tool, apparently.

Related

how to get the length scale, length unit, energy scale and energy unit from a EELS map data by DM script

I want to get length scale, length unit, energy scale, and energy unit from an EELS map data by DM script
source.ImageGetDimensionCalibration(0, sOrig, sScale, sUnit, 0)
source.ImageGetDimensionCalibration(2, eOrig, eScale, eUnit, 0)
The above codes did not work, it always make the energy unit with 1eV
For example, an EELS data with dispersion 0.9 ev/pixel, or 0.5 ev/pixel, and the energy range from 0 to 400, and the EELS map is 10nm x8nm, I need to get all this information with DM script.
Now the bug of my code is it always count the disepersion is 1 ev/pixel
Any suggestions, thanks
The following script will hopefully clear up any questions:
void PrintCalibrations(image source)
{
number nDim = source.ImageGetNumDimensions()
result("\n '[" + source.ImageGetLabel() +"]:"+ source.ImageGetName() +"' calibrations:")
result("\n Data has " + nDim + " dimensions.")
for( number d=0; d<nDim; d++ )
{
string unit_dim
number scale_dim, origin_dim
source.ImageGetDimensionCalibration(d, origin_dim, scale_dim, unit_dim, 0)
result("\n\t Dimension #"+d+": scale = " + scale_dim + " \t origin = " + origin_dim + " \t [" + unit_dim + "]" )
}
}
// Pick your data by image letter!
Image SIdata_3D := B
Image PickerSpectrum_1D := A
Image elementalMap_2D := D
ClearResults()
PickerSpectrum_1D.PrintCalibrations()
elementalMap_2D.PrintCalibrations()
SIdata_3D.PrintCalibrations()
Applied to a test data set:

DM script to calculate area under a curve with given ROI

I want to calculate the area under a curve with giving ROI, like the attached picture, I want to get the area under the green curve from 13 to 30, could we reach this function by DM scripting?
Never done similar things before, any suggestions thanks!
If the image is of label "A" then, you can simply do:
sum( A[] )
or, a bit more elaborated:
image img := GetFrontImage()
number integral = sum( img[] )
result("\n The intensity of the selected region in image [")
result( img.imageGetLabel() + "]:" + img.ImageGetName() + " is: ")
result( integral)
Note that this will give you the integral (or just the sum of channel-values) in raw numbers. If you have calibrated the intensity, you will have to take the calibration into account.
If - as in most cases - you just have an intensity scale but no offset, it is straight forward:
image img := GetFrontImage()
number integral = sum( img[] )
number scale = img.ImageGetIntensityScale()
number integral_cal = integral * scale
result("\n The intensity of the selected region in image [")
result( img.imageGetLabel() + "]:" + img.ImageGetName() + " is: ")
result( integral_cal )
If you also have an origin, it becomes:
image img := GetFrontImage()
number integral = sum( img[] )
number scale = img.ImageGetIntensityScale()
number origin = img.ImageGetIntensityOrigin()
number t,l,b,r
img.GetSelection(t,l,b,r)
number nChannels = r - l
number integral_cal = (integral - origin * nChannels) * scale
result("\n The intensity of the selected region in image [")
result( img.imageGetLabel() + "]:" + img.ImageGetName() + " is: ")
result( integral_cal )

How to set starting and ending point for line projection in DM script

I am trying to draw line projection for an image . The line 4 in the code below sy/2 represents the length of projection (here is the half image range). But how to set the starting point or ending point with scripting? For example, I want to draw the line projection, from 1/4 image range to 3/4 image range. Any suggestions?
image src := getfrontimage()
number sx,sy
src.GetSize(sx,sy)
image line_projection := RealImage( "Vertical", 4, sy/2 )
line_projection[irow,0] += src
line_projection *= 1/sx
While using intrinsic variables (icol,irow,...) for iterative summing was the fasted method in GMS 1, this is no longer true for newer versions that utilize multi-threaded code, as demonstrated by the following example:
// various ways to sum a subsection of an image
number sx = 4096, sy = 4096
number startx = 0.2, starty = 0.2
number endx = 0.8, endy = 0.4
// Coordinates of cut
number t = trunc(starty*sy), l = trunc(startx*sx), b = trunc(endy*sy), r = trunc(endx*sx)
image test := realImage( "Test", 4, sx, sy )
test = sin( icol/iwidth * 20*Pi()) + cos( itheta * iradius/iwidth * 50)
test= sin( icol/iwidth * 20*Pi())
test.ShowImage()
ROI marker = NewROI()
marker.ROISetLabel( "Section" )
marker.ROISetRectangle( t, l, b, r )
marker.ROISetVolatile( 0 )
test.ImageGetImageDisplay(0).ImageDisplayAddRoi( marker )
//OKDialog( "Performing vertical sum with various methods now." )
number h = b - t
number w = r - l
ClearResults()
number ts, te, tps = GetHighResTicksPerSecond()
// using intrinsic variables
image sumImg1 := RealImage( "Sum intrinsic", 4, w )
ts = GetHighResTickcount()
sumImg1[icol, 0] += test[t,l,b,r];
te = GetHighResTickcount()
sumImg1.ShowImage()
result("\n Summing using intrinisic variables: " + (te-ts)/tps + " sec")
// using for-loop of slice
image sumImg2 := RealImage( "Sum with slice", 4, w )
ts = GetHighResTickcount()
for( number i=0; i<h; i++)
sumImg2 += test.slice1(0,i,0, 0,w,1)
te = GetHighResTickcount()
sumImg2.ShowImage()
result("\n Summing using for-loop with slice : " + (te-ts)/tps + " sec")
// using project of slice
image sumImg3 := RealImage( "Sum with project", 4, w )
ts = GetHighResTickcount()
sumImg3 = test[t,l,b,r].project( 1 )
te = GetHighResTickcount()
sumImg3.ShowImage()
result("\n Summing using project on section : " + (te-ts)/tps + " sec")
You can use slicing to only look at the image area you are interested in. For "clipping" the source to the interesting part use img[y1, x1, y2, x2].
image src := getFrontImage();
number width, height;
src.GetSize(width, height);
number start_y = 1/4 * height;
number end_y = 3/4 * height;
image line_projection := RealImage("Vertical", 4, width);
line_projection[icol, 0] += src[start_y, 0, end_y, width];
line_projection *= 1/(height/2);
line_projection.ShowImage();

Can one define a one dimensional image inline?

I would like to describe a very simple image (really a vector) of length 2, like (1,2) for the purpose of some linear algebra.
The following creates a two dimensional image with a y axis of length 1:
image a := [2,1]: {
{1, 2}
}
MatrixPrint(a)
This outputs
{
{1, 2}
}
How would I in a similar fashion output this instead?
{123,45}
Additionally, if I had image of arbitrary shape (a, b), how can I slice it to extract a one dimensional image at a value n, either along the x or y axes? (Extracting a line profile along one of the image axes)
In your example you do define a 2D image, so you get a 2D output. If the image really would be 1D, your output would be 1D, i.e.
image a := [2]: {123, 45}
MatrixPrint(a)
So your second question actually is the answer to your first: You need to do a 1D slice of the data, which you can do with the command slice1() as follows:
image a := [2,1]: {
{123, 45}
}
MatrixPrint( a.slice1(0,0,0,0,2,1) )
Note some peculiarities of the command:
The command always assume the input is 3D, so the first 3 parameters are the start-index triplet x/y/z even if it is just 2D or 1D data.
the 2nd triplet specifies the sampling of the slice. First the dimensions index (0=x) then the number of sampling steps (2) and then the stepsize (1)
Similar slice commands exist for 2D slices, 3D slices and nD Slices from nD data.
The matrixPrint command only outputs to the results window. There is no way to reroute this to some string. However, you can easily make yourself a method that would do that (albeit not very fast for big data):
string VectorPrint( image img, string FormatStr, number maxNum )
{
if ( !img.ImageIsValid() ) return "{invalid}"
if ( 1 != img.ImageGetNumDimensions() ) return "{not 1D}"
string out = "{ "
number nx = img.ImageGetDimensionSize(0)
if (( nx <= maxNum ) || ( maxNum <= 2) )
{
for( number i=0; i<min(nx,maxNum); i++)
out += Format( sum(img[0,i]), FormatStr ) + ", "
out = out.left( out.len() - 2 )
}
else
{
for( number i=0; i<maxNum-1; i++)
out += Format( sum(img[0,i]), FormatStr ) + ", "
out = out.left( out.len() - 2 ) + ", ... , "
out += Format( sum(img[0,nx-1]), FormatStr )
}
out += " }"
return out
}
image a := [10,4]: {
{1,2,3,4,5,6,7,8,9,10},
{123, 45, 12.3, -12, 55, 1.2, 9999, 89.100, 1e-10, 0},
{0,0,0,0,0,0,0,0,0,0},
{1,2,3,4,5,6,7,8,9,10}
}
// Slice 2D image to 1D image at n'th line
number n = 1
image line := a.slice1(0,n,0,0,a.ImageGetDimensionSize(0),1)
// Printout with given number format and a maximum number of entries
string fStr = "%3.1f"
number maxN = 3
Result( "\n "+VectorPrint( line, fStr, maxN ) )

List all X coordinates by given a Y coordinate in a line profiles by DM scripting

For a line profile (curve), I want to reach that list all X coordinates that corresponding a Y coordinate by given this Y coordinate. And I could get the minimum and maximum values of these x coordinates. Here supposed I want to list all the X coordinates corresponding y=8, is this correct or any other better way? Thx
Number minx, maxx
Image front=:getfrontimage()
GetSize( front, xsize, ysize )
for (i=0; i<xsize; i++)
{
x= getpixel(front, i, 8)
minx=min(x)
maxx=max(x)
}
You script becomes wrong when you use the min and max, because you can not get a minimum/maximum of a single value (or rather, it is always that value). What you want to do is likely:
image spec := RealImage("Test",4,100)
spec = trunc(Random()*10)
number v = 8
ClearResults()
number nCh = spec.ImageGetDimensionSize(0)
for( number i=0; i<nCh; i++)
{
if( v == sum(spec[i,0]) )
Result("\n Value "+ v +" # " + i )
}
(The sum() is needed here a a trick to convert an image-expression to a single value.)
However, going pixel-by-pixel in script can be slow. Whenever possible, try to code with image-expressions, because they are much faster (for big images).
I therefore often utilize a trick: I threshold an image for the value I search for, and then iterate over that mask as long as it is not all-zero. The max(img,x,y) command will return the first maximum if there are multiple, so I get an ordered list.
image spec := RealImage("Test",4,100)
spec = trunc(Random()*10)
spec.ShowImage()
number v = 8
image mask = (spec==v)?1:0
ClearResults()
while( 0<sum(mask) )
{
number x,y
max(mask,x,y)
Result("\n Value " + v +" # " + x )
mask[x,0]=0
}
Edit: Answering the question of the comment below.
This is how one gets the ZLP maximum (position and value) from a line-profile in calibrated values.
Precursor: DM contains all data as simple arrays and values (real or integer). These are the real data and unrelated to any calibrations. You see these values if you toggle the "calibration" checkbox off in the Image Status palette:
These are the values all script commands etc. will use, i.e. positions are always indices (starting from 0) and values are the raw numeric values stored.
These images or spectra are calibrated by defining an origin and scale (and unit) for each dimensional axis as well as the intensity (=value). These triplets of values can be found in the image display info of data:
Only when the "Show calibrated values" checkbox is checked, is the data displayed in calibrated values. However, the real values remain unchanged. Just the scale/origin values are used to convert the numbers.
If you want to use a script to use calibrated values, then you have to perform the same conversions in you script yourself.
Here is the example:
image spectrum := GetFrontImage()
number xScale = spectrum.ImageGetDimensionScale(0) // 0 for X dimension
number xOrigin = spectrum.ImageGetDimensionOrigin(0)
string xUnit = spectrum.ImageGetDimensionUnitString(0)
number iScale = spectrum.ImageGetIntensityScale()
number iOrigin = spectrum.ImageGetIntensityOrigin()
string iUnit = spectrum.ImageGetIntensityUnitString()
string info = "\n"
info += "Image ["+spectrum.ImageGetLabel()+"]:"
info += "\n\t Dimension calibration: nCh * " + xScale + " + " + xOrigin + " [" + xUnit + "]"
info += "\n\t Intensity calibration: (value - " + iOrigin + ") * " + iScale +" [" + iUnit + "]"
Result(info)
// Find ZLP maximum (uncalibrated values)
number maxP_ch, dummy, maxV_raw
maxV_raw = max(spectrum,maxP_ch,dummy)
info = "\n"
info += "\n\t The maximum position is at channel index: " + maxP_ch
info += "\n\t The maximum Value at maximum position is: " + maxV_raw
Result(info)
number maxP_cal = xOrigin + xScale * maxP_ch
number maxV_cal = (maxV_raw - iOrigin) * iScale
info = "\n"
info += "\n\t The maximum position is at : " + maxP_cal
info += "\n\t The maximum Value is : " + maxV_cal
Result(info)
Note the different calibration formulas between dimensional calibration and intensity calibration!