jashliao 用 VC++ 實現 fanfuhan OpenCV 教學022 ~ opencv-022-圖像均值模糊(blur)和高斯模糊(GaussianBlur)影像濾波器實際比較 [保持銳利度&降低雜訊干擾]
jashliao 用 VC++ 實現 fanfuhan OpenCV 教學022 ~ opencv-022-圖像均值模糊(blur)和高斯模糊(GaussianBlur)影像濾波器實際比較 [保持銳利度&降低雜訊干擾]
資料來源: https://fanfuhan.github.io/
https://fanfuhan.github.io/2019/04/02/opencv-022/
GITHUB:https://github.com/jash-git/fanfuhan_ML_OpenCV
https://github.com/jash-git/jashliao-implements-FANFUHAN-OPENCV-with-VC
★前言:

★主題:
高斯濾波器能夠有效的抑制噪聲,平滑圖像。高斯濾波器相比於均值濾波器對圖像個模糊程度較小。
高斯模糊和均值模糊其原理上的唯一區別只是在於卷積核的值不同罷了,高斯卷積核矩陣值服從二維高斯函數
也就是說一個圖像與服從二維高斯分布的函數做卷積,由於高斯函數是連續的,所有yaoj要將二維高斯函數進行釆樣和離散化,最后得到服從高斯函數規律的卷積核矩陣。
最有用的濾波器 (盡管不是最快的)。 高斯濾波是將輸入數組的每一個像素點與高斯內核卷積將卷積和當作輸出像素值。
OPENCV提供的高斯濾波(GaussianBlur)函數,其相關介紹如下所列:
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT)
src – 輸入圖片,可以使是任意通道數,該函數對通道是獨立處理的,但是深度只能是CV_8U, CV_16U, CV_16S, CV_32F or CV_64F.
dst – 輸出圖片,和輸入圖片相同大小和深度。
ksize – 高斯內核大小。ksize.width和ksize.height允許不相同但他們必須是正奇數。或者等於0,由參數sigma的乘機決定。
sigmaX – 高斯內核在X方向的標准偏差。
sigmaY – 高斯內核在Y方向的標准偏差。如果sigmaY為0,他將和sigmaX的值相同,如果他們都為0,那么他們由ksize.width和ksize.height計算得出。
borderType – 用於判斷圖像邊界的模式。
★C++
// VC_FANFUHAN_OPENCV022.cpp : 定義主控台應用程式的進入點。
//
/*
// Debug | x32
通用屬性
| C/C++
| | 一般
| | 其他 Include 目錄 -> C:\opencv\build\include
|
| 連結器
| |一一般
| | 其他程式庫目錄 -> C:\opencv\build\x64\vc15\lib
|
| |一輸入
| | 其他相依性 -> opencv_world411d.lib;%(AdditionalDependencies)
// Releas | x64
組態屬性
| C/C++
| | 一般
| | 其他 Include 目錄 -> C:\opencv\build\include
|
| 連結器
| |一般
| | 其他程式庫目錄 -> C:\opencv\build\x64\vc15\lib
|
| |一輸入
| | 其他相依性 -> opencv_world411.lib;%(AdditionalDependencies)
*/
#include "stdafx.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;
void showHistogram(InputArray src, cv::String StrTitle);
void backProjection_demo(Mat &mat, Mat &model);
void blur3x3(Mat &src, Mat *det);
void pause()
{
printf("Press Enter key to continue...");
fgetc(stdin);
}
int main()
{
Mat src = imread("../../images/target.png");
if (src.empty())
{
cout << "could not load image.." << endl;
pause();
return -1;
}
else
{
imshow("input_src", src);
showHistogram(src, "Histogram_input_src");
//均值模糊
Mat dst1, dst2, dst3;
blur(src, dst1, Size(5, 5), Point(-1, -1), 4);//blur(src, dst, Size(6, 6), Point(-1, -1), 4);//blur(src, dst, Size(15, 15), Point(-1, -1), 4);//blur(src, dst, Size(3, 3), Point(-1, -1), 4);
imshow("blur ksize=5", dst1);
showHistogram(dst1, "Histogram_blur ksize=5");
GaussianBlur(src, dst2, Size(5, 5), 15, 0);
imshow("gaussian ksize=5", dst2);
showHistogram(dst2, "Histogram_gaussian ksize=5");
GaussianBlur(src, dst3, Size(15, 15), 15, 0);
imshow("gaussian ksize=15", dst3);
showHistogram(dst3, "Histogram_gaussian ksize=15");
/*
Mat dst01 = Mat::zeros(src.size(), src.type());
blur3x3(src, &dst01);// 3x3 均值模糊,自定义版本实现
imshow("dst01_blur3x3", dst01);
showHistogram(dst01, "Histogram_dst01_blur3x3");
*/
waitKey(0);
}
return 0;
}
void blur3x3(Mat &src, Mat *det)
{
// 3x3 均值模糊,自定义版本实现
for (int row = 1; row < src.rows - 1; row++) {
for (int col = 1; col < src.cols - 1; col++) {
Vec3b p1 = src.at<Vec3b>(row - 1, col - 1);
Vec3b p2 = src.at<Vec3b>(row - 1, col);
Vec3b p3 = src.at<Vec3b>(row - 1, col + 1);
Vec3b p4 = src.at<Vec3b>(row, col - 1);
Vec3b p5 = src.at<Vec3b>(row, col);
Vec3b p6 = src.at<Vec3b>(row, col + 1);
Vec3b p7 = src.at<Vec3b>(row + 1, col - 1);
Vec3b p8 = src.at<Vec3b>(row + 1, col);
Vec3b p9 = src.at<Vec3b>(row + 1, col + 1);
int b = p1[0] + p2[0] + p3[0] + p4[0] + p5[0] + p6[0] + p7[0] + p8[0] + p9[0];
int g = p1[1] + p2[1] + p3[1] + p4[1] + p5[1] + p6[1] + p7[1] + p8[1] + p9[1];
int r = p1[2] + p2[2] + p3[2] + p4[2] + p5[2] + p6[2] + p7[2] + p8[2] + p9[2];
det->at<Vec3b>(row, col)[0] = saturate_cast<uchar>(b / 9);
det->at<Vec3b>(row, col)[1] = saturate_cast<uchar>(g / 9);
det->at<Vec3b>(row, col)[2] = saturate_cast<uchar>(r / 9);
}
}
}
void backProjection_demo(Mat &image, Mat &model)
{
Mat image_hsv, model_hsv;
cvtColor(image, image_hsv, COLOR_BGR2HSV);//彩色轉HSV
cvtColor(model, model_hsv, COLOR_BGR2HSV);
// 定义直方图参数与属性
int h_bins = 32, s_bins = 32;
int histSize[] = { h_bins, s_bins };//要切分的像素強度值範圍,預設為256。每個channel皆可指定一個範圍。例如,[32,32,32] 表示RGB三個channels皆切分為32區段
float h_ranges[] = { 0, 180 }, s_ranges[] = { 0, 256 };
const float* ranges[] = { h_ranges, s_ranges };
int channels[] = { 0, 1 };
Mat roiHist;//計算ROI的直方圖
calcHist(&model_hsv, 1, channels, Mat(), roiHist, 2, histSize, ranges);
normalize(roiHist, roiHist, 0, 255, NORM_MINMAX, -1, Mat());
Mat roiproj, backproj;
calcBackProject(&image_hsv, 1, channels, roiHist, roiproj, ranges);//使用反向投影 產生ROI(前景)的mask
bitwise_not(roiproj, backproj);//產生背景的mask
imshow("ROIProj", roiproj);
imshow("BackProj", backproj);
}
void showHistogram(InputArray src, cv::String StrTitle)
{
bool blnGray = false;
if (src.channels() == 1)
{
blnGray = true;
}
// 三通道/單通道 直方圖 紀錄陣列
vector<Mat> bgr_plane;
vector<Mat> gray_plane;
// 定义参数变量
const int channels[1] = { 0 };
const int bins[1] = { 256 };
float hranges[2] = { 0, 255 };
const float *ranges[1] = { hranges };
Mat b_hist, g_hist, r_hist, hist;
// 计算三通道直方图
/*
void calcHist( const Mat* images, int nimages,const int* channels, InputArray mask,OutputArray hist, int dims, const int* histSize,const float** ranges, bool uniform=true, bool accumulate=false );
1.輸入的圖像數組
2.輸入數組的個數
3.通道數
4.掩碼
5.直方圖
6.直方圖維度
7.直方圖每個維度的尺寸數組
8.每一維數組的範圍
9.直方圖是否是均勻
10.配置階段不清零
*/
if (blnGray)
{
split(src, gray_plane);
calcHist(&gray_plane[0], 1, 0, Mat(), hist, 1, bins, ranges);
}
else
{
split(src, bgr_plane);
calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
}
/*
* 显示直方图
*/
int hist_w = 512;
int hist_h = 400;
int bin_w = cvRound((double)hist_w / bins[0]);
Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);
// 归一化直方图数据
if (blnGray)
{
normalize(hist, hist, 0, histImage.rows, NORM_MINMAX, -1);
}
else
{
normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1);
normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1);
normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1);
}
// 绘制直方图曲线
for (int i = 1; i < bins[0]; ++i)
{
if (blnGray)
{
line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(hist.at<float>(i - 1))),
Point(bin_w * (i), hist_h - cvRound(hist.at<float>(i))), Scalar(255, 255, 255),
2, 8, 0);
}
else
{
line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
Point(bin_w * (i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0),
2, 8, 0);
line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
Point(bin_w * (i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0),
2, 8, 0);
line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255),
2, 8, 0);
}
}
imshow(StrTitle, histImage);
}
★Python
import cv2 as cv
import numpy as np
src = cv.imread("D:/javaopencv/snow.png")
cv.namedWindow("input", cv.WINDOW_AUTOSIZE)
cv.imshow("input", src)
dst1 = cv.blur(src, (5, 5))
dst2 = cv.GaussianBlur(src, (5, 5), sigmaX=15)
dst3 = cv.GaussianBlur(src, (0, 0), sigmaX=15)
cv.imshow("blur ksize=5", dst1)
cv.imshow("gaussian ksize=5", dst2)
cv.imshow("gaussian sigmax=15", dst3)
cv.waitKey(0)
cv.destroyAllWindows()
★結果圖:

★延伸說明/重點回顧: