Алгоритмы

индекс
298,75

Интегральное представление изображений

Во многих задачах обработки изображений требуется рассчитывать яркость прямоугольного участка изображения. Например, в вейвлет-преобразованиях, фильтрах Хаара, SURF и многих других алгоритмах. Интегральное представление позволяет быстро рассчитывать суммарную яркость произвольного прямоугольника, причем время рассчета не зависит от площади прямоугольника.

Интегральное представление


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

image

Где I(i,j) — яркость пиксела исходного изображения.

Каждый элемент матрицы II[x,y] представляет собой сумму пикселов в прямоугольнике от (0,0) до (x,y). Рассчет матрицы занимает линейное время, пропорциональное числу пикселов в изображении.

Рассчет матрицы можно производить по рекуррентной формуле:

II(x,y) = I(x,y) — II(x-1,y-1) + II(x,y-1) + II(x-1,y)

(код на C# приведен ниже)

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

Пусть ABCD — интересующий нас прямоугольник:
image

Из рисунка понятно, что сумму внутри прямоугольника можно выразить через суммы и разности смежных прямоугольников по следующей формуле:

SumOfRect(ABCD) = II(A) + II(С) — II(B) — II(D)

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

Аппроксимация круга


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

Аппроксимируем круг фигурой, показанной на картинке:

image

Данное приближение является достаточно грубым, однако для многих практических целей вполне приемлемым.
Для вычисления суммы пикселов внутри фигуры, применим дискретную теорему Грина, и получим следующую формулу:

SumOfFigure(ABCDEFGHIJKL)=II(A)-II(B)+II(С)-II(D)+II(E)-II(F)+II(G)-II(H)+II(I)-II(J)+II(K)-II(L)

где
A=(X-r, Y-R) B=(X+r, Y-R)
C=(X+r, Y-r) D=(X+R, Y-r)
E=(X+R, Y+r) F=(X+r, Y+r)
G=(X+r, Y+R) H=(X-r, Y+R)
I=(X-r, Y+r) J=(X-R, Y+r)
K=(X-R, Y-r) L=(X-r, Y-r)
r=R/√2
R — радиус круга
(X,Y) — центр круга

Как видим, формула требует 12 обращений к интегральной матрице и 11 арифметических операций (не считая рассчета координат самих точек фигуры).

Реализация C#


Первый метод производит вычисление интегральной матрицы. Второй — рассчитывает суммарную яркость произвольного прямоугольника:

    //вычисление интегрального представления изображения
    public static int[,] IntegralImage(int[,] sourceImage)
    {
      int width = sourceImage.GetLength(0);
      int height = sourceImage.GetLength(1);

      int[,] result = new int[width, height];

      result[0, 0] = sourceImage[0, 0];
      for (int x = 1; x < width; x++)
        result[x, 0] = sourceImage[x, 0] + result[x - 1, 0];
      for (int y = 1; y < height; y++)
        result[0, y] = sourceImage[0, y] + result[0, y - 1];

      for (int y = 1; y < height; y++)
        for (int x = 1; x < width; x++)
          result[x, y] = sourceImage[x, y] + result[x - 1, y] + result[x, y - 1] - result[x - 1, y - 1];

      return result;
    }

    //рассчет суммы яркости пикселов в произвольном прямоугольнике
    public static int SumOfRectangle(int[,] integralImage, Rectangle rect)
    {
      int A = 0, B = 0, C = 0, D = 0;
      if (rect.Top > 0 || rect.Left > 0)
        if (rect.Top <= 0)
          D = integralImage[rect.Left - 1, rect.Bottom];
        else
        if (rect.Left <= 0)
          B = integralImage[rect.Right, rect.Top - 1];
        else
        {
          A = integralImage[rect.Left - 1, rect.Top - 1];
          B = integralImage[rect.Right, rect.Top - 1];
          D = integralImage[rect.Left - 1, rect.Bottom];
        }

      C = integralImage[rect.Right, rect.Bottom];
      return A + C - B - D;
    }


Ссылки:
Wiki. Summed area table
YouTube. Semi-discrete Calculus.
Применение в SURF (eng)
Модификация для прямоугольников, повернутых на 45 градусов (eng)
Дискретная теорема Грина (eng)
+47
29 августа 2010, 00:29
95

комментарии (29)

0
Nc_Soft #
Зачем обсчитывать 4 прямоугольника, нельзя 1 обсчитать сразу? Или я что-то не так понимаю?
+1
BigObfuscator #
Если вам нужно просто посчитать сумму внутри одного прямоугольника — то интегральное представление вам не нужно.

Другое дело, когда вам нужно считать множество таких прямоугольников, причем они частично накладываются друг на друга. А это бывает нужно практически для всех современных алгоритмов сжатия и преобразования (вейвлет, фурье и т.д.). Если считать эти прямоугольники «в лоб», вам понадобится перебрать гораздо больше пикселов, чем при вычислении интегральной матрицы.
0
sunnybear #
я думал, что вейвлеты не пересекаются — просто 16x16, скажем. Нет?
+1
BigObfuscator #
Вейвлет преобразование подразумевает умножение участка изображения на набор функций. При этом, для каждой функции пикселы изображения будут перебираться заново.
Небольшой пример для простейшего вейвлета Хаара:



Обратите внимание, что и для первого коэффициента Hx, и для второго Hy — мы перебрали все пикселы изображения. По сути, два раза сложили их всех с некоторыми коэффициентами.
Пример 2x2 конечно не очень показателен. Но представте себе, что фильтр имеет размер 128x128. Тогда для вычисления коэффициента свертки нужно будет сложить 16k пикселов. А потом еще раз сложить столько же пикселов, для рассчета второго коэффициента (вот здесь как раз они и пересекаются). А ведь это простейший фильтр. Реальные же фильтры для сжатия изображений — используют, например, 16 коэффициентов. Значит вам придется складывать все пикселы 16 раз.

В случае же интегрального представления и фильтр 2x2 и фильтр 128x128 будут вычислены за одинаковое время.
0
sunnybear #
под пересечением в данном случае, видимо, понимается наложение. Но неужели в интегральном представлении уже все эти коэффициенты рассчитаны? Там же сумма яркостей, а в вейвлете у нас фильтр (какая яркость с плюсом, а какая-то с минусом). Если вертикальные-горизонтальные фильтры, действительно, через смещение будут считаться, то как быть с диагональными?
1 -1
-1 1
0
BigObfuscator #
С диагональными вот так:
0
BigObfuscator #
под пересечением в данном случае, видимо, понимается наложение
В данном случае — да, наложение. А если мы будем рассматривать фильтры разных масштабов (2x2, 4x4 и т.д.) то они будут именно частично пересекаться.
+3
StrangeAttractor #
Ник автора вынес мне мозг, когда я попытался это себе представить :-)
+1
amarao #
Интегральное представление имеет интересную особенность. По интегральной матрице можно вычислить сумму пикселов произвольного прямоугольника. — что-то не дописали. Вероятнее всего, «можно вычислить за o(1)», потому что по любой матрице с пикселами можно вычислить яркость прямоугольника. Для тупого bitmap это будет o (x*y).
0
BigObfuscator #
Да, спасибо, подправил.
0
liq #
Интересно. Кстати алгоритм неплохо паралелиться за счет свойств суммы в формуле .

