目录
note
此次使用的均值滤波卷积核3x3
[1/9,1/9,1/9;1/9,1/9,1/9;1/9,1/9,1/9]
code
// 图像噪声之椒盐噪声
// 随机分布
// 灰度值:0或255
void SaltPepperNoise(Mat& noise, int noiseAmount) {
if (noise.type() != CV_8UC1 && noise.type() != CV_8UC3) {
return;
}
srand(time(nullptr));
for (int i = 0; i < noiseAmount; ++i) {
int x = rand() % noise.cols;
int y = rand() % noise.rows;
if (i % 2) {
if (noise.type() == CV_8UC1) {
noise.at<uchar>(x,y) = 255;
}
else if (noise.type() == CV_8UC3) {
noise.at<Vec3b>(x,y) = Vec3b(255,255,255);
}
}
else {
if (noise.type() == CV_8UC1) {
noise.at<uchar>(x,y) = 0;
}
else if (noise.type() == CV_8UC3) {
noise.at<Vec3b>(x,y) = Vec3b(0,0,0);
}
}
}
}
// 不扩充边缘的矩阵卷积,输出矩阵和输入矩阵一样大
void MyMatConvolute2(Mat& src, Mat& kernel, Mat& res) {
if (src.channels() != 1) {
return;
}
if ((kernel.channels() != 1) || (kernel.rows != kernel.cols) || (kernel.rows / 2 == 0)) {
return;
}
int srcType = src.type();
// 先扩大,再缩小为原来大小
int edge = kernel.rows / 2;
Mat tmp(src.rows+2*edge, src.cols+2*edge, srcType, Scalar(0));
for (int i = 0; i < src.rows; ++i) {
for (int j = 0; j < src.cols; ++j) {
if (srcType == CV_8UC1) {
tmp.at<uchar>(i+edge,j+edge) = src.at<uchar>(i,j);
}
else if (srcType == CV_32FC1) {
tmp.at<float>(i+edge,j+edge) = src.at<float>(i,j);
}
else {
abort();
}
}
}
MyMatConvolute1(tmp, kernel, res);
}
// 不扩充边缘的矩阵卷积,导致输出矩阵比输入矩阵小
void MyMatConvolute1(Mat& src, Mat& kernel, Mat& res) {
if (src.channels() != 1) {
return;
}
if ((kernel.channels() != 1) || (kernel.rows != kernel.cols) || (kernel.rows / 2 == 0)) {
return;
}
// 数据类型转换:uint8_t转float32_t
int srcType = src.type();
int kernelType = kernel.type();
if (srcType != CV_32FC1) {
src.convertTo(src, CV_32FC1);
}
if (kernelType != CV_32FC1) {
kernel.convertTo(kernel, CV_32FC1);
}
int left = kernel.rows / 2;
res = Mat(src.rows-2*left, src.cols-2*left, CV_32FC1);
int res_row = 0, res_col = 0;
for (int i = 0; i <= src.rows-kernel.rows; ++i) {
for (int j = 0; j <= src.cols-kernel.cols; ++j) {
float out = 0.0;
for (int p = 0; p < kernel.rows; ++p) {
for (int q = 0; q < kernel.cols; ++q) {
out += src.at<float>(i+p,j+q) * kernel.at<float>(p,q);
}
}
res.at<float>(res_row,res_col) = out;
++res_col;
if (res_col >= res.cols) {
res_col = 0;
++res_row;
}
}
}
// 数据类型还原
src.convertTo(src, srcType);
kernel.convertTo(kernel, kernelType);
res.convertTo(res, srcType);
}
// 图像增强之均值滤波
// 处理域:空间域
// 卷积核使用大小:3x3
void img_filter_average_test(void) {
Mat src = imread("../source/lena.jpg", IMREAD_GRAYSCALE);
// Mat src = imread("../source/lena.jpg");
if (src.empty()) {
printf("src empty\n");
return;
}
Mat noiseMat(src.rows, src.cols, src.type(), Scalar(0));
Mat added(src.rows, src.cols, src.type(), Scalar(0));
Mat kernel = Mat::ones(3,3, CV_32FC1) / 9.0;
Mat myfiltered(src.rows, src.cols, src.type(), Scalar(0));
Mat opencvfiltered(src.rows, src.cols, src.type(), Scalar(0));
namedWindow("src", WINDOW_NORMAL);
namedWindow("noise", WINDOW_NORMAL);
namedWindow("added", WINDOW_NORMAL);
namedWindow("myfiltered", WINDOW_NORMAL);
namedWindow("opencvfiltered", WINDOW_NORMAL);
imshow("src", src);
SaltPepperNoise(noiseMat, 1000);
imshow("noise", noiseMat);
add(src, noiseMat, added);
imshow("added", added);
MyMatConvolute2(added, kernel, myfiltered);
imshow("myfiltered", myfiltered);
filter2D(added, opencvfiltered, added.type(), kernel, Point(-1,-1), 0.0, BORDER_CONSTANT);
imshow("opencvfiltered", opencvfiltered);
MyWait();
}