how to get the length scale, length unit, energy scale and energy unit from a EELS map data by DM script - 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:

Related

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 shift the column in a SI image

In order to perform drift correction in a SI image as shown in the following figure:
I write the code :
number max_shift=5
image src := GetFrontImage()
number sx, sy, sz
src.Get3DSize(sx, sy, sz)
result("sx: "+sx+"\n")
result("sy: "+sy+"\n")
result("sz: "+sz+"\n")
// assume a random shift in x
image shift := IntegerImage("xcorrd",4,0, 1, sy)
shift = max_shift*Random()
// make a coordinate table
image col := IntegerImage("col",4,0, sx, sy)
image row := IntegerImage("row",4,0, sx, sy)
image plane := IntegerImage("plane",4,0, sx, sy)
col = icol
row = irow
plane = iplane
// to expand the shift as the same size with source image
image ones := IntegerImage("ones",4,0, sx, sy)
ones = 1
// create a random column shift of the source SI image
for(number i=0; i<sy; i++) {
col[i,0,i+1,sx] = col[i,0,i+1,sx]+shift.GetPixel(0,i)*ones[i,0,i+1,sx]
};
// drift corrected
image im := RealImage("test si", 4, sx+max_shift, sy, sz)
im=0
im[col, row, plane] = src[icol,irow,iplane]
im.ImageGetTagGroup().TagGroupCopyTagsFrom(src.ImageGetTagGroup())
im.ImageCopyCalibrationFrom(src)
im.SetName(src.GetName()+"-drift corrected")
im.showimage()
The image can be corrected, however the spectrum cannot be transferred to the corrected SI as shown :
I am just wondering what's wrong with my script.
Thank you in advance.
im[col, row, plane] = src[icol,irow,iplane]
The intrinsic variables icol, irow, iplane will be evaluated by the only fixed size image expression in the line. In your case col, row and plane (all of same size)
However, they are all 2D so what is internally happening is that you iterate over X & Y and then write the values:
im[ col(x,y), row(x,y), plane(x,y) ] = src[x,y,0] // iterated over all x/y
As Don I was mentioning in the comments, you would want to iterate over the z dimension.
Alternatively, you could make all of your images of size (sx,sy,sz) in your script.
This would work for the expression, but is horrifically inefficient.
In general, the best solution here is to no t use icol,irow,iplane at all, but make use of the Slice commands. see this answer:
I would possibly code a line-wise x-shift for an SI like below:
The script utilizes the fact that one can shift whole "blocks" (X x 1 x Z) in x-direction, iterating over y.
number sx = 256
number sy = 256
number sz = 100
image testSI := realImage("SI",4,sx,sy,sz)
testSI = sin(itheta/(idepth-iplane)*idepth) + (iplane % (icol+1))/idepth
testSI.ShowImage()
image xdrift := RealImage("XDrift per line",4,sy)
xdrift = trunc(random()*5 + 20*sin(icol/iwidth*3*PI()))
xdrift.ShowImage()
// Apply linewise Drift to SI, assuming xDrift holds this data
xDrift -= min(xDrift) // ensure only positive shifts
image outSI := realImage("SI shifted",4,sx+max(xDrift),sy,sz)
outSI.ShowImage()
for( number y=0; y<sy; y++ ){
number yShift = sum(xDrift[y,0])
outSI.slice2( yShift,y,0, 0,sx,1, 2,sz,1 ) = testSI.slice2(0,y,0,0,sx,1,2,sz,1)
}
The script below performs the iteration "plane by plane", but does not have a restriction on the plane-wise shift.
In fact, here each pixel gets an assigned XY shift.
Note that you can use warp(source, xexpr, yexpr ) instead of 2D addressing source[ xexpr, yexpr ] if you want to use bilinear interploation of values (and 0 truncation outside the valid range).
number sx = 256
number sy = 256
number sz = 100
image testSI := realImage("SI",4,sx,sy,sz)
testSI = sin(itheta/(idepth-iplane)*idepth) + (iplane % (icol+1))/idepth
testSI.ShowImage()
image xdrift := RealImage("pixelwise XDrift",4,sx,sy)
xdrift = irow%10*random() + 20*cos(irow/iheight*5*PI())
xdrift.ShowImage()
image ydrift := RealImage("pixelwise yDrift",4,sx,sy)
ydrift = 10*abs(cos(icol/iwidth* (irow/iheight) * 10 * PI())) + irow/iheight * 10
ydrift.ShowImage()
// Apply pixelwise Drift to SI
xDrift -= min(xDrift) // ensure only positive shifts
yDrift -= min(yDrift) // ensure only positive shifts
number mx = max(xDrift)
number my = max(yDrift)
image outSI := realImage("SI shifted",4,sx+mx,sy+my,sz)
outSI.ShowImage()
for( number z=0;z<sz;z++){
image outPlane := outSI.Slice2( 0,0,z, 0,sx+mx,1,1,sy+my,1)
image srcPlane := testSI.Slice2( 0,0,z, 0,sx,1,1,sy,1)
outPlane = srcPlane[ icol + xdrift[icol,irow] - mx, irow + ydrift[icol,irow] - my ]
// outPlane = srcPlane.warp( icol + xdrift[icol,irow] - mx, irow + ydrift[icol,irow] - my)
}

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!

