查找图案图像(二进制文件)
Find a pattern image (binary file)
对于 DigitalMicrograph 中的字符串变量,我们可以使用 "find" 函数找到特定图案的位置:
Number find( String str, String sub_str )
我想做同样的事情,但使用图像数据。例如,我可以用
创建一个图像
image img := exprsize(1024, icol);
我想找到的模式是
image pattern := exprsize( 15, icol+64 );
在上面的例子中,我们知道模式的偏移量w.r.t。数据位于第 64 列。真实情况下我们不会有如此简单的模式(即直线)。使用 "for" 循环的粗暴方法肯定会起作用,但是当数据量越来越大时,它会变得非常慢。有人有 better/elegant 的建议吗?一维图像可能更容易,二维图像怎么样?
非常感谢!
鉴于您正在有效地寻找与数字数据的精确匹配,因此明智地使用图像表达式可能是获得解决方案的最有效途径。大致按照您的示例,我们首先设置源数据和目标模式:
Image sourceData := RealImage("Source data", 4, 4096);
sourceData = Random();
Image targetPattern := RealImage("Target pattern", 4, 15);
targetPattern = sourceData.Index(icol + 1733, 0);
然后我们准备一个精心安排的带有单个图像表达式的搜索缓冲区:
Number targetSize = targetPattern.ImageGetDimensionSize(0);
Number searchBufferW = sourceData.ImageGetDimensionSize(0) - targetSize;
Image searchBuffer := RealImage("Search buffer", 4, searchBufferW, targetSize);
searchBuffer = sourceData.Index(icol + irow, 0);
这会将源数据的所有可能匹配子集排列在二维图像的垂直列中。最后我们做一点图像数学来定位与目标模式的匹配,如果存在的话:
searchBuffer = Abs(searchBuffer - targetPattern.Index(irow, 0));
Image projectionVector := targetPattern.ImageClone();
projectionVector = 1.0;
Image searchResult := projectionVector.MatrixMultiply(searchBuffer);
Number posX, posY;
Number wasFound = (searchResult.Min(posX, posY) == 0);
String resultMsg = (wasFound) ? "Pattern found at " + posX : "Pattern not found";
OKDialog(resultMsg);
第一行将在与目标模式匹配的搜索缓冲区列的每个像素中产生一个精确的零。垂直求和搜索缓冲区并使用 Min() 函数查找零可以加快匹配项的搜索速度。
注意使用 MatrixMultiply() 进行快速垂直求和投影。这仅适用于类型 Real(4 字节浮点)源数据。但是,有一些稍微复杂的快速数据投影方法,它们也可以为任何数字数据类型提供相当快的结果。
虽然针对 1D 数据集中的 1D 模式进行了说明,但通过使用 multi-dimensioned 搜索缓冲区和使用 ImageDataSlice 的更高级索引,这种方法可能可以扩展到 2D 和 3D 数据集中的 1D 和 2D 模式对象,但这将是另一个问题的主题。
正如 Mike 指出的那样,cross-correlation 是在存在噪声的情况下搜索模式的好方法。然而,在没有噪音的情况下搜索甚至更好(如果不是 完美 方法)!这将适用于 1D 和 2D 脚本。见下文
number sx = 1024
number sy = 1024
number pw = 32
number ph = 32
number px = 100 // trunc( random()*(sx-pw) )
number py = 200 // trunc( random()*(sy-ph) )
image test := RealImage("Data",4,sx,sy)
test = random()
image pattern := test[py,px,py+ph,px+pw].ImageClone()
//test.showimage()
//pattern.showimage()
image patternSearch = test*0
patternSearch[0,0,ph,pw] = pattern
//patternSearch.ShowImage()
image corr := CrossCorrelate(test,patternSearch)
corr.ShowImage()
number mx,my,mv
mv = max(corr,mx,my)
mx -= trunc(sx/2) // because we've placed the pattern in the
my -= trunc(sy/2) // top/left of the search-mask
Result("\n Pattern = " + px + " / " + py )
Result("\n max = " + mv + " at " + mx + "/" + my )
image found = test*0
found[my,mx,my+ph,mx+pw]=pattern
rgbImage overlay = RGB((test-found)*256,found*256,0)
overlay.ShowImage()
如果您的问题只是一维问题并且您有非常大的数据,那么另一种方法可能会为您提供更快的解决方案。然后我会建议尝试使用 RAW-data 流(通过 TagGroup Streaming 命令)并使用您必须调整搜索的任何其他信息,即仅搜索流中模式的开头,然后仅验证"hit"等
此处添加的注释是为了解决与一维图像中的搜索模式有关的问题。如果我们 运行 多次执行以下脚本,那么我们会发现大约 50% 的时间无法正确找到模式。
number sx = 1024
number sy = 0
number pw = 16
number ph = 0
number px = trunc( random()*(sx-pw) )
number py = 0 // trunc( random()*(sy-ph) )
image test := RealImage("Data",4,sx );
test = random();
image patternSearch := exprsize( sx, icol<pw? test[icol+px, irow]: 0 );
// test.ShowImage();
// patternSearch.ShowImage();
patternSearch.SetName( "PatternSearch" );
//
image corr := CrossCorrelate(test,patternSearch)
// corr.ShowImage()
number mx,my,mv
mv = max(corr,mx,my)
mx -= trunc(sx/2) // because we've placed the pattern in the
my -= trunc(sy/2) // top/left of the search-mask
if( mx <= 0 ) mx += sx;
Result("\n\n Pattern = " + px + " / " + py )
Result("\n max = " + mv + " at " + mx + "/" + my )
根据要求,这里有一个片段显示了如何在 "raw" 数据流中进行搜索。我并不是说下面的脚本是最快或最优雅的解决方案,它只是展示了相应命令的工作原理。 (您可以在联机 F1 帮助的 "File Input and Output" 部分中找到它们。)
我放入的 'idea':只需在流中搜索搜索模式的最后一个值的出现。仅在找到时,查看给定距离处的 start-value 是否也匹配。仅在这种情况下,检查整个模式。对于 长 搜索模式,这应该是一种有用的方法,但对于非常短的搜索模式,它可能不是最佳选择。
{
number patternSize = 8
number dataSize = 24000
number patternPos = trunc( random() * ( dataSize - patternSize ) )
number const = 200
number dataTypeSizeByte = 4
number stream_byte_order = 0
// Prepare test-Dummies
image searchSet := IntegerImage( "search", dataTypeSizeByte, 0, patternSize )
searchSet = const * sin( icol/iwidth * Pi() )
// searchSet.ShowImage()
image dataSet := IntegerImage( "data", dataTypeSizeByte, 0, dataSize )
dataSet = const * random() * 0.3
dataSet.Slice1( patternPos, 0, 0, 0, patternSize, 1 ) = searchSet
// dataSet.ShowImage()
// Prepare Data as RawStream
object buffer = NewMemoryBuffer( dataSize * dataTypeSizeByte )
object stream = NewStreamFromBuffer(buffer)
dataSet.ImageWriteImageDataToStream( stream, stream_byte_order )
stream.StreamSetPos(0,0)
// Prepare aux. Tags for streaming
TagGroup tg = NewTagGroup();
tg.TagGroupSetTagAsUInt32( "UInt32_0", 0 )
// Prepare values to search for
number startValue = searchSet.GetPixel(0,0)
number lastValue = searchSet.GetPixel(patternSize-1,0)
// search for the pattern
// Search for the LAST value of the pattern only.
// If found, check if the FIRST value in appropriated distance also matches
// Only then compare whole pattern.
number value
number streamEndPos = stream.StreamGetSize()
number streamPos = (patternSize-1) * dataTypeSizeByte // we can skip the first few tests
stream.StreamSetPos(0, streamPos )
while( streamPos < streamEndPos )
{
tg.TagGroupReadTagDataFromStream( "UInt32_0", stream, stream_byte_order )
streamPos = stream.StreamGetPos()
tg.TagGroupGetTagAsUInt32( "UInt32_0", value ) // use appropriate data type!
if ( lastValue == value )
{
result("\n Pattern might end at: "+streamPos/dataTypeSizeByte)
// shift to start-value (relative) to check first value!
stream.StreamSetPos(1, -1 * patternSize * dataTypeSizeByte )
tg.TagGroupReadTagDataFromStream( "UInt32_0", stream, stream_byte_order )
tg.TagGroupGetTagAsUInt32( "UInt32_0", value )
if ( startValue == value )
{
result("\t (Start also fits!) " )
// Now check all of it!
stream.StreamSetPos(1, -1 * dataTypeSizeByte )
image compTemp := IntegerImage( "SectionData", dataTypeSizeByte, 0, patternSize )
compTemp.ImageReadImageDataFromStream( stream, stream_byte_order )
if ( 0 == sum( abs(compTemp - searchSet) ) )
{
number foundPos = (stream.StreamGetPos()/dataTypeSizeByte - patternSize)
Result("\n Correct starting position: " + patternPos )
Result("\n Found starting position : " + foundPos )
OKDialog( "Found subset at position : " + foundPos )
exit(0)
}
}
stream.StreamSetPos(0, streamPos )
}
}
OKDialog("Nothing found.")
}
对于 DigitalMicrograph 中的字符串变量,我们可以使用 "find" 函数找到特定图案的位置:
Number find( String str, String sub_str )
我想做同样的事情,但使用图像数据。例如,我可以用
创建一个图像image img := exprsize(1024, icol);
我想找到的模式是
image pattern := exprsize( 15, icol+64 );
在上面的例子中,我们知道模式的偏移量w.r.t。数据位于第 64 列。真实情况下我们不会有如此简单的模式(即直线)。使用 "for" 循环的粗暴方法肯定会起作用,但是当数据量越来越大时,它会变得非常慢。有人有 better/elegant 的建议吗?一维图像可能更容易,二维图像怎么样?
非常感谢!
鉴于您正在有效地寻找与数字数据的精确匹配,因此明智地使用图像表达式可能是获得解决方案的最有效途径。大致按照您的示例,我们首先设置源数据和目标模式:
Image sourceData := RealImage("Source data", 4, 4096);
sourceData = Random();
Image targetPattern := RealImage("Target pattern", 4, 15);
targetPattern = sourceData.Index(icol + 1733, 0);
然后我们准备一个精心安排的带有单个图像表达式的搜索缓冲区:
Number targetSize = targetPattern.ImageGetDimensionSize(0);
Number searchBufferW = sourceData.ImageGetDimensionSize(0) - targetSize;
Image searchBuffer := RealImage("Search buffer", 4, searchBufferW, targetSize);
searchBuffer = sourceData.Index(icol + irow, 0);
这会将源数据的所有可能匹配子集排列在二维图像的垂直列中。最后我们做一点图像数学来定位与目标模式的匹配,如果存在的话:
searchBuffer = Abs(searchBuffer - targetPattern.Index(irow, 0));
Image projectionVector := targetPattern.ImageClone();
projectionVector = 1.0;
Image searchResult := projectionVector.MatrixMultiply(searchBuffer);
Number posX, posY;
Number wasFound = (searchResult.Min(posX, posY) == 0);
String resultMsg = (wasFound) ? "Pattern found at " + posX : "Pattern not found";
OKDialog(resultMsg);
第一行将在与目标模式匹配的搜索缓冲区列的每个像素中产生一个精确的零。垂直求和搜索缓冲区并使用 Min() 函数查找零可以加快匹配项的搜索速度。
注意使用 MatrixMultiply() 进行快速垂直求和投影。这仅适用于类型 Real(4 字节浮点)源数据。但是,有一些稍微复杂的快速数据投影方法,它们也可以为任何数字数据类型提供相当快的结果。
虽然针对 1D 数据集中的 1D 模式进行了说明,但通过使用 multi-dimensioned 搜索缓冲区和使用 ImageDataSlice 的更高级索引,这种方法可能可以扩展到 2D 和 3D 数据集中的 1D 和 2D 模式对象,但这将是另一个问题的主题。
正如 Mike 指出的那样,cross-correlation 是在存在噪声的情况下搜索模式的好方法。然而,在没有噪音的情况下搜索甚至更好(如果不是 完美 方法)!这将适用于 1D 和 2D 脚本。见下文
number sx = 1024
number sy = 1024
number pw = 32
number ph = 32
number px = 100 // trunc( random()*(sx-pw) )
number py = 200 // trunc( random()*(sy-ph) )
image test := RealImage("Data",4,sx,sy)
test = random()
image pattern := test[py,px,py+ph,px+pw].ImageClone()
//test.showimage()
//pattern.showimage()
image patternSearch = test*0
patternSearch[0,0,ph,pw] = pattern
//patternSearch.ShowImage()
image corr := CrossCorrelate(test,patternSearch)
corr.ShowImage()
number mx,my,mv
mv = max(corr,mx,my)
mx -= trunc(sx/2) // because we've placed the pattern in the
my -= trunc(sy/2) // top/left of the search-mask
Result("\n Pattern = " + px + " / " + py )
Result("\n max = " + mv + " at " + mx + "/" + my )
image found = test*0
found[my,mx,my+ph,mx+pw]=pattern
rgbImage overlay = RGB((test-found)*256,found*256,0)
overlay.ShowImage()
如果您的问题只是一维问题并且您有非常大的数据,那么另一种方法可能会为您提供更快的解决方案。然后我会建议尝试使用 RAW-data 流(通过 TagGroup Streaming 命令)并使用您必须调整搜索的任何其他信息,即仅搜索流中模式的开头,然后仅验证"hit"等
此处添加的注释是为了解决与一维图像中的搜索模式有关的问题。如果我们 运行 多次执行以下脚本,那么我们会发现大约 50% 的时间无法正确找到模式。
number sx = 1024
number sy = 0
number pw = 16
number ph = 0
number px = trunc( random()*(sx-pw) )
number py = 0 // trunc( random()*(sy-ph) )
image test := RealImage("Data",4,sx );
test = random();
image patternSearch := exprsize( sx, icol<pw? test[icol+px, irow]: 0 );
// test.ShowImage();
// patternSearch.ShowImage();
patternSearch.SetName( "PatternSearch" );
//
image corr := CrossCorrelate(test,patternSearch)
// corr.ShowImage()
number mx,my,mv
mv = max(corr,mx,my)
mx -= trunc(sx/2) // because we've placed the pattern in the
my -= trunc(sy/2) // top/left of the search-mask
if( mx <= 0 ) mx += sx;
Result("\n\n Pattern = " + px + " / " + py )
Result("\n max = " + mv + " at " + mx + "/" + my )
根据要求,这里有一个片段显示了如何在 "raw" 数据流中进行搜索。我并不是说下面的脚本是最快或最优雅的解决方案,它只是展示了相应命令的工作原理。 (您可以在联机 F1 帮助的 "File Input and Output" 部分中找到它们。)
我放入的 'idea':只需在流中搜索搜索模式的最后一个值的出现。仅在找到时,查看给定距离处的 start-value 是否也匹配。仅在这种情况下,检查整个模式。对于 长 搜索模式,这应该是一种有用的方法,但对于非常短的搜索模式,它可能不是最佳选择。
{
number patternSize = 8
number dataSize = 24000
number patternPos = trunc( random() * ( dataSize - patternSize ) )
number const = 200
number dataTypeSizeByte = 4
number stream_byte_order = 0
// Prepare test-Dummies
image searchSet := IntegerImage( "search", dataTypeSizeByte, 0, patternSize )
searchSet = const * sin( icol/iwidth * Pi() )
// searchSet.ShowImage()
image dataSet := IntegerImage( "data", dataTypeSizeByte, 0, dataSize )
dataSet = const * random() * 0.3
dataSet.Slice1( patternPos, 0, 0, 0, patternSize, 1 ) = searchSet
// dataSet.ShowImage()
// Prepare Data as RawStream
object buffer = NewMemoryBuffer( dataSize * dataTypeSizeByte )
object stream = NewStreamFromBuffer(buffer)
dataSet.ImageWriteImageDataToStream( stream, stream_byte_order )
stream.StreamSetPos(0,0)
// Prepare aux. Tags for streaming
TagGroup tg = NewTagGroup();
tg.TagGroupSetTagAsUInt32( "UInt32_0", 0 )
// Prepare values to search for
number startValue = searchSet.GetPixel(0,0)
number lastValue = searchSet.GetPixel(patternSize-1,0)
// search for the pattern
// Search for the LAST value of the pattern only.
// If found, check if the FIRST value in appropriated distance also matches
// Only then compare whole pattern.
number value
number streamEndPos = stream.StreamGetSize()
number streamPos = (patternSize-1) * dataTypeSizeByte // we can skip the first few tests
stream.StreamSetPos(0, streamPos )
while( streamPos < streamEndPos )
{
tg.TagGroupReadTagDataFromStream( "UInt32_0", stream, stream_byte_order )
streamPos = stream.StreamGetPos()
tg.TagGroupGetTagAsUInt32( "UInt32_0", value ) // use appropriate data type!
if ( lastValue == value )
{
result("\n Pattern might end at: "+streamPos/dataTypeSizeByte)
// shift to start-value (relative) to check first value!
stream.StreamSetPos(1, -1 * patternSize * dataTypeSizeByte )
tg.TagGroupReadTagDataFromStream( "UInt32_0", stream, stream_byte_order )
tg.TagGroupGetTagAsUInt32( "UInt32_0", value )
if ( startValue == value )
{
result("\t (Start also fits!) " )
// Now check all of it!
stream.StreamSetPos(1, -1 * dataTypeSizeByte )
image compTemp := IntegerImage( "SectionData", dataTypeSizeByte, 0, patternSize )
compTemp.ImageReadImageDataFromStream( stream, stream_byte_order )
if ( 0 == sum( abs(compTemp - searchSet) ) )
{
number foundPos = (stream.StreamGetPos()/dataTypeSizeByte - patternSize)
Result("\n Correct starting position: " + patternPos )
Result("\n Found starting position : " + foundPos )
OKDialog( "Found subset at position : " + foundPos )
exit(0)
}
}
stream.StreamSetPos(0, streamPos )
}
}
OKDialog("Nothing found.")
}