We need to treat the SEM images from FEI and Zeiss tools in DigitalMicrograph. They are stored as tif. DigitalMicrograph can read 2D tif but images appear uncalibrated in X,Y directions. Is there any import plugIn that transfers the calibration information ? Alternatively, I can imagine that the calibration can be red directly from a stream. Has anyone a clear idea about the offset where such numbers are stored in a stream of tif? I am not very familiar with organization of tif and I know some variations exist. In particular, FEI and Zeiss tifs seems to be organized differently.
Both FEI and ZEISS seem to store the calibration info in their own, custom ASCII text tags. Following the TIFF Format specifications (PDF) one can easily write a script which extracts all ASCII fields from the TIFF. From there on, one has to perform text-manipulations to get to the calibrations and set them to the image.
The script below does this for both FEI and ZEISS images, using the following information:
FEI
The size information is in the text in Form:
[Scan]
PixelWidth=8.26823e-010
PixelHeight=8.26823e-010
This specifies the pixel size in [meter]ZEISS
The size information is in the text in Form:
AP_HEIGHT
Height = 343.0 nm
AP_WIDTH
Width = 457.3 nm
This specifies the FOV size.
Script on PasteBin.
// Auxilliary method for stream-reading of values
number ReadValueOfType(object fStream, string type, number byteOrder)
{
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 == "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 Throw("Invalid read-type:"+type)
return val
}
string ExtractTextFromTiff( string path )
{
string txt
if ( !DoesFileExist(path) ) Throw("File not found.\n"+path)
// Open Stream
number fileID = OpenFileForReading( path )
object fStream = NewStreamFromFileReference(fileID,1)
// Read data byte order. (1 = big Endian, 2= little Endian for Gatan)
number val
number byteOrder = 0
val = fStream.ReadValueOfType( "uint16", byteOrder )
byteOrder = ( 0x4949 == val ) ? 2 : ( 0x4D4D == val ? 1 : 0 )
//Result("\n TIFF endian:"+byteOrder)
// Verify TIFF image
val = fStream.ReadValueOfType( "uint16", byteOrder )
if ( val != 42 ) Throw( "Not a valid TIFF image" )
// Browse all directories
number offset = fStream.ReadValueOfType( "uint32", byteOrder )
while( 0 != offset )
{
fStream.StreamSetPos( 0, offset ) // Start of IFD
number nEntries = fStream.ReadValueOfType( "uint16", byteOrder )
for ( number e=0;e<nEntries;e++)
{
number tag = fStream.ReadValueOfType( "uint16", byteOrder )
number typ = fStream.ReadValueOfType( "uint16", byteOrder )
number count = fStream.ReadValueOfType( "uint32", byteOrder )
number dataOffset = fStream.ReadValueOfType( "uint32", byteOrder )
//Result("\n entry # "+e+": ID["+tag+"]\ttyp="+typ+"\tcount="+count+"\t offset @ "+dataOffset)
if ( 2 == typ ) // ASCII
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
string textField = fStream.StreamReadAsText( 0, count )
txt+=textField
fStream.StreamSetPos( 0, currentPos )
}
}
offset = fStream.ReadValueOfType( "uint32", byteOrder ) // this is 0000 for the last directory according to spec
}
return txt
}
String TruncWhiteSpaceBeforeAndAfter( string input )
{
string work = input
if ( len(work) == 0 ) return ""
while ( " " == left(work,1) )
{
work = right( work, len(work) - 1 )
if ( len(work) == 0 ) return ""
}
while ( " " == right(work,1) )
{
work = left( work, len(work) - 1 )
if ( len(work) == 0 ) return ""
}
return work
}
// INPUT: String with line-wise information
// OUTPUT: TagGroup
// Assumptions:
// - Groups are specified in a line in the format: [GroupName]
// - The string contains information line-wise in the format: KeyName=Vale
TagGroup CreateTagsFromString( string input )
{
TagGroup tg = NewTagGroup()
string work = input
string eoL = "\n"
string GroupLeadIn = "["
string GroupLeadOut = "]"
string keyToValueSep= "="
string groupName = ""
number pos = find(work,eoL )
while( -1 != pos )
{
string line = left(work,pos)
work = right(work,len(work)-pos-len(eoL))
number leadIn = find(line,GroupLeadIn)
number leadOut = find(line,GroupLeadOut)
number sep = find(line,keyToValueSep)
if ( ( -1 < leadIn ) && ( -1 < leadOut ) && ( leadIn < leadOut ) ) // Is it a new group? "[GROUPNAME]"
{
groupName = mid(line,leadIn+len(GroupLeadIn),leadOut-leadIn-len(GroupLeadOut))
groupName = TruncWhiteSpaceBeforeAndAfter(groupName)
}
else if( -1 < sep ) // Is it a value? "KEY=VALUE" ?
{
string key = left(line,sep)
string value= right(line,len(line)-sep-len(keyToValueSep))
key = TruncWhiteSpaceBeforeAndAfter(key)
value = TruncWhiteSpaceBeforeAndAfter(value)
string tagPath = groupName + ( "" == groupName ? "" : ":" ) + key
tg.TagGroupSetTagAsString( tagPath, value )
}
pos = find(work,eoL)
}
return tg
}
void ImportTIFFWithTags()
{
string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TIFF file",path, path)) exit(0)
string extractedText = ExtractTextFromTiff(path)
/*
if ( TwoButtonDialog("Show extracted text?","Yes","No") )
result(extractedtext)
*/
tagGroup infoAsTags = CreateTagsFromString(extractedText )
/*
if ( TwoButtonDialog("Output tagstructure?","Yes","No") )
infoAsTags.TagGroupOpenBrowserWindow(path,0)
*/
// Import data and add info-tags
image imported := OpenImage(path)
imported.ImageGetTagGroup().TagGroupSetTagAsTagGroup("TIFF Tags",infoAsTags)
imported.ShowImage()
// Calibrate image, if info is found
// It seems FEI stores this value as [m] in the tags PixelHeight and PixelWidth
// while ZEISS images contain the size of the FOV in the tags "Height" and "Width" as string including unit
number scaleX = 0
number scaleY = 0
string unitX
string UnitY
string hStr
string wStr
if ( imported.GetNumberNote( "TIFF Tags:Scan:PixelWidth", scaleX ) )
{
unitX = "nm"
scaleX *= 1e9
}
if ( imported.GetNumberNote( "TIFF Tags:Scan:PixelHeight", scaleY ) )
{
unitY = "nm"
scaleY *= 1e9
}
if ( imported.GetStringNote( "TIFF Tags:Width", wStr ) )
{
number pos = find( wStr, " " )
if ( -1 < pos )
{
scaleX = val( left(wStr,pos) )
scaleX /= imported.ImageGetDimensionSize(0)
unitX = right( wStr, len(wStr)-pos-1 )
}
}
if ( imported.GetStringNote( "TIFF Tags:Height", hStr ) )
{
number pos = find( hStr, " " )
if ( -1 < pos )
{
scaleY = val( left(hStr,pos) )
scaleY /= imported.ImageGetDimensionSize(1)
unitY = right( hStr, len(hStr)-pos-1 )
}
}
if ( 0 < scaleX )
{
imported.ImageSetDimensionScale(0,scaleX)
imported.ImageSetDimensionUnitString(0,unitX)
}
if ( 0 < scaleY )
{
imported.ImageSetDimensionScale(1,scaleY)
imported.ImageSetDimensionUnitString(1,unitY)
}
}
ImportTIFFWithTags()
Edit 2021: I've also written a related script for full TIFF import posted as answer to a separate question.