Численная вычислительная среда Matlab (или, например, ее свободная альтернатива GNU Octave) предоставляет функцию conv2 для двумерной свертки заданной матрицы с ядром свертки. При написании кода на C++, основанного на бесплатной библиотеке обработки изображений OpenCV, я обнаружил, что OpenCV в настоящее время не предлагает эквивалентного метода.
Хотя существует метод filter2D(), который реализует двумерную корреляцию, и которые могут быть использованы для свернутые изображение с заданным ядром (листать, что ядро и перемещение точки привязки в правильное положение, как объяснено на соответствующая страница документации OpenCV), было бы неплохо иметь метод, предлагающий такие же параметры обработки границ, как Matlab («полная», «действительная» или «такая же» свертка), например для сравнения результатов того же алгоритма, реализованного как в Matlab, так и в C++ с использованием OpenCV.
Вот что я придумал:
enum ConvolutionType {
/* Return the full convolution, including border */
CONVOLUTION_FULL,
/* Return only the part that corresponds to the original image */
CONVOLUTION_SAME,
/* Return only the submatrix containing elements that were not influenced by the border
*/
CONVOLUTION_VALID
};
void conv2(const Mat &img, const Mat& kernel, ConvolutionType type, Mat& dest) {
Mat source = img;
if(CONVOLUTION_FULL == type) {
source = Mat();
const int additionalRows = kernel.rows-1, additionalCols = kernel.cols-1;
copyMakeBorder(img, source, (additionalRows+1)/2, additionalRows/2,
(additionalCols+1)/2, additionalCols/2, BORDER_CONSTANT, Scalar(0));
}
Point anchor(kernel.cols - kernel.cols/2 - 1, kernel.rows - kernel.rows/2 - 1);
int borderMode = BORDER_CONSTANT;
filter2D(source, dest, img.depth(), flip(kernel), anchor, 0, borderMode);
if(CONVOLUTION_VALID == type) {
dest = dest.colRange((kernel.cols-1)/2, dest.cols - kernel.cols/2)
.rowRange((kernel.rows-1)/2, dest.rows - kernel.rows/2);
}
}
В моих модульных тестов, эта реализация дала результаты, которые были практически идентичны с реализацией Matlab. Обратите внимание, что как OpenCV, так и Matlab выполняют свертку в пространстве Фурье, если ядро достаточно велико. Определение «большой» варьируется в обеих реализациях, но результаты все равно должны быть очень похожими даже для больших ядер.
Кроме того, производительность этого метода может быть проблемой для «полного» случая свертки, так как всю исходную матрицу нужно скопировать, чтобы добавить границу вокруг нее. Наконец, если вы получаете исключение в вызове filter2D(), и используете ядро только с одним столбцом, это может быть вызвано этой ошибкой. В этом случае установите для параметра borderMode значение, например. BORDER_REPLICATE, или используйте последнюю версию библиотеки из соединительной линии OpenCV.
minor edit: правильный вызов 'conv2 (A, B, 'same')' – Amro