А почему C#? И вы действительно эти функции с нуля пишете для своих целей?
0
BigObfuscator #
Кстати алгоритм неплохо паралелиться за счет свойств суммы в формуле

Чесно говоря, не вижу как его распаралелить. Ведь каждое последующее значение зависит от трех предыдущих.
А почему C#? И вы действительно эти функции с нуля пишете для своих целей?

Для своих целей я использую OpenCV. А сишарп использую для проверки идей.
+1
liq #
Самый простой способ.
1) считаем scan для каждой строки.
2) транспонируем результат
3) считаем scan для каждой строки.
4) транспонируем обратно.

Паралелится за счет независимого подсчета каждой строки. + сам scan паралелится неплохо.
+1
Calvrack #
В теории да вариант хороший, нигде кстати его не встречал. А на практике для многоядерных процессоров чтобы работа кеша и шины не сожрали выигрыш на картинках разумного размера (порядка 2000х2000), извращаться придется страшно.
Сколько интересно можно выжать если SSE и параллелизацию на 4 ядра по максимуму использовать?
+1
liq #
Этот алгоритм я впервые услышал от Антона Обухова(сотрудник Nvidia). На прошлогоднем или позопрошлогоднем графиконе. В часности этот алгоритм хорошо работает на CUDA. На SSE врятли имеет смысл так извращаться, ИМХО достаточно будет просто паралельно складывать строки а потом пройтись по столбцам.
+1
Calvrack #
Я уже лет сто не оптимизировал для x86, и не знаю что там за сложности, на DSP всяких (типа OMAP 3536) все у нас упирается в шину, то-есть само сложение полностью скрыто прокачкой буфера из памяти на процессор и потом интегрального буфера обратно в память. Я так экстраполировал, по прикидкам — на x86 похоже будет что-то подобное. Другое конечно дело, если память очень быстрая или каналов к ней больше одного. Но надо проверять.
0
Calvrack #
OMAP 3525 сорри.
0
BigObfuscator #
Нужно подумать, спасибо за ссылки.
В целом, здесь операций потребуется больше, если не ошибаюсь. Но на многоядернике может и даст выигрыш.
+1
liq #
Угу. Больше. Но когда у вас 480 потоков(у GTX480 именно столько), у вас будут немного другие приоритеты.

