iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0

一、 簡介

當你在使用社群網站時,是否曾經使用過美顏濾鏡?這些濾鏡可以讓你的臉部瑕疵模糊不清,而這種「模糊」就是使用平滑化對影像進行處理。

影像平滑化(Smoothing)也稱為模糊(blurring),在影像處理中扮演著重要的角色。主要作用是將圖片中的細節減少,同時消除受到環境干擾所引起的雜訊。透過影像平滑化,我們能夠將視覺的焦點更集中地放在影像的主要特徵上,而不被過多的細節所干擾。

以下圖片展示了對圖片進行平滑化後所產生的3D效果。在應用濾波器進行處理之後,原始圖片中的許多雜訊被有效抑制,並且在Z方向,即f(x,y)的變動相對平緩,不會出現劇烈的波動。

https://ithelp.ithome.com.tw/upload/images/20230923/20161732C23VXWthat.jpg

二、 原理

1. 空間濾波

稍微在複習一下,在上一章介紹過空間濾波器,運算的方法是透過核w(x,y)圖片f(x,y)進行摺積運算,從而生成新影像g(x,y)。平滑化是空間濾波器的一種應用,利用調整核w(x,y)去改變濾波方式。這個章節會直接略過解釋摺積運算的原理。

https://ithelp.ithome.com.tw/upload/images/20230923/20161732WT8QwYFRm1.png

如果你對影像是如何進行摺積運算還不是很熟,可以參考【Day8】影像處理的數學基礎:深入解析影像摺積原理

2. 平均濾波(Mean Filter)

平均濾波是一種常見的影像處理技術,用於平滑化或模糊化影像,適合處理輕微的高斯雜訊或均勻雜訊。平均濾波的核心概念是將每個像素的值替換為其周圍鄰域像素的平均值,從而達到減少噪點和細節,讓影像變得更平滑的效果。這種平均化操作可以有助於去除一些高頻細節,從而降低影像的干擾,使得影像更容易分析或其他後續處理。

https://ithelp.ithome.com.tw/upload/images/20230923/20161732gO6Zcxwc5N.png

2. 高斯濾波(Gaussian Filter)

高斯濾波是一種常用的影像平滑化技術,也被稱為高斯模糊。高斯濾波器在計算加權平均時,考慮了像素間的距離,能夠保留更好的邊緣細節並減少紋理模糊。與傳統的平均濾波相比,高斯濾波更適合在保留影像特徵的同時進行平滑。高斯濾波基於高斯函數的特性,通過將每個像素的值替換為其周圍鄰域像素的加權平均值來實現平滑效果,越靠近中心像素的鄰域像素具有更大的權重,反之距離中心越遠的權重越小,權重分布可以透過調整常態分佈的標準差來改變,標準差越大核函數就會越平坦。在OpenCV的高斯核中,所有元素的加總為1。
https://ithelp.ithome.com.tw/upload/images/20230926/201617328l9NEME03r.png

3. 中值濾波(Median Filter)

中值濾波是一種非線性的影像處理濾波器,運作原理與傳統的線性濾波方法不同。在中值濾波中,不像其他濾波器使用特定核函數對像素進行加權平均,而是將指定像素區域內的像素值進行排序,然後選取中間位置的像素值作為輸出

這種運作方式對於消除影像中的椒鹽噪點非常有效。因為在一個像素區域內,噪點通常會使某些像素值突然增加或減少,而中值濾波可以通過選擇中間值避免極端的像素值變化,同時保留了影像的主要特徵。
https://ithelp.ithome.com.tw/upload/images/20230923/20161732bj1MmINCYL.png

4. 雙邊濾波(Bilateral Filter)

雙邊濾波器(Bilateral Filter)的特點是能夠在平滑的同時保留影像的邊緣和細節。這種濾波器在一定程度上解決了傳統平滑化方法可能引起的細節模糊問題,特別適合處理有噪點的影像。雙邊濾波器的核心思想是結合空間域和強度域的資訊來進行平滑。在進行像素平滑的過程中,不僅考慮了像素的空間位置,還考慮了它們之間的強度相似性。具體來說,距離較近的像素和強度相似的像素在平滑時會得到更高的權重,而距離較遠或強度差異較大的像素則獲得較低的權重。

