Свёртка (англ. convolution) — это операция, показывающая «схожесть» одной функции с отражённой и сдвинутой копией другой.
В случае работы с изображениями — свёртка — это операция вычисления нового значения заданного пикселя, при которой учитываются значения окружающих его соседних пикселей.
Главным элементом свёртки является т.н. ядро свёртки — это матрица (произвольного размера и отношения сторон; чаще всего используется квадратная матрица (по-умолчанию, размеры 3х3)).
[ ][ ][ ]
[ ][я][ ]
[ ][ ][ ]
У ядра свёртки есть важный параметр — якорь — это элемент матрицы (чаще всего — центр), который прикладывается к заданному пикселю изображения.
Работает свёртка очень просто:
При вычислении нового значения выбранного пикселя изображения, ядро свёртки прикладывается своим центром к этому пикселю. Соседние пиксели так же накрываются ядром.
Далее, вычисляется сумма произведений значений пикселей изображения на значения, накрывшего данный пиксель элемента ядра.
Полученная сумма и является новым значением выбранного пикселя.
Теперь, если применить свёртку к каждому пикселю изображения, то получится некий эффект, зависящий от выбранного ядра свертки.
Пример:
пусть у нас есть клеточное поле, которое соответствует пикселям исходного изображния:
src — исходное изображение
dst — изображение для сохранения результа
kernel — ядро свёртки (массив из одного канала из элементов типа float)
anchor — якорь ядра ( (-1,-1) говорит, что якорь в центре (для ядра нечётной размерности))
Т.о. накладывая ядро с разными коэффициентами можно получить различные эффекты.
Свёртка — очень полезная и распространённая операция, лежащая в основе различных фильтров (размытие, повышение резкости, нахождение краёв).
Но возникает вопрос, как должен вести себя алгоритм свёртки на краях изображения?
Если взять рассмотренный пример, то как нужно рассчитывать свёртку, если приложить якорь ядра к пикселю со значением 47?
Хорошим решением этой проблемы может быть создание изображение большего размера, чем исходное, у которого на краях будут заданное значение пикселей. Начинать обработку картинки нужно тогда не с 0-го, а с 1-го пикселя (и до n-1-го пискселя):
В OpenCV уже есть функция реализующая копирование изображения с последующим окружением границей с заданным значением — cvCopyMakeBorder()
CVAPI(void) cvCopyMakeBorder( const CvArr* src, CvArr* dst, CvPoint offset,
int bordertype, CvScalar value CV_DEFAULT(cvScalarAll(0)));
— копирует исходное изображение, окружая его границей
src — исходное изображение
dst — изображение для сохранения результа
offset — смещение (координаты левого верхнего угла целевого изображения)
bordertype — тип границы:
#define IPL_BORDER_CONSTANT 0 // все пиксели границы заливаются value
#define IPL_BORDER_REPLICATE 1 // крайние пиксели изображения используются для заливки
// другие типы границы пока не поддерживаются:
#define IPL_BORDER_REFLECT 2 // пока не поддерживается
#define IPL_BORDER_WRAP 3 // пока не поддерживается
Здраствуйте, у меня вопрос. Как построить градиент изображения? А если конкретнее, то я понял что надо сделать 2 операции свертки, после которых у нас получится 2 матрицы. Каким образом из них получить необходимую матрицу?
Интересный эффект получил на OpenCVSharp, когда сначала вместо float-массивов использовал double-массивы :) Многие варианты давали почти полностью чёрное изображение, а массив для сглаживания дал эффект хорошего осветления :)
Комментарии (2)
RSS свернуть / развернутьBoard
JohnJ
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.