А вообще как уже выше сказал Calvrack, серьезные проблемы возникают когда упираешься в работу с памятью.
0
BigObfuscator #
Добавил в статью аппроксимацию круга.
0
Bas1l #
Кажется, интегральное представление черно-белых изображений можно рассматривать с точки зрения теории вероятностей. Если изображению поставить в соответствие двумерную случайную величину, то интенсивность пикселя изображения можно рассматривать как плотность вероятности события в данной точке (правда, их нужно будет отнормировать на суммарную интенсивность пикселей), а распределение вероятностей как раз и будет интегральным представлением изображения. И весь аппарат теорвера можно переносить на изображения.
+1
BigObfuscator #
Да, у меня тоже возникала ассоциация с теорвером (тем более что у меня основная область деятельности — статистика). Точь в точь — функция распеределения двумерной дискретной случайной величины. С той лишь разницей, что там сумма всегда к 1 сходится, а здесь — нет.
Только вот что это может дать? Кроме внешнего сходства, это совершенно разные методы, для разных целей.
+3
lany #
Любопытно. Я как раз вчера про дерево Фенвика читал. Оно даёт логарифмическое время для вычисления суммы прямоугольной области (против константного у вас), но зато и логарифмическое время изменения произвольного пикселя (против линейного у вас).
0
mbrdancer #
II(x,y) = I(x,y) + II(x-1,y-1) — II(x,y-1) — II(x-1,y)
Не могу понять. У меня не сходится.
разве не так?:
II(x,y) = I(x,y) — II(x-1,y-1) + II(x,y-1) + II(x-1,y)
0
BigObfuscator #
Да нет, все правильно, давайте посчитаем:


Все сходится. Просто тут нужно аккуратнее с тем, какие точки считать границей прямоугольника.
0
mbrdancer #
да не, я не про вторую, а про первую — про вычисление интегральных значений по рекурсии.
0
mbrdancer #
Потому что:

(тут сиреневым обозначены ячейки, в которой хранится сумма по серым ячейкам — то есть, как раз интегральное значение. А темно-серым обозначена область, значения из которой учтены дважды)
Итого имеем:
II(x, y) + II(x-1, y-1) = II(x-1, y) + II(x, y-1) + I(x, y)
значит
II(x, y) = I(x, y) + II(x-1, y) + II(x, y-1) — II(x-1, y-1)

Это все если я правильно понял смысл интегрального значения.

В чем я не прав?
+1
BigObfuscator #
Да, вы правы, это у меня в рекуррентной формуле ошибка была. Исправил, спасибо. Причем в сишарпном коде все правильно считалось, а в статье знаки попутал.
Но на самом деле эта формула не имеет особой ценности, поскольку реальные алгоритмы считают интегральную матрицу более эффективно.
Например вот так:

S(x,y) = S(x, y-1)+I(x, y)
II(x,y) = II(x-1, y)+S(x, y)

Либо еще более продвинуто, как здесь

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