卷積定義
矩陣的掩碼操作即對圖像進行卷積。對圖像卷積操作的意義為:鄰近像素對(包括該像素自身)對新像素的影響;影響大小取決於卷積核對應位置值得大小。
例如:圖像增強可以使用
用代碼實現
void Sharpen(const Mat& myImage, Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // 僅接受uchar圖像
Result.create(myImage.size(), myImage.type());
const int nChannels = myImage.channels();
for (int j = 1; j < myImage.rows - 1; ++j)//從第一行開始,而不是0;到rows-2行結束
{
const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
const uchar* current = myImage.ptr<uchar>(j);
const uchar* next = myImage.ptr<uchar>(j + 1);//下一行
uchar* output = Result.ptr<uchar>(j);
for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
{
*output++ = saturate_cast<uchar>(5 * current[i]
- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
}
}
//邊緣處理。邊緣沒有卷積,所以邊緣賦值為原圖像對應像素的值。
//先處理上和下
uchar* output_top = Result.ptr<uchar>(0);
const uchar* origin_top = myImage.ptr<uchar>(0);
uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
*output_top++ = *origin_top++;
*output_bottom++ = *origin_bottom++;
}
//左和右 分別有nChannel
for (int i = 0; i < myImage.rows; ++i){
const uchar* origin_left = myImage.ptr<uchar>(i);
uchar* output_left = Result.ptr<uchar>(i);
const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
uchar* output_right = output_left + nChannels*(myImage.cols - 1);
for (int j = 0; j < nChannels; ++j){
*output_left++ = *origin_left++;
*output_right++ = *origin_right++;
}
}
}
卷積在圖像處理中應用很廣泛,可以使用OpenCV自帶函數
void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )

濾波器
濾波器中,所有元素和相加后應該等於1。如果大於1,則圖像會變亮,如果小於1,圖像變暗。
平滑濾波器
平滑濾波器的作用是去除圖像中一些不重要的細節,減小噪聲。可以分為線性和非線性2大類:
1、線性平滑濾波器:均值波濾器
2、非線性平滑濾波器:最大值濾波、中值濾波、最小值濾波。
線性平滑濾波
線性平滑濾波可以減小“尖銳”,減小噪聲;因為圖像邊緣是有“尖銳”變換引起的,所以也就模糊了邊緣。
上面這個濾波器,各個像素的值貢獻相同,是均值濾波,在OpenCV有對應函數。
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
有時各個像素的重要性不同,可以使用:
上面的濾波器表面中間和其挨着的像素貢獻大一些。
非線性濾波
線性濾波是對濾波器區域內各個像素點加權求和,結果和原圖像濾波區域有一個線性關系。非線性濾波則不然;基於統計排序的非線性濾波分為:最大值濾波、中值濾波、最小值濾波。顧名思義,上述三種濾波器是分別取卷積核區域內最大像素點值、中值像素點值、最小像素點值。
中值濾波:
中值濾波是對卷積核區域內像素排序,取中位數,只有奇數才有中位數,所有卷積核只能為3、5、7……這樣的奇數。中值濾波在去除噪聲的同時,可以比較好的保留圖像細節(尖銳等),能夠有效去除脈沖噪聲(椒鹽噪聲)。
void medianBlur(InputArray src, OutputArray dst, int ksize)
高斯濾波:卷積核是按照高斯分布的
和均值像素差值大的像素,對結果貢獻較小;離均值“近”的像素,對結果貢獻較大。
函數為:
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )
雙邊濾波:
前面提高的濾波都是知識考慮了圖像的空間關系,即卷積核區域內的空域關系,沒有考慮像素間的關系。因此在平滑時,會模糊整幅圖像;雙邊濾波不僅考慮了圖像的空域關系,還考慮了圖像間像素值的差異關系。可以參考這里
OpenCV對應函數
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )
實驗代碼
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
void Sharpen(const Mat& myImage, Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // 僅接受uchar圖像
Result.create(myImage.size(), myImage.type());
const int nChannels = myImage.channels();
for (int j = 1; j < myImage.rows - 1; ++j)//從第一行開始,而不是0;到rows-2行結束
{
const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
const uchar* current = myImage.ptr<uchar>(j);
const uchar* next = myImage.ptr<uchar>(j + 1);//下一行
uchar* output = Result.ptr<uchar>(j);
for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
{
*output++ = saturate_cast<uchar>(5 * current[i]
- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
}
}
//邊緣處理。邊緣沒有卷積,所以邊緣賦值為原圖像對應像素的值。
//先處理上和下
uchar* output_top = Result.ptr<uchar>(0);
const uchar* origin_top = myImage.ptr<uchar>(0);
uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
*output_top++ = *origin_top++;
*output_bottom++ = *origin_bottom++;
}
//左和右 分別有nChannel
for (int i = 0; i < myImage.rows; ++i){
const uchar* origin_left = myImage.ptr<uchar>(i);
uchar* output_left = Result.ptr<uchar>(i);
const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
uchar* output_right = output_left + nChannels*(myImage.cols - 1);
for (int j = 0; j < nChannels; ++j){
*output_left++ = *origin_left++;
*output_right++ = *origin_right++;
}
}
}
int main(int argc, char* argv[]){
const char* path = "";
Mat img = imread(path);
if (img.empty())
{
cout << "error";
return -1;
}
imshow("原圖像", img);
//均值濾波
Mat blur_mat;
blur(img, blur_mat, Size(3, 3));
imshow("均值濾波", blur_mat);
//線性非均值濾波
Mat kern = (Mat_<float>(3, 3) << 1, 2, 1,
2, 4, 2,
1, 2, 1) / 16;
Mat blur2_mat;
filter2D(img, blur2_mat, img.depth(), kern);
imshow("線性非均值濾波", blur2_mat);
//中值濾波
Mat memedian_mat;
medianBlur(img, memedian_mat, 3);
imshow("中值濾波", memedian_mat);
//高斯濾波
Mat Gaussian_mat;
GaussianBlur(img, Gaussian_mat, Size(3, 3), 0, 0);
imshow("高斯濾波", Gaussian_mat);
//雙邊濾波
Mat bilateral_mat;
bilateralFilter(img, bilateral_mat, 25, 25 * 2, 25 / 2);
imshow("雙邊濾波", bilateral_mat);
waitKey();
return 0;
}