https://ithelp.ithome.com.tw/upload/images/20230923/20161732nCbDdPpDIK.jpg

三、程式碼

這個程式碼提供了兩種運算方式,一個是使用OpenCV內建的函式,另一個是使用自己建的Kernel做摺積,兩者效果一樣。你可以透過設定USE_OPENCV10來決定你要使用前者還是後者的實現方式。

#define USE_OPENCV 1

1. 逐行解釋

1) 使用cv::filter2D()進行影像摺積

將核 kernel 對灰階影像 grayImage 進行摺積,並將結果存儲在輸出影像 dst 中。

cv::Mat dst;
cv::filter2D(grayImage, dst,CV_8U, kernel);
  • grayImage:輸入的灰階影像,即待處理的影像。
  • dst:輸出影像,摺積操作的結果將存儲在這個影像中。
  • CV_8U:輸出影像的深度(或數據類型)。在這裡,它表示輸出影像的像素值是8位無符號整數(範圍從0到255)。
  • kernel:摺積核或濾波器,用於定義摺積操作的內容。

2) 使用cv::ones取得平均濾波核

建立一個大小為size x size的矩陣kernel,並將其所有元素初始化為1,形成一個均值核。

使用cv::sum函數計算核的所有元素之和,並將核正規化,確保其所有元素的加總為1。這是為了確保均值濾波的效果,即對核內的像素進行平均。

cv::Mat kernel = cv::Mat::ones(size, size, CV_32F);
kernel /= cv::sum(kernel);

3) 使用cv::getGaussianKernel取得高斯核

getGaussianKernel這個程式會生成一個3x1的高斯核,然後輸出每個元素的值。的第三個參數指定了輸出高斯核的數據類型,這裡必須使用CV_32F表示32位浮點數類型,因為kernel裡面是浮點數。生成兩個3x1的高斯核(一個是行方向的,另一個是列方向的),然後對它們進行相乘,得到3x3的高斯核。

cv::Mat kernel_x=cv::getGaussianKernel(3, 1, CV_32F);
cv::Mat kernel_y=cv::getGaussianKernel(3, 1, CV_32F);
cv::transpose(kernel_x,kernel_x);
cv::Mat kernel = kernel_y*kernel_x ;

4) 使用OpenCV濾波器

使用OpenCV函式庫中的cv::boxFilter函式來實平均濾波,定義一個大小為size的核進行平均濾波。size數值越大,影像會越模糊。

cv::boxFilter(grayImage, dst, grayImage.depth(), cv::Size(size,size));

使用OpenCV函式庫中的cv::GaussianBlur函式來實高斯濾波,定義一個大小為size的高斯核,X、Y方向的常態分佈標準差為sigma,sigma數值越大影像會越模糊。

cv::GaussianBlur(grayImage, dst, cv::Size(size,size), sigma,sigma);

使用OpenCV函式庫中的cv::medianBlur函式來實現中值濾波,定義一個大小為size的核,對每個核中的元素取中位數。size數值越大,影像中的細節會越少。

cv::medianBlur(grayImage, dst, size);

使用OpenCV函式庫中的cv::bilateralFilter函式來實現雙邊濾波。

cv::bilateralFilter(grayImage, dst,9,sigmaColor,sigmaSpace);

2. 完整程式碼

#include "opencv2/opencv.hpp"
#define USE_OPENCV 1
using namespace std;

cv::Mat grayImage;

// 回調函數,用於平均濾波
void mean_filter_trackbar_callback(int size, void*) {
	if (size % 2 == 0)
		size++;
	cv::Mat dst;
#if USE_OPENCV
	// 使用OpenCV函數執行平均濾波
	cv::boxFilter(grayImage, dst, grayImage.depth(), cv::Size(size, size));
#else
    // 使用自訂的均值濾波內核進行影像處理
    cv::Mat kernel = cv::Mat::ones(size, size, CV_32F);
	kernel /= cv::sum(kernel);
    cv::filter2D(grayImage, dst, grayImage.depth(), kernel);
#endif
	cv::imshow("Mean Filter", dst);
}

