循序渐进之(六)空间域图像增强之平滑空间滤波
冈萨雷斯第二版《数字图像处理》,平滑空间滤波是3.6章节的内容。
下面图片来自于图像平滑处理,6种滤波总结的综合示例,具体相关原理也可通过本博客学习。
1.均值滤波(方框滤波),只是参数不同这里放在一起。。
滤波问题,还有个边界处理的小问题,这一块可以看一下:图像处理之其他杂项(六)之Mat格式图像处理相关小事(像素越界,边界处理),,在现阶段opencv+VS中好像不存在这个问题(后续更正:好吧,这句话不正确,有些情况不存在越界问题,自动填充数值,有些情况越界报错,具体情况具体分析),而且如下面代码中那样,如果将边界问题考虑到,会造成掩模扩大后图像一定边界范围内的像素处理不到的情况。
#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
void meanFilterS(Mat &src, int n);
void meanFilterSB(Mat &src, int n);
int main()
{
Mat originImage=imread("E://匹配.jpg",0);
Mat src1=originImage.clone(),src2 = originImage.clone();
meanFilterSB(src1, 25);
meanFilterS(src2, 25);
imshow("原图", originImage);
imshow("有边界滤波",src1);
imshow("无边界滤波", src2);
waitKey(0);
return 0;
}
void meanFilterSB(Mat &src, int n)//考虑边界情况
{
for (int i = (n - 1) / 2; i < src.rows - (n - 1) / 2; i++)
{
for (int j = (n - 1) / 2; j < src.cols - (n - 1) / 2; j++)
{
int total = 0;
int x;
int y;
for (x = i - ((n - 1) / 2); x < i + ((n - 1) / 2); x++)
for (y = j - ((n - 1) / 2); y < j+ ((n - 1) / 2); y++)
{
total += src.at<uchar>(x, y);
}
src.at<uchar>(i, j) = total / n / n;
}
}
}
void meanFilterS(Mat &src, int n)//不考虑边界情况
{
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j <src.cols; j++)
{
int total = 0;
int x;
int y;
for (x = i - ((n - 1) / 2); x < i + ((n - 1) / 2); x++)
for (y = j - ((n - 1) / 2); y < j + ((n - 1) / 2); y++)
{
total += src.at<uchar>(x, y);
}
src.at<uchar>(i, j) = total / n / n;
}
}
}
处理结果如下:
2.高斯滤波
高斯滤波总体原理和均值滤波类似,只是平均分布系数分布符合正态分布,,滤波更加合理化。
重点公式:
程序如下:
#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
Mat generateGaussianTemplate(int ksize, double sigma);
void gaussianBlur(Mat &src,Mat &gaussian);
int main()
{
Mat originImage = imread("E://匹配.jpg", 0);
imshow("原图", originImage);
Mat copyImage1=originImage.clone();
Mat copyImage2,copyImage3;
copyImage1.convertTo(copyImage2, CV_32FC1,1.0/255);//第三个系数的作用是把float类型的像素值归一化到0.0到1.0
copyImage1.convertTo(copyImage3, CV_32FC1,1.0/255);
Mat gaussian1=generateGaussianTemplate(5,0.8);
gaussianBlur(copyImage2, gaussian1);
imshow("高斯滤波图σ=0.8", copyImage2);
Mat gaussian2 = generateGaussianTemplate(5, 1.8);
gaussianBlur(copyImage3, gaussian2);
imshow("高斯滤波图σ=1.8", copyImage3);
waitKey(0);
return 0;
}
Mat generateGaussianTemplate( int ksize, double sigma)//计算高斯滤波的内核矩阵
{
Mat window(ksize,ksize,CV_32FC1);
static const double pi = 3.1415926;
int center = ksize / 2;
double x2, y2;
double sum = 0;
for (int i = 0; i < ksize; i++)
{
x2 = pow(i - center, 2);
for (int j = 0; j < ksize; j++)
{
y2 = pow(j - center, 2);
float g = exp(-(x2 + y2) / (2 * sigma * sigma));
g /= sqrt(2 * pi )* sigma;//看了好几版的正态分布的函数,这个系数有好几种写法,其中不必纠结于系数sqrt(2 * pi )* sigma,因为它只是!一个! 常数!并不会影响互相之间的比例关系,并且最终都要进行归一化
sum += g;
window.at<float>(i,j) = g;
}
}
for (int i = 0; i < ksize; i++)
{
for (int j = 0; j < ksize; j++)
{
window.at<float>(i, j) /= sum;//归一化
}
}
return window;
}
void gaussianBlur(Mat &src,Mat &gaussian)
{
for (int i = (gaussian.rows - 1) / 2; i < src.rows - (gaussian.rows - 1) / 2 ; i++)//这里就要考虑边界滤波的越界问题,否则会出现错误
{
for (int j = (gaussian.cols - 1) / 2; j < src.cols - (gaussian.cols - 1) / 2 ; j++)
{
float temp = 0.0;
int o = 0;
for (int x = i - (gaussian.rows - 1) / 2; x < i + (gaussian.rows - 1) / 2 + 1; x++)
{
int p = 0;
for (int y = j - (gaussian.cols - 1) / 2; y < j + (gaussian.cols - 1) / 2 + 1; y++)
{
temp += src.at<float>(x, y)*gaussian.at<float>(o, p);
p++;
}
o++;
}
src.at<float>(i, j) = temp;
}
}
}
处理结果如下:
3.中值滤波
取一个滤波核范围内的中间值,这个好理解,所以对于突变的极大值极小值(椒盐噪声)有很好的滤波效果。
#include<iostream>
#include<opencv.hpp>
using namespace std;
using namespace cv;
unsigned char lvbo(unsigned char D[]);
void meanFilter(Mat &src);
void salt(cv::Mat image, int n);
const int n = 3;
const int m = n*n;
unsigned char block[m];
int main()
{
Mat originImage = imread("E://匹配.jpg", 0);
imshow("原图", originImage);
Mat saltImage=originImage.clone();
salt(saltImage,10000);
imshow("盐噪声图", saltImage);
Mat src = saltImage.clone();
meanFilter(src);
imshow("中值滤波图", src);
waitKey(0);
return 0;
}
unsigned char lvbo(unsigned char D[])//排序取中间值
{
unsigned int temp;
int i, j;
for (i = 0; i<m; i++)
{
for (j = i+1; j<m; j++)
{
if (D[i]>D[j])
{
temp = D[i];
D[i] = D[j];
D[j] = temp;
}
}
}
return D[4];
}
void meanFilter(Mat &src)
{
//for (int i = (n- 1) / 2; i < src.rows - (n - 1) / 2; i++)//这里就要考虑边界滤波的越界问题,否则会出现错误
//{
//for (int j = (n - 1) / 2; j < src.cols - (n - 1) / 2; j++)
for (int i = 0; i < src.rows; i++)//这里边界问题没有考虑,也不会报错,,有些奇怪。。哪些情况会有问题?现在看没有什么规律
{
for (int j =0; j < src.cols; j++)
{
int k = 0;
for (int x = i - (n - 1) / 2; x < i + (n - 1) / 2 + 1; x++)
{
for (int y = j - (n - 1) / 2; y < j + (n - 1) / 2 + 1; y++)
{
block[k] = src.at<uchar>(x, y);
k++;
}
}
unsigned char meanValue = lvbo(block);
int x = meanValue;
src.at<uchar>(i, j) = meanValue;
}
}
}
void salt(cv::Mat image, int l)
{
int i, j;
for (int k = 0; k < l / 2; k++)
{
// rand() is the random number generator
i = std::rand() % image.cols; // % 整除取余数运算符,rand=1022,cols=1000,rand%cols=22
j = std::rand() % image.rows;
if (image.type() == CV_8UC1)
{ // gray-level image
image.at<uchar>(j, i) = 255; //at方法需要指定Mat变量返回值类型,如uchar等
}
else if (image.type() == CV_8UC3)
{ // color image
image.at<cv::Vec3b>(j, i)[0] = 255; //cv::Vec3b为opencv定义的一个3个值的向量类型
image.at<cv::Vec3b>(j, i)[1] = 255; //[]指定通道,B:0,G:1,R:2
image.at<cv::Vec3b>(j, i)[2] = 255;
}
}
}
处理结果如下:
4.双边滤波
双边滤波相对于高斯滤波,系数项多了一个值域参数,因为其他的滤波只考虑空间分布,一视同仁,对于边缘也有同样的模糊效果,这是我们不愿意看到的,系数项的作用是对于疑似边缘的处理进行对应调节,滤波合理化,减小模糊效果。
下面图片来自:双边滤波器的原理及实现
代码参考:C++实现双边滤波
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD);
double ColorFactor(int x, int y, double sigmaR);
Mat BilateralFilter(Mat &inputImg, int filterSize, double sigmaR, double sigmaD);
int main()
{
Mat image = imread("E:\\匹配.jpg",0);
imshow("原图", image);
Mat bilateralImage = image.clone();
Mat dst = BilateralFilter(bilateralImage, 15, 50, 10);
Mat out;
imshow("双边滤波图", dst);
bilateralFilter(image, out, 15, 50, 10);
imshow("opencv自带",out);
waitKey(0);
return 0;
}
double SpaceFactor(int x1, int y1, int x2, int y2, double sigmaD)
{
double absX = pow(abs(x1 - x2), 2);
double absY = pow(abs(y1 - y2), 2);
return exp(-(absX + absY) / (2 * pow(sigmaD, 2)));
}
double ColorFactor(int x, int y, double sigmaR)
{
double distance = pow(abs(x - y), 2);
return exp(-distance / (2 * pow(sigmaR, 2)));
}
Mat BilateralFilter(Mat &inputImg, int filterSize, double sigmaR, double sigmaD)
{
int len;
if (filterSize % 2 != 1 || filterSize <= 0)
{
cerr << "Filter Size must be a positive odd number!" << endl;
return inputImg;
}
len = filterSize / 2;
Mat resultGrayImg =inputImg.clone();
for (int i = 20; i<inputImg.rows; i++)
{
for (int j = 20; j<inputImg.cols; j++)
{
double k = 0.0;
double f = 0.0;
//不对边缘进行处理
if ((i - len) >= 0 && (i + len)<inputImg.rows && (j - len) >= 0 && (j + len)<inputImg.cols)
{
for (int r = i - len; r <= i + len; r++)
{
for (int c = j - len; c <= j + len; c++)
{
f = f+ inputImg.ptr<uchar>(r)[c] * SpaceFactor(i, j, r, c, sigmaD)*ColorFactor(inputImg.ptr<uchar>(i)[j], inputImg.ptr<uchar>(r)[c], sigmaR);
k += SpaceFactor(i, j, r, c, sigmaD)*ColorFactor(inputImg.ptr<uchar>(i)[j], inputImg.ptr<uchar>(r)[c], sigmaR);
}
}
int value = f / k;
int a = resultGrayImg.ptr<uchar>(i)[j];
if (value < 0)
value = 0;
else if (value > 255)
value = 255;
resultGrayImg.ptr<uchar>(i)[j] = value;
}
}
}
return resultGrayImg;
}
处理结果如下: