Найдите изображение шаблона (двоичный файл)

Для строковой переменной в DigitalMicrograph мы можем найти положение определенного паттерна с помощью функции «найти»:

Number find( String str, String sub_str )

Я хотел бы сделать то же самое, но с данными изображения. Например, я могу создать изображение с

image img := exprsize(1024, icol);

и образец, который я хочу найти,

image pattern := exprsize( 15, icol+64 );

В приведенном выше случае мы знаем смещение шаблона относительно. данные находятся в столбце номер 64. В реальном случае у нас не будет такого простого шаблона (т.е. прямой линии). Подход грубой силы с циклом for, безусловно, будет работать, но он становится болезненно медленным, когда размер данных становится больше. У кого-нибудь есть лучшее/элегантное предложение? 1D-изображение может быть проще, а как насчет 2D-изображения?

Большое спасибо!


person KEVIVI    schedule 17.01.2016    source источник
comment
Это интересный вопрос, но ответ зависит от того, что вы подразумеваете под «данными изображения». Вы имеете в виду данные реального изображения, которые включают шум? Если это так, то шаблон никогда не будет точно совпадать, и потребуется что-то устойчивое к шуму, например взаимная корреляция. Если вы просто имеете в виду какие-то числовые данные, которые хранятся в массиве изображений, то вы можете найти точное совпадение, преобразовав числовые данные в представление шестнадцатеричной строки, а затем используя функцию поиска. Пожалуйста, предоставьте более подробную информацию о вашей проблеме.   -  person Mike Kundmann    schedule 17.01.2016
comment
Я ищу точное совпадение, так что похоже, что шестнадцатеричная строка - это путь. Есть ли быстрый способ преобразовать двоичную строку в шестнадцатеричную (т.е. не преобразовывать по одному байту за раз), просто используя сценарии DM? На самом деле существует множество приложений для преобразования bin-hex.   -  person KEVIVI    schedule 17.01.2016


Ответы (3)


В соответствии с просьбой, вот фрагмент, показывающий, как можно выполнить поиск в «сыром» потоке данных. Я не утверждаю, что приведенный ниже скрипт является самым быстрым или элегантным решением, он просто показывает, как работают соответствующие команды. (Вы найдете их в разделе "Ввод и вывод файлов" интерактивной справки 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.")
}
person BmyGuest    schedule 18.01.2016
comment
Сценарий будет работать медленнее, если в данных есть много значений, соответствующих конечному значению поиска. При этом достаточно быстро. В зависимости от того, что именно вы пытаетесь найти, любой алгоритм поиска может быть настроен для большей скорости... Необработанная потоковая передача во всех случаях превзойдет простое извлечение типа GetPixel. Тем не менее, я все еще думаю, что метод CC должен работать в целом лучше. - person BmyGuest; 18.01.2016

Учитывая, что вы фактически ищете точное соответствие числовым данным, разумное использование графических выражений может быть наиболее эффективным путем к решению. Примерно следуя вашему примеру, начнем с настройки исходных данных и целевого шаблона:

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 байта с плавающей запятой). Однако существуют несколько более сложные подходы к быстрому проецированию данных, которые также дадут довольно быстрый результат для любого числового типа данных.

Хотя этот подход проиллюстрирован для одномерного шаблона в одномерном наборе данных, этот подход, вероятно, можно распространить на одномерные и двумерные шаблоны в двухмерных и трехмерных наборах данных с помощью многомерного поискового буфера и более сложной индексации с использованием объектов ImageDataSlice, но это было бы тема для другого вопроса.

person Mike Kundmann    schedule 17.01.2016
comment
Это элегантное решение для 1D данных. Единственная загвоздка в том, что буфер поиска станет очень большим, если данные для поиска имеют большой размер, а шаблон поиска имеет небольшой размер. Спасибо! - person KEVIVI; 18.01.2016

Как указал Майк, взаимная корреляция — хороший способ поиска закономерности в присутствии шума. Однако еще лучше (если не идеальный метод) искать при отсутствии шума! Это будет работать в 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()

Если ваша проблема связана только с 1D и у вас очень большие данные, альтернативный подход может дать вам более быстрое решение. Затем я бы предложил попробовать использовать потоковую передачу 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 )
person BmyGuest    schedule 18.01.2016
comment
Подход с кросс-корреляцией отлично работает с поиском 2D-шаблонов с одной защелкой. Нам нужно будет позаботиться о случае, когда значение mx -= trunc(sx/2) или my -= trunc(sy/2) может быть отрицательным, добавив две следующие строки: if( mx ‹= 0 ) mx + = секс; если (мой ‹= 0) мой += sy; С другой стороны, этот подход с кросс-корреляцией плохо работает с одномерным изображением. Иногда не удается найти шаблон, и я не уверен, почему. Я не уверен, как искать поток необработанных данных. Можете ли вы привести один простой пример? Большое спасибо! - person KEVIVI; 18.01.2016
comment
Можете ли вы опубликовать пример, когда CC для 1D не работает? Мне любопытно, почему это так - или что-то неожиданное с использованием CC для 1D-данных... И спасибо за подсказку со сдвигом ширины/2 или высоты/2. Не стесняйтесь редактировать мой пост напрямую :c) - person BmyGuest; 18.01.2016