使用OpenCV的平均过滤器

Average Filter with OpenCV

本文关键字:过滤器 OpenCV 使用      更新时间:2023-10-16

我正在尝试实现一个具有 5x5 内核的平均过滤器,尽管 OpenCV 中有一个函数,但我需要在没有它的情况下做到这一点。有问题,我认为这是变量 uchar,但我尝试了 int、float 和 double,结果图像不正确。我使用填充为 7 的图像。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include "filter.h"
#include <iostream>
#include <fstream>
using namespace std;
using namespace cv;
cv::Mat filter::mean_filter(cv::Mat& image_in){
    int centro = 7;
    float total = 0.0;
    double window[25];
    double mean= 0.0;
    int final=0;
    int nlines, ncols;

    cv::Mat kernel = cv::Mat::ones(5, 5, CV_32S);
    nlines=image_in.size().height;
    ncols=image_in.size().width;
    cv::Mat image_out = cv::Mat::zeros(nlines,ncols,CV_32S);
    for (unsigned int j=centro; j<nlines - centro; j++){
        for (unsigned int z=centro; z<ncols - centro; z++){
            window[0]=image_in.at<uchar>(j-2,z-2);
            window[1]=image_in.at<uchar>(j-1,z-2);
            window[2]=image_in.at<uchar>(j  ,z-2);
            window[3]=image_in.at<uchar>(j+1,z-2);
            window[4]=image_in.at<uchar>(j+2,z-2);
            window[5]=image_in.at<uchar>(j-2,z-1);
            window[6]=image_in.at<uchar>(j-1,z-1);
            window[7]=image_in.at<uchar>(j  ,z-1);
            window[8]=image_in.at<uchar>(j+1,z-1);
            window[9]=image_in.at<uchar>(j+2,z-1);
            window[10]=image_in.at<uchar>(j-2,z);
            window[11]=image_in.at<uchar>(j-1,z);
            window[12]=image_in.at<uchar>(j  ,z);
            window[13]=image_in.at<uchar>(j+1,z);
            window[14]=image_in.at<uchar>(j+2,z);
            window[15]=image_in.at<uchar>(j-2,z+2);
            window[16]=image_in.at<uchar>(j-1,z+2);
            window[17]=image_in.at<uchar>(j  ,z+2);
            window[18]=image_in.at<uchar>(j+1,z+2);
            window[19]=image_in.at<uchar>(j+2,z+2);
            window[20]=image_in.at<uchar>(j-2,z+1);
            window[21]=image_in.at<uchar>(j-1,z+1);
            window[22]=image_in.at<uchar>(j  ,z+1);
            window[23]=image_in.at<uchar>(j+1,z+1);
            window[24]=image_in.at<uchar>(j+2,z+1);
            mean=0.0;
            final=0;
            for (unsigned int k=0; k<25; k++){      
                mean+=window[k];    
            }
            mean=mean/25;   
            final=round(mean);
            image_out.at<int>(j,z)=final;
        }
    }
return image_out;
}

我稍微更改了您的代码并有一个有效的解决方案。这是一种非常原始的方法,但它有效。

可能的改进可能是通过跟踪哪些像素离开内核区域以及哪些像素进入内核区域来重用一些已经累积的像素值。另一种改进的可能性是在图像上并行化循环。

cv::Mat mean_filter(cv::Mat& image_in, int kernel)
{
  // Make sure you get a grayscale image.
  assert(image_in.type() == CV_8UC1);
  // Make sure your kernel is an uneven number
  assert(kernel % 2 == 1);
  // Make sure your kernel is bigger than 1
  assert(kernel >= 1);
  // for padding calculate the border needed
  int padding = (kernel - 1) / 2;
  int mean = 0.0;
  int final = 0;
  int nlines, ncols;
  cv::Mat img_temp;
  nlines = image_in.size().height;
  ncols = image_in.size().width;
  // Make propper padding. Here it is done with 0. Padding describes the adding of a border to the image in order to avoid a cropping by applying a filter-mask.
  copyMakeBorder(image_in, img_temp, padding, padding, padding, padding, BORDER_CONSTANT, 0);
  // allocate the output image as grayscale as the input is grayscale as well
  cv::Mat image_out = cv::Mat::zeros(nlines, ncols, CV_8UC1);
  // loop over whole image
  for (unsigned int j = padding; j<nlines + padding; j++){
    for (unsigned int z = padding; z<ncols + padding; z++){
      mean = 0.0;
      // loop over kernel area
      for (int x = -padding; x <= padding; x++){
        for (int y = -padding; y <= padding; y++){
          // accumulate all pixel-values
          mean += img_temp.at<uchar>(j + x, z + y);
        }
      }
      mean = mean / (kernel * kernel);
      final = round(mean);
      // cast result to uchar and set pixel in output image
      image_out.at<uchar>(j - padding, z - padding) = (uchar)final;
    }
  }
  return image_out;
}