Изображение / “большая часть пиксельной оптимизации поиска” сходства?

Ситуация:

Скажем, я имею изображение, скажем, 512x512 пикселей и отображаю B, 5x5 или 7x7 пикселей. Оба изображения составляют 24 бита rgb, и B имеют альфа-маску на 1 бит (таким образом, каждый пиксель или абсолютно прозрачен или абсолютно тверд).

Я должен найти в рамках изображения пиксель, который (с соседями it) наиболее тесно напоминает изображение B, Орегон пиксель, который, вероятно, наиболее тесно напоминает изображение B.

Подобие вычисляется как "расстояние", которое является суммой "расстояний" между непрозрачными пикселями B и пикселями A, разделенными на число непрозрачных пикселей B. Вот демонстрационный код SDL для объяснения:

struct Pixel{
    unsigned char b, g, r, a;
};

void fillPixel(int x, int y, SDL_Surface* dst, SDL_Surface* src, int dstMaskX, int dstMaskY){
    Pixel& dstPix = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*x + dst->pitch*y));

    int xMin = x + texWidth - searchWidth;
    int xMax = xMin + searchWidth*2;
    int yMin = y + texHeight - searchHeight;
    int yMax = yMin + searchHeight*2;


    int numFilled = 0;
    for (int curY = yMin; curY < yMax; curY++)
        for (int curX = xMin; curX < xMax; curX++){
            Pixel& cur = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*(curX & texMaskX) + dst->pitch*(curY & texMaskY)));
            if (cur.a != 0)
                numFilled++;
        }

    if (numFilled == 0){
        int srcX = rand() % src->w;
        int srcY = rand() % src->h;
        dstPix = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*srcX + src->pitch*srcY));
        dstPix.a = 0xFF;
        return;
    }

    int storedSrcX = rand() % src->w;
    int storedSrcY = rand() % src->h;
    float lastDifference = 3.40282347e+37F;

    //unsigned char mask = 

    for (int srcY = searchHeight; srcY < (src->h - searchHeight); srcY++)
        for (int srcX = searchWidth; srcX < (src->w - searchWidth); srcX++){
            float curDifference = 0;
            int numPixels = 0;
            for (int tmpY = -searchHeight; tmpY < searchHeight; tmpY++)
                for(int tmpX = -searchWidth; tmpX < searchWidth; tmpX++){
                    Pixel& tmpSrc = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*(srcX+tmpX) + src->pitch*(srcY+tmpY)));
                    Pixel& tmpDst = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*((x + dst->w + tmpX) & dstMaskX) + dst->pitch*((y + dst->h + tmpY) & dstMaskY)));
                    if (tmpDst.a){
                        numPixels++;
                        int dr = tmpSrc.r - tmpDst.r;
                        int dg = tmpSrc.g - tmpDst.g;
                        int db = tmpSrc.g - tmpDst.g;
                        curDifference += dr*dr + dg*dg + db*db;
                    }
                }
            if (numPixels)
                curDifference /= (float)numPixels;
            if (curDifference < lastDifference){
                lastDifference = curDifference;
                storedSrcX = srcX;
                storedSrcY = srcY;
            }
        }

    dstPix = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*storedSrcX + src->pitch*storedSrcY));
    dstPix.a = 0xFF;
}

Эта вещь, как предполагается, используется для поколения структуры.

Теперь, вопрос:
Самый легкий способ сделать это - поиск грубой силы (который используется в стандартной программе в качестве примера). Но это медленно - даже использование ускорения GPU и двухъядерного CPU не сделает его намного быстрее. Похоже, что я не могу использовать измененный двоичный поиск из-за маски B. Так, как я могу найти желаемый пиксель быстрее?

Дополнительная информация:

  1. Позволяется использовать 2 ядра, ускорение GPU, CUDA, и 1.5.. 2 гигабайта RAM для задачи.
  2. Я предпочел бы избегать некоторой длинной фазы предварительной обработки, которая займет 30 минут для окончания.

Идеи?

8
задан SigTerm 26 May 2010 в 16:38
поделиться

6 ответов

Отвечая на свой вопрос.

Краткий ответ: Мне удалось удалить альфа-канал, поэтому я решил использовать пирамиды изображений (см. пирамида и пирамида Гаусса в сети). Это дало огромное улучшение скорости.

Длинный ответ:

Моей первоначальной целью был синтез текстур. Альфа использовалась для создания пикселей, которые еще не были заполнены, а B представляла часть уже сгенерированного изображения. (Т.е. A был образцом, а B - сгенерированным изображением)

После небольшого исследования я обнаружил, что либо нет быстрого способа выполнить поиск в N-мерном пространстве (например, область 3x3 пикселя в основном 24-компонентный вектор (исключая центральный пиксель), тогда как 7x7 будет 144-компонентным, поиск такой области будет 24-мерным или 144-мерным поиском). Что ж, есть способы (например, статья под названием « I-COLLIDE: интерактивная и точная система обнаружения столкновений для крупномасштабных сред ») использует 3 отсортированных массива (каждый отсортирован по разному измерению) для создания трехмерных search), но они, очевидно, будут лучше работать с числами с плавающей запятой и меньшим числом измерений.