// 回調函數,用於高斯濾波
void gaussian_filter_trackbar_callback(int position, void*) {
	int size = cv::getTrackbarPos("Size", "Gaussian Filter");
	if (size % 2 == 0)
		size++;
	int sigma = cv::getTrackbarPos("Sigma", "Gaussian Filter");
	cv::Mat dst;
#if USE_OPENCV
	// 使用OpenCV函數執行高斯濾波
	cv::GaussianBlur(grayImage, dst, cv::Size(size, size), sigma, sigma);
#else
	// 使用自訂的高斯核進行摺積
	cv::Mat kernel_x = cv::getGaussianKernel(size, sigma, CV_32F);
	cv::Mat kernel_y = cv::getGaussianKernel(size, sigma, CV_32F);
	cv::transpose(kernel_x, kernel_x);
	cv::Mat kernel = kernel_y * kernel_x;
    cv::filter2D(grayImage, dst, grayImage.depth(), kernel);
#endif

	cv::imshow("Gaussian Filter", dst);
}

// 回調函數,用於中值濾波
void median_filter_trackbar_callback(int position, void*) {
	int size = cv::getTrackbarPos("Size", "Median Filter");
	if (size % 2 == 0)
		size++;
	cv::Mat dst;
	cv::medianBlur(grayImage, dst, size);
	cv::imshow("Median Filter", dst);
}

// 回調函數,用於雙邊濾波
void bilateral_filter_trackbar_callback(int position, void*) {
	int sigmaColor = cv::getTrackbarPos("SigmaColor", "Bilateral Filter");
	int sigmaSpace = cv::getTrackbarPos("SigmaSpace", "Bilateral Filter");
	cv::Mat dst;
	cv::bilateralFilter(grayImage, dst, 9, sigmaColor, sigmaSpace);
	cv::imshow("Bilateral Filter", dst);
}

int main() {
	grayImage = cv::imread("C:\\Users\\vince\\Downloads\\Lenna.png", cv::IMREAD_GRAYSCALE);
	cv::imshow("original", grayImage);

	cv::namedWindow("Mean Filter", cv::WINDOW_NORMAL);
	cv::resizeWindow("Mean Filter", cv::Size(512, 600));
	cv::createTrackbar("Size", "Mean Filter", NULL, 255, mean_filter_trackbar_callback);

	cv::namedWindow("Gaussian Filter", cv::WINDOW_NORMAL);
	cv::resizeWindow("Gaussian Filter", cv::Size(512, 600));
	cv::createTrackbar("Size", "Gaussian Filter", NULL, 255, gaussian_filter_trackbar_callback);
	cv::createTrackbar("Sigma", "Gaussian Filter", NULL, 255, gaussian_filter_trackbar_callback);

	cv::namedWindow("Median Filter", cv::WINDOW_NORMAL);
	cv::resizeWindow("Median Filter", cv::Size(512, 600));
	cv::createTrackbar("Size", "Median Filter", NULL, 255, median_filter_trackbar_callback);

	cv::namedWindow("Bilateral Filter", cv::WINDOW_NORMAL);
	cv::resizeWindow("Bilateral Filter", cv::Size(512, 600));
	cv::createTrackbar("SigmaColor", "Bilateral Filter", NULL, 255, bilateral_filter_trackbar_callback);
	cv::createTrackbar("SigmaSpace", "Bilateral Filter", NULL, 255, bilateral_filter_trackbar_callback);

	cv::waitKey(0);
	return 0;
}

3. 測試結果

可以看到平均濾波、高斯濾波和中值濾波的輸出結果,會發現平均濾波與高斯濾波相比,平均濾波容易產生原本畫面沒有的輪廓,且邊緣相較模糊,不利於邊緣檢測。接下來再觀察中值濾波,會發現圖片損失掉大量的細節,但是相較上面兩個濾波器輪廓較為明顯,對邊緣檢測來講會比較容易。最後是雙邊濾波的結果,可以看到圖片在平滑的情況下依舊可以維持邊緣的清晰度。

https://ithelp.ithome.com.tw/upload/images/20230923/20161732fFpshRR9JY.png


上一篇
【Day14】影像處理 空間濾波器(Spatial Filter)
下一篇
【Day16】​OpenCV 影像金字塔:不同尺度下的影像
系列文
圖解C++影像處理與OpenCV應用:從基礎到高階,深入學習超硬核技術!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言