【问题标题】:Find a pattern image (binary file)查找图案图像(二进制文件)
【发布时间】:2016-04-22 09:53:33
【问题描述】:

对于 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”循环的粗暴强制方法肯定会起作用,但是当数据量变大时,它会变得非常缓慢。有人有更好/优雅的建议吗? 1D图像可能更容易,2D图像怎么样?

非常感谢!

【问题讨论】:

  • 这是一个有趣的问题,但答案取决于您所说的“图像数据”是什么意思。您是指包含噪声的真实图像数据吗?如果是这样,那么一个模式将永远不会完全匹配,并且需要像互相关这样的耐噪声的东西。如果您只是指碰巧存储在图像数组中的某种数字数据,那么您可以通过将数字数据转换为十六进制字符串表示,然后使用 Find 函数来定位精确匹配。请提供有关您的问题的更多详细信息。
  • 我正在寻找完全匹配的,所以看起来十六进制字符串是要走的路。是否有一种仅使用 DM 脚本将二进制转换为十六进制字符串的快速方法(即一次不转换一个字节)?确实有很多 bin-hex 转换应用程序。

标签: dm-script pattern-finding


【解决方案1】:

正如 Mike 所指出的,互相关是在存在噪声的情况下搜索模式的好方法。但是,在没有噪音的情况下进行搜索会更好(如果不是 完美 方法)!这将适用于 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 数据流(通过 TagGroup Streaming 命令)并使用您必须调整搜索的任何其他信息,即仅搜索流中模式的开头,然后仅验证“命中” "等。

此处添加了注释以解决有关 1D 图像中的搜索模式的问题。如果我们多次运行以下脚本,我们会发现大约有 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 )

【讨论】:

  • 互相关方法非常适用于一次捕获的 2D 模式搜索。我们需要通过添加以下两行来处理 mx -= trunc(sx/2) 或 my -= trunc(sy/2) 的值可能为负的情况: if( mx
  • 你能发布一个一维CC失败的例子吗?我很好奇为什么会出现这种情况 - 或者如果使用 CC 处理 1D 数据会出现意外情况……感谢有关宽度/2 或高度/2 移位的提示。随意直接编辑我的帖子:c)
【解决方案2】:

根据要求,这里是一个片段,展示了如何在“原始”数据流中进行搜索。我并不是说下面的脚本是最快或最优雅的解决方案,它只是展示了相应的命令是如何工作的。 (您可以在在线 F1 帮助的“文件输入和输出”部分找到它们。)

我提出的“想法”:只需在流中搜索您的搜索模式的最后一个值的出现。仅当找到时,查看给定距离处的起始值是否也匹配。只有在这种情况下,检查整个模式。这对于 搜索模式应该是一种有用的方法,但对于非常短的搜索模式可能不是那么理想。

{
    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.")
}

【讨论】:

  • 如果数据中有很多个值匹配“search-end-value”,脚本会变慢。虽然它相当快。根据您要定位的具体内容,任何搜索算法都可以调整以提高速度……原始流在所有情况下都将击败简单的“GetPixel”类型提取。但是,我仍然认为 CC 方法通常会更好。
【解决方案3】:

鉴于您正在有效地寻找与数字数据的精确匹配,那么明智地使用图像表达式可能是解决问题的最有效途径。大致按照您的示例,我们首先设置源数据和目标模式:

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);

这会将源数据的所有潜在匹配子集排列在 2D 图像的垂直列中。最后,我们做一些图像数学来定位与目标模式的匹配(如果存在):

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 模式进行了说明,但通过使用多维搜索缓冲区和使用 ImageDataSlice 对象的更高级索引,这种方法可能可以扩展到 2D 和 3D 数据集中的 1D 和 2D 模式,但是将是另一个问题的主题。

【讨论】:

  • 这是一维数据案例的优雅解决方案。唯一的问题是,如果要搜索的数据很大并且搜索模式很小,那么搜索缓冲区会变得非常大。谢谢!
猜你喜欢
  • 1970-01-01
  • 2016-07-14
  • 2018-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-22
  • 2012-12-15
  • 1970-01-01
相关资源
最近更新 更多