Переменная размер блока сумма абсолютной разницы в C ++ - PullRequest
8 голосов
/ 12 ноября 2011

Я хотел бы выполнить вычисление блока переменного размера сумма абсолютной разности с использованием двумерного массива 16-битных целых чисел в программе на C ++ настолько эффективно, насколько это возможно.Меня интересует код соответствия блоков в реальном времени.Мне было интересно, есть ли какие-либо библиотеки программного обеспечения для этого?Код работает на Windows XP, и я застрял, используя Visual Studio 2010 для компиляции.Процессор представляет собой 2-ядерный AMD Athlon 64 x2 4850e.

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

У меня есть один меньший 2-D массивЯ назову template_grid, а один большой 2-D массив я назову image.Я хочу найти область на изображении, которая минимизирует сумму абсолютной разности между пикселями в шаблоне и пикселями в области на изображении.

Самый простой способ вычислить SAD в C ++, если быбыть следующим:

for(int shiftY = 0; shiftY < rangeY; shiftY++) {
    for(int shiftX = 0; shiftX < rangeX; shiftX++) {
        for(int x = 0; x < lenTemplateX; x++) {
            for(int y = 0; y < lenTemplateY; y++) {
                SAD[shiftY][shiftX]=abs(template_grid[x][y] - image[y + shiftY][x + shiftX]);
            }
        }
    }
}

Расчет SAD для конкретных размеров массивов был оптимизирован в библиотеке примитивов производительности Intel.Однако массивы, с которыми я работаю, не соответствуют размерам этих библиотек.

Существует два диапазона поиска, с которыми я работаю,

большой диапазон: rangeY = 45, rangeX =10

небольшой диапазон: диапазон Y = 4, диапазон X = 2

Существует только один размер шаблона: lenTemplateY = 61, lenTemplateX = 7

Ответы [ 3 ]

3 голосов
/ 26 августа 2016

Незначительная оптимизация:

for(int shiftY = 0; shiftY < rangeY; shiftY++) {
  for(int shiftX = 0; shiftX < rangeX; shiftX++) {
    // if you can assume SAD is already filled with 0-es, 
    // you don't need the next line
    SAD[shiftX][shiftY]=0;
    for(int tx = 0, imx=shiftX; x < lenTemplateX; tx++,imx++) {
      for(int ty = 0, imy=shiftY; y < lenTemplateY; ty++,imy++) {
        // two increments of imx/imy may be cheaper than 
        // two addition with offsets
        SAD[shiftY][shiftX]+=abs(template_grid[tx][ty] - image[imx][imy]);
      }
    }
  }
}

Развертывание цикла с использованием шаблонов C ++

Может быть, сумасшедшая идея для вашей конфигурации (меня беспокоит компилятор C ++), но она может работать. Я не даю никаких гарантий, но попробуйте.

Идея может работать, потому что ваши template_grid размеры и диапазоны постоянны - таким образом, они известны во время компиляции.
Кроме того, чтобы это работало, ваши image и template_grid должны быть организованы с одинаковым макетом (первый столбец или первый ряд) - способ, которым ваш "пример кода" изображен в вопросе, смешивает SAD x/y с template_grid y/x.
В дальнейшем я буду использовать организацию «по столбцу», так что SAD[ix] обозначает столбец ix th вашей матрицы SAD**. Код работает так же, как и «row first», за исключением того, что имя переменных не будет соответствовать значению ваших массивов значений.

Итак, начнем:

template <
  typename sad_type, typename val_type,
  size_t template_len
> struct sad1D_simple {
  void operator()(
    const val_type* img, const val_type* templ,
    sad_type& result
  ) {
    // template specialization recursion, with one less element to add
    sad1D_simple<sad_type, val_type, template_len-1> one_shorter;
    // call it incrementing the img and template offsets
    one_shorter(img+1, templ+1, result);
    // the add the contribution of the first diff we skipped over above
    result+=abs(*(img+template_len-1)-*(templ+template_len-1));
  }
};

// at len of 0, the result is zero. We need it to stop the
template <
  typename sad_type, typename val_type
>
struct sad1D_simple<sad_type, val_type, 0> {
  void operator()(
    const val_type* img, const val_type* templ,
    sad_type& result
  ) {
    result=0;
  }
};

Почему функтор struct - struct с оператором? C ++ не допускает частичную специализацию шаблонов функций .
Что делает sad1D_simple: развертывает цикл for, который вычисляет SAD двух входных массивов без какого-либо смещения, основываясь на том факте, что длина вашего массива template_grid является постоянной, известной во время компиляции. Это в том же духе, что и «вычисление факториала времени компиляции с использованием шаблонов C ++»

Как это помогает?
Пример использования в приведенном ниже коде:

typedef ulong SAD_t;
typedef int16_t pixel_val_t;

const size_t lenTemplateX = 7; // number of cols in the template_grid
const size_t lenTemplateY = 61;
const size_t rangeX=10, rangeY=45;

pixel_val_t **image, **template_grid;
SAD_t** SAD;
// assume those are initialized somehow


for(size_t tgrid_col=0; tgrid_col<lenTemplateX; tgrid_col++) {
  pixel_val_t* template_col=template_grid[tgrid_col];
  // the X axis - horizontal - is the column axis, right?
  for(size_t shiftX=0; shiftX < rangeX; shiftX++) {
    pixel_val_t* img_col=image[shiftX];
    for(size_t shiftY = 0; shiftY < rangeY; shiftY++) {
      // the Y axis - vertical - is the "offset in a column"=row, isn't it?
      pixel_val_t* img_col_offsetted=img_col+shiftY;

      // this functor is made by recursive specialization
      // there's no cycle inside it, it was unrolled into
      // lenTemplateY individual subtractions, abs-es and additions 
      sad1D_simple<SAD_t, pixel_val_t, lenTemplateY> calc;
      calc(img_col_offsetted, template_col, SAD[shiftX][shiftY]);
    }
  }
}

Мммм ... мы можем сделать лучше? Нет, это не будет развертывание по оси X, мы все еще хотим остаться в одномерной области, но ... ну, может быть, если мы создадим ранжированный sad1D и развернем еще один цикл на той же оси?
Это будет работать , если f rangeX также является постоянным.

template <
  typename sad_type, typename val_type,
  size_t range, size_t template_len
> struct sad1D_ranged {
  void operator()(
    const val_type* img, const val_type* templ,
    // result is assumed to have at least `range` slots
    sad_type* result
  ) {
    // we'll compute here the first slot of the result
    sad1D_simple<sad_type, val_type, template_len>
      calculator_for_first_sad;
    calculator_for_first_sad(img, templ, *(result));

    // now, ask for a recursive specialization for 
    // the next (range-1) sad-s
    sad1D_ranged<sad_type, val_type, range-1, template_len>
       one_less_in_range;
    // when calling, pass the shifted img and result
    one_less_in_range(img+1, templ, result+1);
  }
};

// for a range of 0, there's nothing to do, but we need it
// to stop the template specialization recursion
template <
  typename sad_type, typename val_type,
  size_t template_len
> struct sad1D_ranged<sad_type, val_type, 0, template_len> {
  void operator()(
    const val_type* img, const val_type* templ,
    // result is assumed to have at least `range` slots
    sad_type* result
  ) {
  }
};

А вот как вы его используете:

for(size_t tgrid_col=0; tgrid_col<lenTemplateX; tgrid_col++) {
  pixel_val_t* template_col=template_grid[tgrid_col];
  for(size_t shiftX=0; shiftX < rangeX; shiftX++) {
    pixel_val_t* img_col=image[shiftX];
    SAD_t* sad_col=SAD[shiftX];

    sad1D_ranged<SAD_t, pixel_val_t, rangeY, lenTemplateY> calc;
    calc(img_col, template_col, sad_col);
  }
}

Да ... но вопрос: это улучшит производительность ?
Черт возьми, если я знаю. Для небольшого числа циклов в цикле и для сильной локальности данных (значения близки друг к другу, так что они находятся в кэшах ЦП), развертывание цикла должно улучшить производительность. При большем числе циклов вы можете отрицательно повлиять на предсказание ветвления ЦП и другие "бесполезные", "я знаю, может повлиять на производительность, но я не знаю, как".

Чувство мужества: даже если одна и та же техника развертывания может работать для двух других циклов, ее использование может привести к снижению производительности: нам нужно перейти с одного смежного вектора (столбец image) на другой - все изображение может не помещаться в кэш процессора.

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

0 голосов
/ 25 августа 2016

Я не уверен, насколько вы ограничены использованием SAD или вообще заинтересованы в том, чтобы найти область на изображении, которая лучше всего соответствует шаблону. В последнем случае вы можете использовать свертку вместо SAD. Это можно решить в области Фурье в O (N log N), включая преобразование Фурье (FFT).

Короче говоря, вы можете использовать БПФ (например, используя http://www.fftw.org/), чтобы преобразовать шаблон и изображение в частотную область, затем умножить их и преобразовать обратно во временную область.

Конечно, все это не имеет значения, если вы обязаны использовать SAD.

0 голосов
/ 23 августа 2016

вы можете попробовать сопоставить шаблон OpenCV с параметром квадратной разницы, см. Учебник здесь . OpenCV оптимизирован с OpenCL, но я не знаю, для этой конкретной функции. Я думаю, тебе стоит попробовать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...