实验内容
实验4.1 高斯滤波
实现图像的高斯滤波。
- 通过参数sigma来控制平滑程度。
- 滤波窗口大小取为[6*sigma-1],[.]表示取整。
- 利用二维高斯函数的行列可分离性进行加速。
实验过程
1,计算卷积核大小。
卷积核大小公式为[6*sigma-1],[.]表示取整。如果大小为偶数,则将大小加一。并计算进行卷积需要随原图扩充的边缘大小,大小为:卷积核大小整除 2。
int border_size = (int)(6 * sigma - 1) / 2;
2,计算一维的高斯滤波。
公式为 G ( x ) = e − x 2 2 σ 2 G(x)=e^{-\frac{x^2}{2\sigma^2}} G(x)=e−2σ2x2, b o r d e r _ s i z e border\_size border_size滤波窗口的中心。进行归一化。
double *kernel = new double[(long long)border_size * 2 + 1];
double sum = 1;
kernel[border_size] = 1;
for (int i = 1; i <= border_size; i++)
{
double value = exp(-(i * i) / (2 * sigma * sigma));
kernel[border_size - i] = value;
kernel[border_size + i] = value;
sum += 2 * value;
}
for (int i = 0; i < border_size * 2 + 1; i++)
{
kernel[i] /= sum; // 归一化
}
3,对原图进行镜像填充。
b o r d e r e d _ i m g bordered\_img bordered_img的边长比原图多 2 个边缘宽,进行一次卷积后得到图像 t e m p temp temp,它的行数和填充后图相同,列数和原图相同。
Mat bordered_img;
copyMakeBorder(input, bordered_img, border_size, border_size, border_size, border_size,
BORDER_REFLECT_101);
// 进行对列的卷积后图像
Mat temp = Mat(bordered_img.rows, input.cols, input.type());
4,进行对列的一维高斯滤波卷积。
直接访问图像的像素,不使用 at 方法。每个索引的格式为[通道总数 ∗ 行号 ∗ 总列数 + 列号 + 通道号]。对行卷积也类似。
for (int i = 0; i < bordered_img.rows; i++)
{
for (int j = border_size; j < bordered_img.cols - border_size; j++)
{
for (int c = 0; c < ch; c++)
{
temp.data[ch * (i * temp.cols + j - border_size) + c] = 0;
for (int k = j - border_size; k <= j + border_size; k++)
{
temp.data[ch * (i * temp.cols + j - border_size) + c] +=
bordered_img.data[ch * (i * bordered_img.cols + k) + c] *
kernel[k - j + border_size];
}
}
}
}
5,添加滑块.
为了方便调整 sigma,添加滑块, 10 s i g m a 10sigma 10sigma是 s i g m a sigma sigma的十倍(因为滑块的参数是整数,使用 10 倍 sigma 使得 sigma 可以调整到一位小数)。
createTrackbar("10 * sigma", "image", &sigma10, 100, callBack, 0);
static void callBack(int, void *)
{
gaussianBlur(input_img, output_img, (double)sigma10 / 10);
imshow("image", output_img);
}
6,效果。
附源代码
#include<iostream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
using namespace std;
void gaussianBlur(const Mat& input, Mat& output, double sigma) {
int border_size = (int)(6 * sigma - 1)/2; //需要扩充的边缘宽度
if (border_size < 1) {
output = input;
return;
}
//计算卷积核
double* kernel = new double[(long long)border_size * 2 + 1];
double sum = 1;
kernel[border_size] = 1;
for (int i = 1; i <= border_size; i++) {
double value = exp(-(i * i) / (2 * sigma * sigma));
kernel[border_size - i] = value;
kernel[border_size + i] = value;
sum += 2 * value;
}
for (int i = 0; i < border_size * 2 + 1; i++) {
kernel[i] /= sum; //归一化
}
//填充后图像
Mat bordered_img;
copyMakeBorder(input, bordered_img, border_size, border_size, border_size, border_size, BORDER_REFLECT_101);
//进行对列的卷积后图像
Mat temp = Mat(bordered_img.rows, input.cols,input.type());
int ch = input.channels();//图像通道数
//对列卷积
for (int i = 0; i < bordered_img.rows; i++) {
for (int j = border_size; j < bordered_img.cols - border_size; j++) {
for (int c = 0; c < ch; c++) {
temp.data[ch * (i * temp.cols + j - border_size) + c] = 0;
for (int k = j - border_size; k <= j + border_size; k++) {
temp.data[ch * (i * temp.cols + j - border_size) + c] +=
bordered_img.data[ch * (i * bordered_img.cols + k) + c] * kernel[k - j + border_size];
}
}
}
}
//对行卷积
for (int j = 0; j < temp.cols; j++) {
for (int i = border_size; i < temp.rows - border_size; i++)
for (int c = 0; c < ch; c++) {
output.data[ch * ((i - border_size) * output.cols + j) + c] = 0;
for (int k = i - border_size; k <= i + border_size; k++) {
output.data[ch * ((i - border_size) * output.cols + j) + c] +=
temp.data[ch * (k * temp.cols + j) + c] * kernel[k - i + border_size];
}
}
}
}
void callBack(int, void*);
Mat input_img;
Mat output_img;
int sigma10=10; //十倍sigma
int main() {
//图像加载
input_img = imread("D:/Opencv/opencv-03/01.jpg");
output_img = Mat(input_img.size(), input_img.type());
namedWindow("image");
//imshow("image", input_img);
createTrackbar("10 * sigma", "image", &sigma10, 100, callBack, 0);
waitKey();
return 0;
}
static void callBack(int, void*)
{
gaussianBlur(input_img, output_img, (double)sigma10 / 10);
imshow("image", output_img);
}