Отличия в реализации filter2D - PullRequest
0 голосов
/ 21 января 2019

Я пытался реализовать convolute2D (filter2D в OpenCV) и придумал следующий код.

Mat convolute2D(Mat image, double** kernel, int W){
    Mat filtered_image = image.clone();
    // find center position of kernel (half of kernel size)
    int kCenterX = W / 2;
    int kCenterY = W / 2;
    int xx = 0;
    int yy = 0;
    cout << endl << "Performing convolution .." << endl;
    cout << "Image Size : " << image.rows << ", " << image.cols <<endl;
    for (int i = 0; i < image.rows; ++i){
        for (int j = 0; j < image.cols; ++j){
            for(int x = 0; x < W; ++x){
                xx = W - 1 - x;
                for(int y = 0; y < W; ++y){
                    yy = W - 1 - y;
                    int ii = i + (x - kCenterX);
                    int jj = j + (y - kCenterY);
                    if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
                        filtered_image.at<uchar>(Point(j, i)) += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
                    }

                }
            }
        }
    }
    return filtered_image;
}

Предполагая, что у нас всегда есть квадратное ядро. Но мои результаты сильно отличались от filter2D. Это из-за возможного переполнения или проблема с моей реализацией?

Спасибо

Ответы [ 2 ]

0 голосов
/ 23 января 2019

И используйте cv :: saturate_cast для правильного назначения uchar, например:

filtered_image.at<uchar>(Point(j, i)) = cv::saturate_cast<uchar>(res);
0 голосов
/ 21 января 2019

С вашим кодом есть две проблемы:

  1. Вы не устанавливаете выходное изображение в ноль, прежде чем добавлять к нему значения.Следовательно, вы вычисляете «вход + отфильтрованный вход», а не просто «отфильтрованный вход».

  2. Предполагая, что kernel имеет довольно маленькие значения, «входной пиксель * значение ядра» будетскорее всего, получится небольшое число, которое округляется вниз при записи в uchar.Сложив каждое из этих значений для ядра, вы получите слишком низкий результат.

Я рекомендую вам сделать это:

double res = 0;
for(int x = 0; x < W; ++x){
   int xx = W - 1 - x;
   for(int y = 0; y < W; ++y){
      int yy = W - 1 - y;
      int ii = i + (x - kCenterX);
      int jj = j + (y - kCenterY);
      if( ii >= 0 && ii < image.rows && jj >= 0 && jj < image.cols) {
         res += image.at<uchar>(Point(jj, ii)) * kernel[xx][yy];
      }
   }
}
filtered_image.at<uchar>(Point(j, i)) = res;

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

Для гораздо более высоких скоростей учтите, что проверка на чтение за пределами считывания (if во внутреннем цикле)) значительно замедляет код и совершенно не нужен большинству пикселей (так как несколько пикселей находятся у края изображения).Вместо этого вы можете разбить свои циклы на [0,kCenterX], [kCenterX,image.rows-kCenterX] и [image.rows-kCenterX,image.rows].Средний цикл, который обычно является самым большим, не должен проверять наличие за пределами чтения.

...