What are these GPS data mean, and how to correctly convert them into decimal degrees?

I have a set of household GPS coordinates data, and the format in excel sheet looks like the following (edited for confidential reason):
ID GPSN GPSS GPSE GPSW
1 211234 -9 890123 -9
2 211255 -9 890155 -9
...
My questions are: what kind of GPS coordinates this is (looks like UTM data)? How do I accurately convert them into decimal degrees that only containing a longitude and a latitude (or X, Y data)? Do I need some kind of zoning information to do this correctly? Thanks
I doubt that a GPS receiver would put out UTM coordinates. It looks to me like latitude and longitude in degrees/minutes/seconds (DDMMSS). If so, then one way to do it is the following, in simple Python. The Convert Coordinate Notation tool in ArcGIS might be useful, but you'll have to reformat the data first, probably using Python.
import csv
import sys
# A function that takes 211234, treats it as 21°12'34",
# and returns 21.209444.
def convertToDegrees(DMS):
dms = DMS
dms = int(dms)
seconds = dms % 100
if 60 <= seconds:
print "More than 60 seconds! " + str(DMS) + " is not degrees/minutes/seconds!"
dms /= 100
minutes = dms % 100
if 60 <= minutes:
print "More than 60 minutes! " + str(DMS) + " is not degrees/minutes/seconds!"
dms -= minutes
degrees = dms / 100
degrees += (minutes / 60.0)
degrees += (seconds / (60.0 * 60.0))
if 180 < degrees or -180 > degrees:
print "In " + str(DMS) + ", degrees is outside [-180, 180]: " + str(degrees)
return degrees
# Input and output files from command line parameters
inFilename = sys.argv[1]
outFilename = sys.argv[2]
readFirstRow = False
with open(inFilename, "rb") as inFile:
reader = csv.reader(inFile)
with open(outFilename, "wb") as outFile:
writer = csv.writer(outFile)
# Loop through the rows
for row in reader:
if (not readFirstRow):
# Write the header row only once
writer.writerow(["ID", "latitude", "longitude"])
readFirstRow = True
else:
# Convert this row to latitude and longitude
latitude = 0
longitude = 0
if "-9" != row[1]:
latitude = convertToDegrees(row[1])
if "-9" != row[2]:
latitude = -1 * convertToDegrees(row[2])
if "-9" != row[3]:
longitude = convertToDegrees(row[3])
if "-9" != row[4]:
longitude = -1 * convertToDegrees(row[4])
writer.writerow([row[0], latitude, longitude])
To make sure you get it right, you'll want to confirm that the GPS was putting out latitude and longitude and find out which datum it used (probably WGS 1984).