Предложение использовать обнаружение движения было бесполезным, потому что (кажется) обнаружение движения предполагает, что пиксели представляют движущиеся объекты (в моем случае это не так), и, по крайней мере, некоторая оптимизация опирается на это.

В конце концов я нашел статью « Быстрый синтез текстур с использованием древовидного векторного квантования » (Ли-И Вэй, Марк Левой, Стэнфордский университет), в которой используется метод, основанный на алгоритме, аналогичном тот, который я использовал. Изображение для поиска подвергается субдискретизации несколько раз (аналогично генерации mip-карты), поиск выполняется сначала на самом низком уровне, а затем на следующем. Возможно, это не лучший способ выполнить фактический поиск изображений для другого приложения, но он идеально подходит для моих целей. Бумага относительно старая, но мне она подходит.

В той же статье упоминается несколько методов еще большего ускорения поиска. Один из них - «Древовидное векторное квантование (TSVQ)», хотя я не могу дать больше информации об этом (не проверял - текущий генератор текстур работает с приемлемой скоростью на моем оборудовании, поэтому я, вероятно, не буду смотреть в дальнейшие оптимизации).

1
ответ дан 6 December 2019 в 00:05
поделиться

Одно из возможных ускорений может заключаться в использовании бинарных операторов. Например, вы можете пройти через A XOR B для последующих, перекрывающихся областей A. Результирующая область, значения которой ближе всего к 0, будет частью A, которая наиболее близко напоминает B. при учете альфа-маски предполагается, что альфа-маска A - это все единицы, и включить ее в XOR - 32 бита на пиксель вместо 24.

0
ответ дан 6 December 2019 в 00:05
поделиться

Вы захотите взглянуть на оценку движения, которая используется при кодировании видео, чтобы найти местоположение блока в ранее кодированном изображении, который наиболее похож на блок, который нужно кодировать.

(ПРИМЕЧАНИЕ: у меня недостаточно репутации, чтобы разместить две ссылки, поэтому вам придется искать оценку движения в Википедии).

Некоторые простые алгоритмы сопоставления блоков можно найти здесь . Они работают, анализируя только подмножество точек в области поиска.

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

min_sad = INT_MAX  // minimum sum of absolute difference
min_point = {0, 0}

foreach (point p : all_search_points )
{         
    sad = 0
    for( y = 0; y < block_height; ++y )
        for( x = 0; x < block_width && sad < min_sad; ++x ):
            sad += abs( block_b[y,x] - block_a[p.y+y, p.x+x] )
        if( sad < min_sad )
            min_sad = sad
            min_point = p
}

Раннее завершение также полезно при изучении только подмножества точек поиска, хотя ускорение не так велико, как при полном поиске.

2
ответ дан 6 December 2019 в 00:05
поделиться

Я бы подумал о том, чтобы перенести ваш ранний вызов cur Difference во внутренний цикл, чтобы он мог закоротить задолго до завершения внутреннего цикла, если ошибка уже слишком велика. Ваша торговая если нужна для тяжелой математики. Кроме того, значение шкалы пикселей для ошибки может быть умножением, а не делением (незначительное на новых машинах).

Есть ли шанс одновременного чтения нескольких пикселей или параллельной их обработки?

Для многопоточности вы можете запускать потоки для каждой итерации внешнего цикла for (разбивая на то, сколько потоков вы хотите использовать), чтобы ваши процессоры были более эффективными. Единственной проблемой будет синхронизация максимальной ошибки, которую можно решить, сохранив ошибки во внешней таблице и сравнив в конце, чтобы предотвратить конкуренцию за память.

Кэширование ваших структур для избавления от символов -> может помочь, но обычно компилятор делает это за вас.

Просто некоторые мысли для начала. Все еще ищу ...

0
ответ дан 6 December 2019 в 00:05
поделиться

PDiff - это инструмент перцепционного различия изображений с открытым исходным кодом, который может быть полезен для вас.

0
ответ дан 6 December 2019 в 00:05
поделиться

Вы можете попытаться найти приблизительное решение: Patch Match

В данной работе представлены интерактивные инструменты редактирования изображений, использующие новый рандомизированный алгоритм для быстрого нахождения приблизительных совпадений ближайших соседей между участками изображения. Предыдущие исследования в области графики и зрения использовали такой поиск ближайших соседей для создания различных высокоуровневых инструментов редактирования цифровых изображений. Однако стоимость вычисления поля таких совпадений для всего изображения не позволяла добиться интерактивной производительности. Наш алгоритм обеспечивает значительное улучшение производительности по сравнению с предыдущим уровнем техники (20-100x), что позволяет использовать его в интерактивных инструментах редактирования.

1
ответ дан 6 December 2019 в 00:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: