How to take number from concrete row surrounded by text in TIFF file (if viewed in notepad) and use it in scripting in Digital Micrograph?

Viewed 106

Update#2 link to TIFF file (needed row is 135) https://drive.google.com/file/d/1g3e3xenm5b-awQwpvfZyhfXIqSVGGw2t/view

(Updated#1) Dear stackoverflow users,

first of all I need to mention that I am amateur in programming but I admire coding possibilities and try to exploit them in my work.

In our laboratory we acquire images using Zeiss microscopes, which save images in TIFF format with detailes attached as a text if viewed in notepad. Thus, when you put TIFF image in a Digital Micrograph it is not calibrated and you have to manualy calibrate each image.

On the other hand, in these TIFF files there is a row (its number is fixed but differs from microscope to microscope) with a pixel size looking like these:

"Pixel Size = 0.6 nm"

My thought was, that if I could extract number from this row and put it in script declaring dimensions for every open image - it would save a lot of time. I made a few steps:

Image img
img.GetFrontImage()
While(img.ImageIsValid())
 {Number pixelsize
pixelsize = 0.5
img.imagesetdimensionscale(0,pixelsize)
img.imagesetdimensionscale(1,pixelsize)
img.imagesetdimensionunitstring(0,"nm")
img.imagesetdimensionunitstring(1,"nm")
imagedisplay imgdisp=img.imagegetimagedisplay(0)
imgdisp.applydatabar(2)
img.SetName(img.GetName()+"*")
img := FindNextImage(img)
 }

As I understand:

  1. I used a script from "DM script book" for apllying action on all opened images
  2. I create variable number called pixelsize
  3. I set it manually from file
  4. I put it in dimensions scale in x and y
  5. I set dimensions units as "nm"
  6. I also create scalebar.

So Could you tell me please:

Is it possible to set pixelsize equal to a number in a row in a TIFF file?

Sincerely,

Shadowbane

1 Answers

Yes, it is doable but not trivial. I've found an old script of mine in which I was doing that. I'm no longer in the position to test it, so I'm not 100% certain it will work. Also, it is a bit too long for posting here, so I've also put it under this pastebin link.

// Some constants (See TIFF Format)
number kLittleEndianTIFF    = 0x4949
number kBigEndianTIFF       = 0x4D4D 
number kMagicIDNumberTIFF   = 42

number FALSE = 0
number TRUE = 1

Interface I_ImportTiffWithTags
{
    // Aux. method to simplify single value read from stream
    number ReadValueOfType( object self, string type ); 
    
    // Aux. method to strip multiple pre- and post- string occurances from a string
    string ClipText( object self, string input, string leadIn, string leadOut, number doMultiple );
    string ClipTextWhiteSpaces( object self, string input );

    // Aux. method to convert ASCII text of specific format into a TagStructure
    TagGroup CreateTagsFromString( object self, string input, string entrySep, string keyToValueSep, string GroupLeadIn, string GroupLeadOut );
    TagGroup CreateTagsFromString_FEI_Default( object self, string input );
    TagGroup CreateTagsFromString_ZEISS_Default( object self, string input );

    // Get all TIFF tags from file in path as tagGroup  
    TagGroup GetTIFFTagsAsTG( object self, string path, number doTextFields, number doSingleValues, number includedSkipped );   
    TagGroup GetTIFFTagsAsTG( object self, string path, number includedSkipped );   // doTextField = true, doSingleValues = true
    TagGroup GetTIFFTagsAsTG( object self, string path );                           // doTextField = true, doSingleValues = true, includedSkipped = false
    
    // Get all ASCII TIFF tags as single string
    String  GetTIFFAscii( object self, string path, number splitSeparateTags );
    String  GetTIFFAscii( object self, string path); // splitSeparateTags = true
    
    // Import Image and append TIFF tags as TagGroup "TIFF Tags" to image
    image OpenTIFFWithTags( object self, string path );
    
    // Import TIFF Image stored by FEI SEMs, import info from ASCII in image, calibrate if possible
    image OpenFEI_TIFF( object self, string path, number withTiffTags );
    image OpenFEI_TIFF( object self, string path );
    
    // Import TIFF Image stored by ZEISS SEMs, import info from ASCII in image, calibrate if possible
    image OpenZEISS_TIFF( object self, string path, number withTiffTags );
    image OpenZEISS_TIFF( object self, string path);
    
    // Aux. to find calibration from the tags, format specific
    number GetCalibrationFromTags_FEI( object self, tagGroup FEItgs, number &sx, number &ox, string &ux, number &sy, number &oy, string &uy );
    number GetCalibrationFromTags_ZEISS( object self, tagGroup ZEISStags, number &sx, number &ox, string &ux, number &sy, number &oy, string &uy );
    
}

Class CImportTIFFWithTags
{
    object fStream
    number byteOrder
    number verbose
    number kMaxTextShow 
    
    CImportTIFFWithTags( object self )
    {
        verbose = FALSE
        kMaxTextShow = 20
    }
    
    number ReadValueOfType( object self, string type )
    {
        if ( !fStream.ScriptObjectIsValid() ) Throw( "Invalid file stream." )
        number val = 0
        TagGroup tg = NewTagGroup()
        if ( type == "bool" )
        {
            tg.TagGroupSetTagAsBoolean( type, 0 )
            tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder ) 
            tg.TagGroupGetTagAsBoolean( type, val )
        }
        else if ( type == "uint8" )
        {
            string str = fStream.StreamReadAsText(0,1)
            val = asc(str)
        }
        else if ( type == "uint16" )
        {
            tg.TagGroupSetTagAsUInt16( type, 0 )
            tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder ) 
            tg.TagGroupGetTagAsUInt16( type, val )
        }
        else if ( type == "uint32" )
        {
            tg.TagGroupSetTagAsUInt32( type, 0 )
            tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder ) 
            tg.TagGroupGetTagAsUInt32( type, val )
        }
        else if ( type == "float" )
        {
            tg.TagGroupSetTagAsFloat( type, 0 )
            tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder ) 
            tg.TagGroupGetTagAsFloat( type, val )
        }
        else if ( type == "double" )
        {
            tg.TagGroupSetTagAsDouble( type, 0 )
            tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder ) 
            tg.TagGroupGetTagAsDouble( type, val )
        }
        else Throw("Invalid read-type:"+type)
        return val
    }
    
    
    string ClipText( object self, string input, string leadIn, string leadOut, number doMultiple )
    {
        string work = input
        if ( len(leadIn) <= len(work) )
        {
            while ( leadIn == left(work,len(leadIn)) )
            {
                work = right( work, len(work) - len(leadIn) )
                if ( !doMultiple ) break
                if ( len(work) < len(leadin) ) break
            }
        }
        if ( len(leadOut) <= len(work) )
        {
            while ( leadOut == right(work,len(leadout)) )
            {
                work = left( work, len(work) - len(leadOut) )
                if ( !doMultiple ) break
                if ( len(leadOut) <= len(work) ) break
            }
        }
        return work
    }
    
    string ClipTextWhiteSpaces( object self, string input )
    {
        return self.ClipText( input, " ", " ", TRUE );
    }
    
    TagGroup CreateTagsFromString( object self, string input, string entrySep, string keyToValueSep, string GroupLeadIn, string GroupLeadOut )
    {
        TagGroup tg = NewTagGroup()
        number bCheckGroup = ( len(GroupLeadIn) && len(GroupLeadOut) )  // true if groupLeads are specified
        string work = input
        string groupName = ""
        if ( "" == entrySep )
            entrySep = "\n"
        
        number pos = find( work, entrySep )
        number skipped = 0
        while( -1 != pos )
        {
            //OKDialog("work:"+work)
            //OKDialog("Entry:"+len(entrySep) )
            string entry = left( work, pos )
            work = right( work, len(work) - len(entry) - len(entrySep) + 1)
            number sep     = find( entry, keyToValueSep )
            
            if( -1 < sep )                                                  
            {
                // Entry matches format "KEY=VALUE" with '=' being the defined entrySep
                string key  = left( entry, sep )
                string value= right( entry, len(entry) - sep - len(keyToValueSep) )
                // Truncate trailing white spaces
                key   = self.ClipTextWhiteSpaces(key)
                value = self.ClipTextWhiteSpaces(value)
                
                string tagPath = groupName + ( "" == groupName ? "" : ":" ) + key
                tg.TagGroupSetTagAsString( tagPath, value )
            }
            else if ( bCheckGroup )
            {
                // Entry does not match format "KEY=VALUE", check if it is a groupname
                number leadIn  = find( entry, GroupLeadIn )
                number leadOut = find( entry, GroupLeadOut )
                if ( ( -1 < leadIn ) && ( -1 < leadOut ) && ( leadIn < leadOut ) ) // Is it a new group? "[GROUPNAME]"
                {
                    groupName = mid( entry, leadIn + len(GroupLeadIn), leadOut - leadIn - len(GroupLeadOut) )
                }   
                else
                    skipped++
            }
            else
            {
                skipped++   
            }
            // Find next entry
            pos = find( work, entrySep )        
        }
        if ( 0 != len(work) )
            skipped++
            
        if ( verbose )
        {
            if ( skipped )
                Result( "\nText To Tag skipped " + skipped + " lines.\n" )
        }
    
        return tg
    }
    
    TagGroup CreateTagsFromString_FEI_Default( object self, string input )
    {
        return self.CreateTagsFromString( input, "\n" , "=", "[", "]" )
    }
    
    TagGroup CreateTagsFromString_ZEISS_Default( object self, string input )
    {
        return self.CreateTagsFromString( input, "\n" , "=", "", "" )
    }
    
    
    TagGroup GetTIFFTagsAsTG( object self, string path, number doTextFields, number doSingleValues, number includedSkipped )
    {
        if ( !DoesFileExist( path ) ) Throw( "File not found.\n"+path)
        
        // Open Stream 
        number fileID = OpenFileForReading( path )
        fStream = NewStreamFromFileReference(fileID,1)
        
        // Find Byte Order
        number val
        byteOrder = 0
        val = self.ReadValueOfType( "uint16" )
        byteOrder = ( kLittleEndianTIFF == val  ) ? 2 : ( kBigEndianTIFF == val ? 1 : 0 )
        
        // Verify TIFF image
        val = self.ReadValueOfType( "uint16" )
        if ( val != kMagicIDNumberTIFF ) Throw( "Not a valid TIFF image" )
        
        // Find first directory start
        number offset = self.ReadValueOfType( "uint32" )
        
        // Browse all directories
        number nIFD = 0
        TagGroup AllTiffTagsTGlist = NewTagList()
        TagGroup TiffTGs
        while( 0 != offset )
        {
            // Set to start of directory and read it in
            nIFD++
            TiffTGs = NewTagGroup()
            if ( verbose )  Result( "Reading IFD #" + nIFD + " at \t" + offset )
                                
            fStream.StreamSetPos( 0, offset )   
            number nEntries = self.ReadValueOfType( "uint16" )
            for ( number e=0; e<nEntries; e++ )
            {
                number tag        = self.ReadValueOfType( "uint16" )
                string tagStr     = "ID("+tag+")"
                number typ        = self.ReadValueOfType( "uint16" )
                number count      = self.ReadValueOfType( "uint32" )
                number dataOffset = self.ReadValueOfType( "uint32" )
                if ( verbose )
                    Result( "\n DirEntry # "+Format(e,"%3.0f") + ": TiffIDTag: " + tag + "]ttyp: " + typ + "\tcount: " + count )

                // Read only, if it is either a single value or a string                                
                if ( ( doTextFields ) && ( 2 == typ )  )// ASCII
                {
                    number currentPos = fStream.StreamGetPos()
                    fStream.StreamSetPos( 0, dataOffset )
                    string textField = fStream.StreamReadAsText( 0, count )
                    fStream.StreamSetPos( 0, currentPos )
                    if ( verbose ) Result( "\t value: " + left( textField, min(len(textField), kMaxTextShow ) ) )
                    TiffTGs.TagGroupSetTagAsString( tagStr, textField )     
                }   
                else if ( ( doSingleValues ) && ( 1 == count )  ) // Single Value. 
                {
                    // Note that the 'dataOffset'-4-bytes are already the value for data fitting in 4-bytes!
                    // Otherwise, it specifies the staring offset for the value
                    
                    if ( 1 == typ )         // uInt8
                    {
                        TiffTGs.TagGroupSetTagAsShort( tagStr, dataOffset )
                        if ( verbose )  Result("\t value:" + dataOffset )
                    }
                    else if ( 3 == typ )    // uInt16
                    {
                        TiffTGs.TagGroupSetTagAsUInt16( tagStr, dataOffset )    
                        if ( verbose )  Result("\t value:" + dataOffset )
                    }
                    else if ( 4 == typ )    // uInt32
                    {
                        TiffTGs.TagGroupSetTagAsUInt32( tagStr, dataOffset )    
                        if ( verbose )  Result("\t value:" + dataOffset )
                    }
                    else if ( 5 == typ )    // uInt32 / uInt32 (rational)
                    {
                        number currentPos = fStream.StreamGetPos()
                        number val1 = self.ReadValueOfType( "uint32" )
                        number val2 = self.ReadValueOfType( "uint32" )
                        TiffTGs.TagGroupSetTagAsLongPoint( tagStr, val1, val2 )
                        fStream.StreamSetPos( 0, currentPos )
                        if ( verbose )  Result("\t value:" + val1 + "/" + val2 )
                    }
                    else if ( 11 == typ )   // float
                    {
                        TiffTGs.TagGroupSetTagAsFloat( tagStr, dataOffset )                 
                        if ( verbose )  Result("\t value:" + dataOffset )
                    }
                    else if ( 12 == typ )   // double
                    {
                        number currentPos = fStream.StreamGetPos()
                        number val = self.ReadValueOfType( "double" )
                        TiffTGs.TagGroupSetTagAsDouble( tagStr, val )
                        fStream.StreamSetPos( 0, currentPos )
                        if ( verbose )  Result("\t value:" + val )
                    }
                    else if ( includedSkipped )
                    {
                        if ( verbose )  Result("\t value: SKIPPED DATA TYPE" )
                        TiffTGs.TagGroupSetTagAsString( tagStr, "Not imported, TagType (TIFF "+typ+")" )
                    }               
                }
                else if ( includedSkipped )
                {
                    // Multiple value entries
                    if ( verbose )  Result("\t value: Multiple values. Arrays are not read." )
                    TiffTGs.TagGroupSetTagAsString( tagStr, "Not imported, TagType (TIFF "+typ+") "+count+" values" )
                }
            }
            // Read next directory offset. 
            // This is 0000 for the last directory according to spec
            offset = self.ReadValueOfType( "uint32" ) 
            AllTiffTagsTGlist.TagGroupAddTagGroupAtEnd(TiffTGs)
        }
        
        // Return list if entries or just one group if there is one directory only
        if ( 1 < nIFD ) 
            return AllTiffTagsTGlist
        else
            return TiffTGs
    }
    TagGroup GetTIFFTagsAsTG( object self, string path, number includedSkipped )
    {
        return self.GetTIFFTagsAsTG( path, TRUE, TRUE, includedSkipped )
    }
    TagGroup GetTIFFTagsAsTG( object self, string path )
    {
        return self.GetTIFFTagsAsTG( path, TRUE, TRUE, FALSE )
    }   
    
    String  GetTIFFAscii( object self, string path, number splitSeparateTags )
    {
        String allText
        TagGroup tgs = self.GetTIFFTagsAsTG(path, TRUE, FALSE, FALSE )
        number nTags = tgs.TagGroupCountTags()
        for ( number n=0; n<nTags; n++ )
        {
            string label = tgs.TagGroupGetTagLabel(n)
            if ( splitSeparateTags )
            {
                if ( 1<nTags ) 
                    allText += (n?"\n":"")+"TIFF Tag " + label + ":\n"
            }
            string text
            if ( tgs.TagGroupGetTagAsString( label, text ) ) 
                allText += text + "\n"          
        }
        
        return allText
    }
    
    String  GetTIFFAscii( object self, string path)
    {
        return self.GetTIFFAscii( path, true )
    }
    
    image OpenTIFFWithTags( object self, string path )
    {
        image Imported := OpenImage( path )
        TagGroup TIFFTags = self.GetTIFFTagsAsTG(path, TRUE, TRUE, TRUE)
        Imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup( "TIFF Tags", TIFFTags )
        return Imported
    }
    
    image OpenFEI_TIFF( object self, string path, number withTiffTags )
    {
        image Imported := OpenImage( path )
        if ( withTiffTags )
        {
            TagGroup TIFFTags = self.GetTIFFTagsAsTG(path, TRUE, TRUE, FALSE )
            Imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup( "TIFF Tags", TIFFTags )
        }
        string ASCIITags = self.GetTIFFAscii(path)
        TagGroup FEITags = self.CreateTagsFromString_FEI_Default( ASCIITags )
        Imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup( "Info", FEITags )
        number sx, sy, ox, oy
        string ux, uy
        if ( self.GetCalibrationFromTags_FEI(FEITags, sx, ox, ux, sy, oy, uy ) )
        {
            Imported.ImageSetDimensionCalibration(0,ox,sx,ux,0)
            Imported.ImageSetDimensionCalibration(1,oy,sy,uy,0)
        }
        else
        {
            if ( verbose )
                Result( "\n FEI calibration not found. Image remains uncalibrated." )
        }
        return Imported
    }
    
    number GetCalibrationFromTags_FEI( object self, tagGroup FEItags, number &sx, number &ox, string &ux, number &sy, number &oy, string &uy )
    {
        if ( !FEItags.TagGroupIsValid() ) return FALSE
        number foundSomething = FALSE
        ox=0
        oy=0
        sx=1
        sy=1
        ux=""
        uy=""
        number value
        if ( FEITags.TagGroupGetTagAsNumber( "Scan:PixelWidth", value ) )
        {
            foundSomething = TRUE
            // [value] is in [m], scale to nm or um
            if ( value < 1e-6 )
            {
                if ( value < 1e-9 )
                {
                    ux = "nm"
                    sx =  value * 1e9
                }
                else
                {
                    ux = "µm"
                    sx =  value * 1e6
                }
            }
        }
        if ( FEITags.TagGroupGetTagAsNumber( "Scan:PixelHeight", value ) )
        {
            foundSomething = TRUE
            // [value] is in [m], scale to nm or um
            if ( value < 1e-6 )
            {
                if ( value < 1e-9 )
                {
                    uy = "nm"
                    sy =  value * 1e9
                }
                else
                {
                    uy = "µm"
                    sy =  value * 1e6
                }
            }
        }
        return foundSomething 
    }
    
    
    image OpenFEI_TIFF( object self, string path )
    {
        return self.OpenFEI_TIFF( path, FALSE )
    }
    
    image OpenZEISS_TIFF( object self, string path, number withTiffTags )
    {
        image Imported := OpenImage( path )
        if ( withTiffTags )
        {
            TagGroup TIFFTags = self.GetTIFFTagsAsTG(path, TRUE, TRUE, FALSE )
            Imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup( "TIFF Tags", TIFFTags )
        }
        string ASCIITags = self.GetTIFFAscii(path)
        TagGroup ZEISSTags = self.CreateTagsFromString_ZEISS_Default( ASCIITags )
        Imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup( "Info", ZEISSTags  )
        number sx, sy, ox, oy
        string ux, uy
        if ( self.GetCalibrationFromTags_ZEISS(ZEISSTags, sx, ox, ux, sy, oy, uy ) )
        {
            // "scale" gives full FOV!
            if ( sx!= 1)
                Imported.ImageSetDimensionCalibration(0,ox,sx/Imported.ImageGetDimensionSize(0),ux,0)
            if ( sy!= 1)
                Imported.ImageSetDimensionCalibration(1,oy,sy/Imported.ImageGetDimensionSize(1),uy,0)
        }
        else
        {
            if ( verbose )
                Result( "\n ZEISS calibration not found. Image remains uncalibrated." )
        }
        return Imported
    }
    
    image OpenZEISS_TIFF( object self, string path )
    {
        return self.OpenZEISS_TIFF( path, FALSE )
    }
    
    number GetCalibrationFromTags_ZEISS( object self, tagGroup ZEISStags, number &sx, number &ox, string &ux, number &sy, number &oy, string &uy )
    {
        if ( !ZEISStags.TagGroupIsValid() ) return FALSE
        number foundSomething = FALSE
        ox=0
        oy=0
        sx=1
        sy=1
        ux=""
        uy=""
        string value
        if ( ZEISStags.TagGroupGetTagAsString( "Width", value ) )
        {
            number pos = find( value, " " ) // separate unit!
            if ( -1 < pos )
            {
                sx = val( left(value,pos) )     // FOV width not scale!!
                ux  = right( value, len(value)-pos-1 )
                foundSomething = TRUE
            }
        }
        if ( ZEISStags.TagGroupGetTagAsString( "Height", value ) )
        {
            number pos = find( value, " " ) // separate unit!
            if ( -1 < pos )
            {
                sy = val( left(value,pos) )     // FOV width not scale!!
                uy  = right( value, len(value)-pos-1 )
                foundSomething = TRUE
            }
        }
        return foundSomething   
    }
}

///////////////////
// CALL EXAMPLES //
///////////////////

number option = 1
string msg
msg += "IMPORT TIFF - EXAMPLE\n"
msg += "Select what you want to do:\n"
msg += "(1)\t Import and calibrate FEI TIFF.\n"
msg += "(2)\t Import and calibrate ZEISS TIFF.\n"
msg += "(3)\t Show text content of TIFF.\n"
msg += "(4)\t Show TIFF tags in browser.\n"

while ( 4<option || 1>option )
{
    if ( !GetNumber(msg,option,option)) exit(0)
}

string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TIFF file",path, path)) exit(0)

if ( 1 == option  )
    Alloc(CImportTIFFWithTags).OpenFEI_TIFF(path).ShowImage()
else if ( 2 == option  )
    Alloc(CImportTIFFWithTags).OpenZEISS_TIFF(path).ShowImage()
else if ( 3 == option )
    Result( "\n\n\n\n_____________________________\n" +Alloc(CImportTIFFWithTags).GetTIFFAscii(path) )
else if ( 4 == option )
    Alloc(CImportTIFFWithTags).GetTIFFTagsAsTG(path,true,true,true).TagGroupOpenBrowserWindow(path,0)
Related