2

Is it possible in DM-script to take an ImageDisplay containing multiple layers and separate those layers into different image windows? And conversely, is it possible to overlay separate displays to form one multi-layer display through script?

My goal is to take a LinePlotImageDisplay containing multiple LinePlot layers and separate them so I can perform background subtraction on each of them individually. I then want to perform signal extraction on each of them, and then combine those separate signal LinePlots into one image.

I've tried working around the need to separate the layers by re-ordering them. I know that manually you can right click a layer and select "Send to > Back" and this will result in background subtraction being applied to whatever layer is now at the back. I tried calling LinePlotImageDisplaySetSlice(), but that didn't affect the ordering as I was hoping.

BmyGuest
  • 6,331
  • 1
  • 21
  • 35
  • Welcome to StackOverflow and thanks for posting your questions here. When you find one of the posts to be a sufficient answer to your question, please use the "check" button under the voting buttons to "accept" an answer and mark this question as answered. – BmyGuest Jul 18 '23 at 21:34

2 Answers2

2

The key to extracting the image data of individual slices of a LinePlotImageDisplay object is illustrated in Example 7: Selecting and hiding slices of the following section of DigitalMicrograph's on-line help (accessed via the Help > Search... menu command, or F1 in GMS3): Scripting > Example Scripts > Data Display > Changing LinePlot ImageDisplays. The trick is to use the selection operator, { }, with the sliceID of the desired slice, applied to the Image reference of the multi-slice image of interest, as shown by the second line of the for loop over the slices in the example script:

image sliceImg := front{ sliceID }

where front is a reference to the front image, the target in the example script.

Once one has an Image reference for the desired slice, it can be cloned and shown in a new window, as follows:

image extractedSlice := sliceImg.ImageClone();
extractedSlice.ShowImage();

For combining spectral data into an overlayed LinePlotImageDisplay with multiple slices, see Example 1: Creating multi-slice LinePlots in the above cited section of DM's on-line help.

The above is true for GMS version 3.5.3. If you are using an older version and the above does not work, please provide details of the GMS version and any script code you have tried.

Mike Kundmann
  • 705
  • 5
  • 12
  • 1
    The LinePlot slices selection operator `{}` does not only accept the slideID object, but also an index `{1}` or the slice label `{"slice0"}`. Using the slice-label can be ambiguous though, as several slices can have the same label. In that case, and internal order decides which slices is selected. – BmyGuest Jul 13 '23 at 08:12
2

Mike's answer above is sufficient for your question and should do what you need. However, the topic of LineplotImageDisplays is a bit more complicated so that I feel it deserves a more extensive explanation and background info as well.

As with Mike's answer, it is value for GMS 3.5.3 and earlier GMS version might not give you the same results or accept the example scripts.


