FEI および Zeiss ツールからの SEM 画像を DigitalMicrograph で処理する必要があります。それらは tif として保存されます。DigitalMicrograph は 2D tif を読み取ることができますが、画像は X、Y 方向で校正されていないように見えます。キャリブレーション情報を転送するインポートプラグインはありますか? または、キャリブレーションがストリームから直接赤になる可能性があると想像できます。そのような数値が tif のストリームに格納されるオフセットについて明確な考えを持っている人はいますか? 私は tif の構成にあまり詳しくなく、いくつかのバリエーションが存在することを知っています。特に、FEI と Zeiss の tif は組織が異なっているようです。
3 に答える
FEI と ZEISS はどちらも、キャリブレーション情報を独自のカスタム ASCII テキスト タグに保存しているようです。TIFF フォーマット仕様 (PDF)に従って、 TIFFからすべての ASCII フィールドを抽出するスクリプトを簡単に作成できます。そこから、テキスト操作を実行してキャリブレーションを取得し、それらを画像に設定する必要があります。
以下のスクリプトは、次の情報を使用して、FEI と ZEISS の両方の画像に対してこれを行います。
FEI
サイズ情報は次の形式のテキストにあります。
[Scan]
PixelWidth=8.26823e-010
PixelHeight=8.26823e-010
これはピクセル サイズを [メートル] で指定します。ツァイス
サイズ情報は次の形式のテキストにあります:
AP_HEIGHT
Height = 343.0 nm
AP_WIDTH
Width = 457.3 nm
これは FOV サイズを指定します。
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()
編集 2021:別の質問への回答として投稿された完全な TIFF インポート用の関連スクリプトも作成しました。
FEI は、ツールセットごとに異なる TIF 形式を使用します (SEM と TEM で、どちらもバージョンに依存します)。TIA の場合も同様で、新しいバージョンでは広く使用されている SerReader スクリプトが機能しなくなります。リクエストがあれば、簡単な修正を投稿できます。
TEM 用の FEI TIFF は、すべてのメタデータを XML 形式の単一のタグに格納します。スケール用の別のタグもありますが、これも XML としてフォーマットされています。スケールを読み取るために、上記の BmyGuest のコードを微調整しました。他のメタデータは解析する必要があります。
// Auxilliary method for stream-reading of values
// BmyGuest's March 10, 2016 code modified to read FEI TEM TIF
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 scaletemp
number scalestart, scaleend
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:<X unit", scaletemp ) )
{
unitX = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend = scaletemp.find("</X>")
scaleX = val(scaletemp.mid(scalestart,scaleend-scalestart))*1e9
}
if ( imported.GetStringNote( "TIFF Tags:<Y unit", scaletemp ) )
{
unitY = "nm"
scalestart = scaletemp.find("\">") + 2
scaleend =scaletemp.find("</Y>")
scaleY = val(scaletemp.mid(scalestart,scaleend-scalestart))*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()
TIF または TIFF ( Tagged Image File Format ) は、追加情報を「タグ」としてファイルに保存できる一般的な形式です。(ただし、これは DM のタグと同じではありません。) タグに正確に何が (そしてどこに) 書き込まれるかは、ファイルを書き込むソフトウェア (この場合は FEI) によって異なります。物理的なキャリブレーションがこれらのタグに書き込まれていると思いますが、テストするファイルがありません。(あなたの質問にアップロードするかもしれませんか? )
DMがTIFFインポート時にすべてまたは一部のタグを独自のTagGroup構造に書き込むと信じていますが、確認していません。チェックしましたか?(つまり、DM を介して FEI から TIFF ファイルをインポートし、「画像表示 -> タグ」に移動すると、何が表示されますか? キャリブレーションに必要な情報がそこにあり、利用するための簡単なスクリプトを作成できる可能性があります。これは校正用です。
別の方法として、ほとんどの場合、テキスト エディターで TIFF ファイルを開いて、画像の大量のバイナリに加えて、ASCII テキストのタグを確認できます。これにより、この特定の TIFF ファイルのどこに保存されているかがわかります。
この知識があれば、RAW ストリーミング コマンドを使用して適切な情報を見つけ、キャリブレーションもコピーするインポート スクリプトを作成できます。