如何将 TVIPS 相机的 tif 校准导入 DM
How to import tif calibration from TVIPS camera into DM
我目前正在使用带有软件 EM-menu 的 TVIPS 相机来获取 TEM 图像。当我使用 DigitalMicrograph (DM) 分析数据(TIF 文件)时,出现了一些问题,因为校准对 DM 不可用。我知道之前已经回答了类似的问题:. But the calibrations of the TIF files are stored in X Resolution and Y Resolution (Rational type, the values are identical), which is different from FEI and Zeiss. I tried to modify the code in the , but what I got is the offset of X Resolution and Y Resolution, instead of the real value. I am not familiar with how to assign the values of the specific offset (in this case, the offset is 82110 for X Resolution and 82118 for Y Resoltuion) in TIF files to DM. Below is the code I modified from the question noted. Any suggestion is much appreciated. Raw TIF file 提供帮助解决问题。
// Auxilliary method for stream-reading of values
// BmyGuest's March 10, 2016 code modified to read FEI TEM TIF
// Import and calibrate TVIPS Tiff images
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 ( 5 == typ ) // Rational
{
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)
*/
result(extractedtext)
//result(infoAsTags)
// infoAsTags is blank. ZZ
// 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 scaletemp
number scalestart, scaleend
string hStr
string wStr
if ( imported.GetNumberNote("TIFF Tags:XResolution", scaleX ) )
{
unitX = "nm"
scaleX = 1e7/scaleX
}
if ( imported.GetNumberNote("TIFF Tags:YResolution", scaleY ) )
{
unitY = "nm"
scaleY = 1e7/scaleY
}
/*
if ( imported.GetStringNote("TIFF Tags:<X unit", scaletemp ) )
{
unitX = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend = scaletemp.find("</X>")
scaleX = 1e7/val(scaletemp.mid(scalestart,scaleend-scalestart))
}
if ( imported.GetStringNote("TIFF Tags:<Y unit", scaletemp ) )
{
unitY = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend =scaletemp.find("</Y>")
scaleY = 1e7/val(scaletemp.mid(scalestart,scaleend-scalestart))
}
*/
/*
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)
}
result("\n" + scaleX + "\n")
result(unitX)
// imported.ImageSetDimensionUnitString(0,unitX)
}
ImportTIFFWithTags()
好的,根据查到的信息here,XResolution和YResolution标签的ID分别为282和283,分别。
使用上面的模板脚本,当 运行 在您的示例数据上查看信息文本输出时,可以得到:
entry # 0: ID[256] typ=4 count=1 offset @ 4096
entry # 1: ID[257] typ=4 count=1 offset @ 4096
entry # 2: ID[258] typ=3 count=1 offset @ 16
entry # 3: ID[259] typ=3 count=1 offset @ 1
entry # 4: ID[262] typ=3 count=1 offset @ 1
entry # 5: ID[273] typ=4 count=4096 offset @ 50970
entry # 6: ID[278] typ=4 count=1 offset @ 1
entry # 7: ID[279] typ=4 count=4096 offset @ 67354
entry # 8: ID[282] typ=5 count=1 offset @ 83738
entry # 9: ID[283] typ=5 count=1 offset @ 83746
entry # 10: ID[296] typ=3 count=1 offset @ 3
entry # 11: ID[339] typ=3 count=1 offset @ 1
entry # 12: ID[37706] typ=4 count=1 offset @ 83754
entry # 13: ID[37707] typ=1 count=1616 offset @ 49168
entry # 14: ID[37708] typ=7 count=6312 offset @ 83754
因此您可以看到,您的 TIFF 图像有 15 个目录条目,并且 ID 为 282 和 283 的标签确实存在并且类型为 5。哪个(再次使用来源 here)应该是键入 rational,就像您在修改后的脚本中评论的那样。该类型定义为两个长 (int32) 值。
至此,整体TIFF结构浏览成功,只需要适配读出标签部分即可。您已经针对类型 5 进行了过滤,但最好也针对 ID 值进行额外过滤。
然后你需要读出这些值。它们不再是 text,因此原始脚本使用了不正确的命令。本质上,而不是
if ( 2 == typ ) // ASCII
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
string textField = fStream.StreamReadAsText( 0, count )
txt+=textField
fStream.StreamSetPos( 0, currentPos )
}
你想做的事
if ( 5 == typ ) // Rational (2 int32 values)
{
number currentPos = fStream.StreamGetPos() // Remember Stream Pos
fStream.StreamSetPos( 0, dataOffset ) // Set Stream to offset value as specified
number n1,n2
if ( 282 == tag ) // XResolution
{
n1 = fStream.ReadValueOfType( "long", byteOrder ) // Read long
n2 = fStream.ReadValueOfType( "long", byteOrder ) // continue to read next long
txt += "XResoltion:" + n1 + " / " + n2
}
else if ( 283 == tag ) // YResolution
{
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
txt += "YResoltion:" + n1 + " / " + n2
}
fStream.StreamSetPos( 0, currentPos )
}
请注意,ReadValueOfType
实际上只是在脚本中定义的一个自制的方便命令。底层的 DM 脚本技术是创建一个特定类型的 TagGroup 对象,并将其用作 TagGroupReadTagDataFromStream
命令中的代理。
原始脚本没有 long 类型的值,因此您需要扩展
这个函数,即:
else if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
我猜你可能还想稍微重构一下整个脚本,因为你不需要读出这些值然后将它们转换成文本等。
因此,经过简化的调整脚本可能如下所示:
// 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 if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
else Throw("Invalid read-type:"+type)
return val
}
number ExtractRationalTagOfIDFromTiff( string path, number ID, number &n1, number &n2, number ShowTIFFInfo )
{
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 )
if ( ShowTIFFInfo )
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 )
number success = 0
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 )
if ( ShowTIFFInfo )
Result("\n entry # "+e+": ID["+tag+"]\ttyp="+typ+"\tcount="+count+"\t offset @ "+dataOffset)
if ( ( ID == tag ) && ( 5 == typ ) ) // Rational (2 long values)
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
success = 1
fStream.StreamSetPos( 0, currentPos )
if ( ShowTIFFInfo )
Result( " ==>" + n1 + " / " + n2 )
}
}
offset = fStream.ReadValueOfType( "uint32", byteOrder ) // this is 0000 for the last directory according to spec
}
return success
}
// Import and calibrate TVIPS Tiff images
void ImportCalibratedTVIPS_TIFF()
{
string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TVIPS TIFF file",path, path)) exit(0)
// Import data
image imported := OpenImage(path)
imported.ShowImage()
// Calibrate image, stored as XResolution and YResolution tags
number n1,n2
number scaleX = 0
number scaleY = 0
if ( ExtractRationalTagOfIDFromTiff( path, 282, n1, n2, 1 ) )
{
scaleX = n1/n2
Result("\n X Resolution:" + Format( scaleX, "%g" ))
}
else
{
Result("\n X Resolution: NOT FOUND")
}
if ( ExtractRationalTagOfIDFromTiff( path, 283, n1, n2, 0 ) )
{
scaleY = n1/n2
Result("\n Y Resolution:" + Format( scaleY , "%g" ))
}
else
{
Result("\n Y Resolution: NOT FOUND")
}
if ( 0 != scaleX )
imported.ImageSetDimensionScale( 1, scaleX )
if ( 0 != scaleY )
imported.ImageSetDimensionScale( 1, scaleY )
}
clearResults()
ImportCalibratedTVIPS_TIFF()
运行 你的图像数据上的脚本,我得到:
X Resolution:3.90786e+08
Y Resolution:3.90786e+08
我不知道校准应该使用的单位,但该值似乎有点高...(特别是对于 TEM 图像)?
但是,对于 X 和 Y,它与您指定的一样。
顺便说一句,提供的 TIFF 图像似乎包含以下元信息:
ID[256] = Image Size X
ID[257] = Image Size Y
ID[258] = BitsPerSample
ID[259] = Compression
ID[262] = PhotometricInterpretation
ID[273] = StripOffsets
ID[278] = RowsPerStrip
ID[279] = StripByteCounts
ID[282] = XResolution
ID[283] = YResolution
ID[296] = ResolutionUnit
ID[339] = SampleFormat
ID[37706] = ????
ID[37707] = ????
ID[37708] = ????
Reading out the resolution unit from your image gives:
Resolution Unit:24576
感谢 BmyGuest,下面的脚本现在可以运行了。
我检查了TIF文件中的X Resolution(Y Resolution)和DM中的Scale(nm)之间的关系:Scale = 1e7 / X Resolution。我相应地修改了BmyGuest的脚本并添加了单位"nm"。现在校准是完美的,与 ImageJ 的结果相匹配。
// Auxilliary method for stream-reading of values
// Modifed from BmyGuest's, 2019.11.17
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 if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
else Throw("Invalid read-type:"+type)
return val
}
number ExtractRationalTagOfIDFromTiff( string path, number ID, number &n1, number &n2, number ShowTIFFInfo )
{
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 )
if ( ShowTIFFInfo )
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 )
number success = 0
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 )
if ( ShowTIFFInfo )
Result("\n entry # "+e+": ID["+tag+"]\ttyp="+typ+"\tcount="+count+"\t offset @ "+dataOffset)
if ( ( ID == tag ) && ( 5 == typ ) ) // Rational (2 long values)
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
success = 1
fStream.StreamSetPos( 0, currentPos )
if ( ShowTIFFInfo )
Result( " ==>" + n1 + " / " + n2 )
}
}
offset = fStream.ReadValueOfType( "uint32", byteOrder ) // this is 0000 for the last directory according to spec
}
return success
}
// Import and calibrate TVIPS Tiff images
void ImportCalibratedTVIPS_TIFF()
{
string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TVIPS TIFF file",path, path)) exit(0)
// Import data
image imported := OpenImage(path)
imported.ShowImage()
// Calibrate image, stored as XResolution and YResolution tags
number n1,n2
number scaleX = 0
number scaleY = 0
number PixelWidth = 0
number PixelHeight = 0
if ( ExtractRationalTagOfIDFromTiff( path, 282, n1, n2, 1 ) )
{
scaleX = n1/n2
Pixelwidth =1e7/scaleX
Result("\n X Resolution:" + Format( scaleX, "%g" ))
Result("\n Pixel width:" + Format( PixelWidth, "%g" )+ " nm")
}
else
{
Result("\n X Resolution: NOT FOUND")
}
if ( ExtractRationalTagOfIDFromTiff( path, 283, n1, n2, 0 ) )
{
scaleY = n1/n2
PixelHeight =1e7/scaleY
Result("\n Y Resolution:" + Format( scaleY , "%g" ))
Result("\n Pixel height:" + Format( PixelWidth, "%g" )+ " nm")
}
else
{
Result("\n Y Resolution: NOT FOUND")
}
if ( 0 != scaleX )
imported.ImageSetDimensionScale( 0, PixelWidth )
imported.ImageSetDimensionUnitString( 0, "nm" )
if ( 0 != scaleY )
imported.ImageSetDimensionScale( 1, PixelHeight )
imported.ImageSetDimensionUnitString( 1, "nm" )
}
clearResults()
ImportCalibratedTVIPS_TIFF()
我目前正在使用带有软件 EM-menu 的 TVIPS 相机来获取 TEM 图像。当我使用 DigitalMicrograph (DM) 分析数据(TIF 文件)时,出现了一些问题,因为校准对 DM 不可用。我知道之前已经回答了类似的问题:
// Auxilliary method for stream-reading of values
// BmyGuest's March 10, 2016 code modified to read FEI TEM TIF
// Import and calibrate TVIPS Tiff images
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 ( 5 == typ ) // Rational
{
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)
*/
result(extractedtext)
//result(infoAsTags)
// infoAsTags is blank. ZZ
// 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 scaletemp
number scalestart, scaleend
string hStr
string wStr
if ( imported.GetNumberNote("TIFF Tags:XResolution", scaleX ) )
{
unitX = "nm"
scaleX = 1e7/scaleX
}
if ( imported.GetNumberNote("TIFF Tags:YResolution", scaleY ) )
{
unitY = "nm"
scaleY = 1e7/scaleY
}
/*
if ( imported.GetStringNote("TIFF Tags:<X unit", scaletemp ) )
{
unitX = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend = scaletemp.find("</X>")
scaleX = 1e7/val(scaletemp.mid(scalestart,scaleend-scalestart))
}
if ( imported.GetStringNote("TIFF Tags:<Y unit", scaletemp ) )
{
unitY = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend =scaletemp.find("</Y>")
scaleY = 1e7/val(scaletemp.mid(scalestart,scaleend-scalestart))
}
*/
/*
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)
}
result("\n" + scaleX + "\n")
result(unitX)
// imported.ImageSetDimensionUnitString(0,unitX)
}
ImportTIFFWithTags()
好的,根据查到的信息here,XResolution和YResolution标签的ID分别为282和283,分别。
使用上面的模板脚本,当 运行 在您的示例数据上查看信息文本输出时,可以得到:
entry # 0: ID[256] typ=4 count=1 offset @ 4096
entry # 1: ID[257] typ=4 count=1 offset @ 4096
entry # 2: ID[258] typ=3 count=1 offset @ 16
entry # 3: ID[259] typ=3 count=1 offset @ 1
entry # 4: ID[262] typ=3 count=1 offset @ 1
entry # 5: ID[273] typ=4 count=4096 offset @ 50970
entry # 6: ID[278] typ=4 count=1 offset @ 1
entry # 7: ID[279] typ=4 count=4096 offset @ 67354
entry # 8: ID[282] typ=5 count=1 offset @ 83738
entry # 9: ID[283] typ=5 count=1 offset @ 83746
entry # 10: ID[296] typ=3 count=1 offset @ 3
entry # 11: ID[339] typ=3 count=1 offset @ 1
entry # 12: ID[37706] typ=4 count=1 offset @ 83754
entry # 13: ID[37707] typ=1 count=1616 offset @ 49168
entry # 14: ID[37708] typ=7 count=6312 offset @ 83754
因此您可以看到,您的 TIFF 图像有 15 个目录条目,并且 ID 为 282 和 283 的标签确实存在并且类型为 5。哪个(再次使用来源 here)应该是键入 rational,就像您在修改后的脚本中评论的那样。该类型定义为两个长 (int32) 值。
至此,整体TIFF结构浏览成功,只需要适配读出标签部分即可。您已经针对类型 5 进行了过滤,但最好也针对 ID 值进行额外过滤。 然后你需要读出这些值。它们不再是 text,因此原始脚本使用了不正确的命令。本质上,而不是
if ( 2 == typ ) // ASCII
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
string textField = fStream.StreamReadAsText( 0, count )
txt+=textField
fStream.StreamSetPos( 0, currentPos )
}
你想做的事
if ( 5 == typ ) // Rational (2 int32 values)
{
number currentPos = fStream.StreamGetPos() // Remember Stream Pos
fStream.StreamSetPos( 0, dataOffset ) // Set Stream to offset value as specified
number n1,n2
if ( 282 == tag ) // XResolution
{
n1 = fStream.ReadValueOfType( "long", byteOrder ) // Read long
n2 = fStream.ReadValueOfType( "long", byteOrder ) // continue to read next long
txt += "XResoltion:" + n1 + " / " + n2
}
else if ( 283 == tag ) // YResolution
{
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
txt += "YResoltion:" + n1 + " / " + n2
}
fStream.StreamSetPos( 0, currentPos )
}
请注意,ReadValueOfType
实际上只是在脚本中定义的一个自制的方便命令。底层的 DM 脚本技术是创建一个特定类型的 TagGroup 对象,并将其用作 TagGroupReadTagDataFromStream
命令中的代理。
原始脚本没有 long 类型的值,因此您需要扩展
这个函数,即:
else if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
我猜你可能还想稍微重构一下整个脚本,因为你不需要读出这些值然后将它们转换成文本等。 因此,经过简化的调整脚本可能如下所示:
// 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 if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
else Throw("Invalid read-type:"+type)
return val
}
number ExtractRationalTagOfIDFromTiff( string path, number ID, number &n1, number &n2, number ShowTIFFInfo )
{
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 )
if ( ShowTIFFInfo )
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 )
number success = 0
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 )
if ( ShowTIFFInfo )
Result("\n entry # "+e+": ID["+tag+"]\ttyp="+typ+"\tcount="+count+"\t offset @ "+dataOffset)
if ( ( ID == tag ) && ( 5 == typ ) ) // Rational (2 long values)
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
success = 1
fStream.StreamSetPos( 0, currentPos )
if ( ShowTIFFInfo )
Result( " ==>" + n1 + " / " + n2 )
}
}
offset = fStream.ReadValueOfType( "uint32", byteOrder ) // this is 0000 for the last directory according to spec
}
return success
}
// Import and calibrate TVIPS Tiff images
void ImportCalibratedTVIPS_TIFF()
{
string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TVIPS TIFF file",path, path)) exit(0)
// Import data
image imported := OpenImage(path)
imported.ShowImage()
// Calibrate image, stored as XResolution and YResolution tags
number n1,n2
number scaleX = 0
number scaleY = 0
if ( ExtractRationalTagOfIDFromTiff( path, 282, n1, n2, 1 ) )
{
scaleX = n1/n2
Result("\n X Resolution:" + Format( scaleX, "%g" ))
}
else
{
Result("\n X Resolution: NOT FOUND")
}
if ( ExtractRationalTagOfIDFromTiff( path, 283, n1, n2, 0 ) )
{
scaleY = n1/n2
Result("\n Y Resolution:" + Format( scaleY , "%g" ))
}
else
{
Result("\n Y Resolution: NOT FOUND")
}
if ( 0 != scaleX )
imported.ImageSetDimensionScale( 1, scaleX )
if ( 0 != scaleY )
imported.ImageSetDimensionScale( 1, scaleY )
}
clearResults()
ImportCalibratedTVIPS_TIFF()
运行 你的图像数据上的脚本,我得到:
X Resolution:3.90786e+08 Y Resolution:3.90786e+08
我不知道校准应该使用的单位,但该值似乎有点高...(特别是对于 TEM 图像)? 但是,对于 X 和 Y,它与您指定的一样。
顺便说一句,提供的 TIFF 图像似乎包含以下元信息:
ID[256] = Image Size X ID[257] = Image Size Y ID[258] = BitsPerSample ID[259] = Compression ID[262] = PhotometricInterpretation ID[273] = StripOffsets ID[278] = RowsPerStrip ID[279] = StripByteCounts ID[282] = XResolution ID[283] = YResolution ID[296] = ResolutionUnit ID[339] = SampleFormat ID[37706] = ???? ID[37707] = ???? ID[37708] = ????
Reading out the resolution unit from your image gives:
Resolution Unit:24576
感谢 BmyGuest,下面的脚本现在可以运行了。
我检查了TIF文件中的X Resolution(Y Resolution)和DM中的Scale(nm)之间的关系:Scale = 1e7 / X Resolution。我相应地修改了BmyGuest的脚本并添加了单位"nm"。现在校准是完美的,与 ImageJ 的结果相匹配。
// Auxilliary method for stream-reading of values
// Modifed from BmyGuest's, 2019.11.17
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 if ( type == "long" )
{
tg.TagGroupSetTagAsLong( type, 0 )
tg.TagGroupReadTagDataFromStream( type, fstream, byteOrder )
tg.TagGroupGetTagAsLong( type, val )
}
else Throw("Invalid read-type:"+type)
return val
}
number ExtractRationalTagOfIDFromTiff( string path, number ID, number &n1, number &n2, number ShowTIFFInfo )
{
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 )
if ( ShowTIFFInfo )
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 )
number success = 0
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 )
if ( ShowTIFFInfo )
Result("\n entry # "+e+": ID["+tag+"]\ttyp="+typ+"\tcount="+count+"\t offset @ "+dataOffset)
if ( ( ID == tag ) && ( 5 == typ ) ) // Rational (2 long values)
{
number currentPos = fStream.StreamGetPos()
fStream.StreamSetPos( 0, dataOffset )
n1 = fStream.ReadValueOfType( "long", byteOrder )
n2 = fStream.ReadValueOfType( "long", byteOrder )
success = 1
fStream.StreamSetPos( 0, currentPos )
if ( ShowTIFFInfo )
Result( " ==>" + n1 + " / " + n2 )
}
}
offset = fStream.ReadValueOfType( "uint32", byteOrder ) // this is 0000 for the last directory according to spec
}
return success
}
// Import and calibrate TVIPS Tiff images
void ImportCalibratedTVIPS_TIFF()
{
string path = GetApplicationDirectory("open_save",0)
if (!OpenDialog(NULL,"Select TVIPS TIFF file",path, path)) exit(0)
// Import data
image imported := OpenImage(path)
imported.ShowImage()
// Calibrate image, stored as XResolution and YResolution tags
number n1,n2
number scaleX = 0
number scaleY = 0
number PixelWidth = 0
number PixelHeight = 0
if ( ExtractRationalTagOfIDFromTiff( path, 282, n1, n2, 1 ) )
{
scaleX = n1/n2
Pixelwidth =1e7/scaleX
Result("\n X Resolution:" + Format( scaleX, "%g" ))
Result("\n Pixel width:" + Format( PixelWidth, "%g" )+ " nm")
}
else
{
Result("\n X Resolution: NOT FOUND")
}
if ( ExtractRationalTagOfIDFromTiff( path, 283, n1, n2, 0 ) )
{
scaleY = n1/n2
PixelHeight =1e7/scaleY
Result("\n Y Resolution:" + Format( scaleY , "%g" ))
Result("\n Pixel height:" + Format( PixelWidth, "%g" )+ " nm")
}
else
{
Result("\n Y Resolution: NOT FOUND")
}
if ( 0 != scaleX )
imported.ImageSetDimensionScale( 0, PixelWidth )
imported.ImageSetDimensionUnitString( 0, "nm" )
if ( 0 != scaleY )
imported.ImageSetDimensionScale( 1, PixelHeight )
imported.ImageSetDimensionUnitString( 1, "nm" )
}
clearResults()
ImportCalibratedTVIPS_TIFF()