A LinePlotImageDisplay, as the name suggests, is only a view onto underlying data.` This data, however, can be various things. For example, it can be a 2D image array, where each row is considered one slice in the display. A LineScan Spectrum-Image dataset is such an example.

string kTagPath =  "Some meta data"
image image2D := realImage("2D Image",4,100,4)
image2D = 100 * cos(icol/iwidth*irow*Pi()) * (1+irow)
image2D.ImageGetTagGroup().TagGroupSetTagAsString(kTagPath,"meta data")
image2D.ShowImage()
imageDisplay lpid2D = image2D.ImageGetImageDisplay(0)
lpid2D.ImageDisplayChangeDisplayType("lineplot")
lpid2D.LinePlotImageDisplaySetLegendShown(1)

string dummyStr
number numSlices = lpid2D.LinePlotImageDisplayCountSlices()
for (number i=0; i<numSlices; i++)
{
    string sliceLabel = lpid2D.ImageDisplayGetSliceLabelByIndex(i)
    string imgName = lpid2D{i}.ImageGetName()
    number maxValue = max(lpid2D{i})
    Result("\n Slice index #"+i)
    Result("\n\t Image Name:"+imgName)
    Result("\n\t Slice Label:"+sliceLabel)
    Result("\n\t Slice Max  :"+maxValue)
    if (lpid2D{i}.ImageGetTagGroup().TagGroupGetTagAsString(kTagPath,dummyStr))
        Result("\n\t Meta Data tag:" + dummyStr)
    else
        Result("\n\t Meta Data tag: NOT FOUND")
}

Note, that while we can easily access the data by just using the slice index in brackets { }, all the image references point to the same underlying image, hence the same Meta Data (tags, calibrations etc.). If you were to change one (f.e. by quantifying the slice) you would change it for all. You can compare this behavior to accessing a "slice" of a 3D image using the slice1() command.


On the other hand, a LinePlotImageDisplay can also display different 1D images in the same display, each with their own MetaData and size. An example would be the "background and netto signal" slices added on performing background subtraction on slices.

string kTagPath =  "Some meta data"

imageDisplay lpidSeries 
number numData = 4
for(number i=0; i<numData; i++)
{
    image image1D := realImage("1D Image #"+i,4,100+10*i)   
    image1D = 100 * cos(icol/iwidth*i*Pi()) * (1+i)
    image1D.ImageGetTagGroup().TagGroupSetTagAsString(kTagPath,"meta data #" + i)
    if ( 0 == i )
    {
        image1D.ShowImage()
        lpidSeries = image1D.ImageGetImageDisplay(0)
        lpidSeries.ImageDisplayChangeDisplayType("lineplot")
        lpidSeries.LinePlotImageDisplaySetLegendShown(1)
    }
    else
    {
        lpidSeries.ImageDisplayAddImage(image1D,"Label #"+i)
    }
}
lpidSeries.LinePlotImageDisplaySetDisplayedChannels(-5,135)


string dummyStr
number numSlices = lpidSeries.LinePlotImageDisplayCountSlices()
for (number i=0; i<numSlices; i++)
{
    string sliceLabel = lpidSeries.ImageDisplayGetSliceLabelByIndex(i)
    string imgName = lpidSeries{i}.ImageGetName()
    number maxValue = max(lpidSeries{i})
    Result("\n Slice index #"+i)
    Result("\n\t Image Name:"+imgName)
    Result("\n\t Slice Label:"+sliceLabel)
    Result("\n\t Slice Max  :"+maxValue)
    if (lpidSeries{i}.ImageGetTagGroup().TagGroupGetTagAsString(kTagPath,dummyStr))
        Result("\n\t Meta Data tag:" + dummyStr)
    else
        Result("\n\t Meta Data tag: NOT FOUND")
}

Note the difference here. You access the Lineplot slices with the same syntax, but now they really point to separate images with separate Meta Data.

There is no "2D Data" image and, consequently, changing the display type back to a raster display will only show you the first 1D image as raster display.

This also means, that you can't 'recombine' slices like this and your likely goal of processing LineScan like Spectrum Images will need to be handled differently. Below is just one example - there are also be alternative ways of handling it.

string kTagPath =  "Some meta data"
image image2D := realImage("2D Image (lineScan)",4,100,4)
image2D = 100 * cos(icol/iwidth*irow*Pi()) * (1+irow)
image2D.ImageGetTagGroup().TagGroupSetTagAsString(kTagPath,"meta data")
image2D.ShowImage()

imageDisplay lpid2D = image2D.ImageGetImageDisplay(0)
lpid2D.ImageDisplayChangeDisplayType("lineplot")
lpid2D.LinePlotImageDisplaySetLegendShown(1)


image result2D := image2D.ImageClone()
result2D *= 0
result2D.ShowImage()

imageDisplay lpidResult2D = result2D.ImageGetImageDisplay(0)
lpidResult2D .ImageDisplayChangeDisplayType("lineplot")
lpidResult2D .LinePlotImageDisplaySetLegendShown(1)


string dummyStr
number numSlices = lpid2D.LinePlotImageDisplayCountSlices()
for (number i=0; i<numSlices; i++)
{
    // Create a copy, to get a separate set of meta Data
    image sliceImage := lpid2D{i}.ImageClone()  
    // Process your slice
    sliceImage += Random()*25
    sliceImage.ImageGetTagGroup().TagGroupSetTagAsString(kTagPath,"processed slice "+i)
    // Write the results back into a 2D image you've pre-created
    lpidResult2D{i} = sliceImage
}

string dummyStr
number numSlices = lpidResult2D.LinePlotImageDisplayCountSlices()
for (number i=0; i<numSlices; i++)
{
    string sliceLabel = lpidResult2D.ImageDisplayGetSliceLabelByIndex(i)
    string imgName = lpidResult2D{i}.ImageGetName()
    number maxValue = max(lpidResult2D{i})
    Result("\n Slice index #"+i)
    Result("\n\t Image Name:"+imgName)
    Result("\n\t Slice Label:"+sliceLabel)
    Result("\n\t Slice Max  :"+maxValue)
    if (lpidResult2D{0}.ImageGetTagGroup().TagGroupGetTagAsString(kTagPath,dummyStr))
        Result("\n\t Meta Data tag:" + dummyStr)
    else
        Result("\n\t Meta Data tag: NOT FOUND")
}
BmyGuest
  • 6,331
  • 1
  • 21
  • 35
  • This is an excellent clarification of the nature of overlayed LinePlotImageDisplay objects for different types of source Image objects. It is also helpful to know that the selection operator also works directly with slice indices! Thank you for providing further details in this very useful expanded answer. – Mike Kundmann Jul 12 '23 at 16:25