OpenCV 【十八】图像平滑处理/腐蚀与膨胀(Eroding and Dilating)/开闭运算,形态梯度,顶帽,黑帽运算
圖像濾波總結(jié)(面試經(jīng)驗(yàn)總結(jié))https://blog.csdn.net/Darlingqiang/article/details/79507468
目錄
part one 圖像平滑處理
1原理
2代碼
3效果
part two 腐蝕與膨脹(Eroding and Dilating)
1原理
2代碼
3運(yùn)行結(jié)果
part three更多形態(tài)學(xué)變換?
1 原理
2 代碼
3 結(jié)果
part one 圖像平滑處理
1原理
-
平滑?也稱?模糊, 是一項(xiàng)簡(jiǎn)單且使用頻率很高的圖像處理方法。
-
平滑處理的用途有很多, 但是在本教程中我們僅僅關(guān)注它減少噪聲的功用 (其他用途在以后的教程中會(huì)接觸到)。
-
平滑處理時(shí)需要用到一個(gè)?濾波器?。 最常用的濾波器是?線性?濾波器,線性濾波處理的輸出像素值 (i.e.?) 是輸入像素值 (i.e.?)的加權(quán)和 :
?稱為?核, 它僅僅是一個(gè)加權(quán)系數(shù)。
不妨把?濾波器?想象成一個(gè)包含加權(quán)系數(shù)的窗口,當(dāng)使用這個(gè)濾波器平滑處理圖像時(shí),就把這個(gè)窗口滑過(guò)圖像。
-
濾波器的種類有很多, 這里僅僅提及最常用的:
1.1歸一化塊濾波器 (Normalized Box Filter)
-
最簡(jiǎn)單的濾波器, 輸出像素值是核窗口內(nèi)像素值的 均值 ( 所有像素加權(quán)系數(shù)相等)
-
核如下:
1.2高斯濾波器 (Gaussian Filter)
-
最有用的濾波器 (盡管不是最快的)。 高斯濾波是將輸入數(shù)組的每一個(gè)像素點(diǎn)與 高斯內(nèi)核 卷積將卷積和當(dāng)作輸出像素值。
-
還記得1維高斯函數(shù)的樣子嗎?
?
假設(shè)圖像是1維的,那么觀察上圖,不難發(fā)現(xiàn)中間像素的加權(quán)系數(shù)是最大的, 周邊像素的加權(quán)系數(shù)隨著它們遠(yuǎn)離中間像素的距離增大而逐漸減小。
Note
?
2維高斯函數(shù)可以表達(dá)為 :
其中 為均值 (峰值對(duì)應(yīng)位置),
代表標(biāo)準(zhǔn)差 (變量 和 變量 各有一個(gè)均值,也各有一個(gè)標(biāo)準(zhǔn)差)
1.3中值濾波器 (Median Filter)
中值濾波將圖像的每個(gè)像素用鄰域 (以當(dāng)前像素為中心的正方形區(qū)域)像素的 中值 代替 。
1.4雙邊濾波 (Bilateral Filter)
-
目前我們了解的濾波器都是為了 平滑 圖像, 問(wèn)題是有些時(shí)候這些濾波器不僅僅削弱了噪聲, 連帶著把邊緣也給磨掉了。 為避免這樣的情形 (至少在一定程度上 ), 我們可以使用雙邊濾波。
-
類似于高斯濾波器,雙邊濾波器也給每一個(gè)鄰域像素分配一個(gè)加權(quán)系數(shù)。 這些加權(quán)系數(shù)包含兩個(gè)部分, 第一部分加權(quán)方式與高斯濾波一樣,第二部分的權(quán)重則取決于該鄰域像素與當(dāng)前像素的灰度差值。
-
詳細(xì)的解釋可以查看 鏈接
2代碼
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
?
using namespace std;
using namespace cv;
?
/// 全局變量
int DELAY_CAPTION = 15000;
int DELAY_BLUR = 100;
int MAX_KERNEL_LENGTH = 31;
?
Mat src; Mat dst;
char window_name[] = "Filter Demo 1";
?
/// 函數(shù)申明
int display_caption(char* caption);
int display_dst(int delay);
?
/**
* main 函數(shù)
*/
int main(int argc, char** argv)
{namedWindow(window_name, CV_WINDOW_AUTOSIZE);
?/// 載入原圖像src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\4.jpg", 1);
?if (display_caption("Original Image") != 0) { return 0; }
?dst = src.clone();if (display_dst(DELAY_CAPTION) != 0) { return 0; }/// 使用 均值平滑if (display_caption("Homogeneous Blur") != 0) { return 0; }
?for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2){blur(src, dst, Size(i, i), Point(-1, -1));if (display_dst(DELAY_BLUR) != 0) { return 0; }}
?/// 使用高斯平滑if (display_caption("Gaussian Blur") != 0) { return 0; }
?for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2){GaussianBlur(src, dst, Size(i, i), 0, 0);if (display_dst(DELAY_BLUR) != 0) { return 0; }}
?/// 使用中值平滑if (display_caption("Median Blur") != 0) { return 0; }
?for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2){medianBlur(src, dst, i);if (display_dst(DELAY_BLUR) != 0) { return 0; }}
?/// 使用雙邊平滑if (display_caption("Bilateral Blur") != 0) { return 0; }
?for (int i = 1; i < MAX_KERNEL_LENGTH; i = i + 2){bilateralFilter(src, dst, i, i * 2, i / 2);if (display_dst(DELAY_BLUR) != 0) { return 0; }}
?/// 等待用戶輸入display_caption("End: Press a key!");
?waitKey(0);return 0;
}
?
int display_caption(char* caption)
{dst = Mat::zeros(src.size(), src.type());putText(dst, caption,Point(src.cols / 4, src.rows / 2),CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 255, 255));
?imshow(window_name, dst);int c = waitKey(DELAY_CAPTION);if (c >= 0) { return -1; }return 0;
}
?
int display_dst(int delay)
{imshow(window_name, dst);int c = waitKey(delay);if (c >= 0) { return -1; }return 0;
}
createTrackbar 的使用
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<iostream>
?
using namespace std;
using namespace cv;
//拖動(dòng)條回調(diào)函數(shù)
void onChangeTrackBar(int pos, void *data)
{//強(qiáng)制類型轉(zhuǎn)換Mat srcImage = *(cv::Mat*)(data);Mat dstImage;//根據(jù)拖動(dòng)條的值對(duì)傳入圖像進(jìn)行二值化threshold(srcImage, dstImage, pos, 255, 0);imshow("threshold", dstImage);
}
int main()
{//讀取圖像Mat srcImage = imread("C:\\Users\\guoqi\\Desktop\\ch7\\1.jpg", IMREAD_UNCHANGED);if (!srcImage.data) {cout << "read failed" << endl;system("pause");return -1;}//原圖像轉(zhuǎn)換灰度圖Mat srcGray;cvtColor(srcImage, srcGray, CV_RGB2GRAY);namedWindow("threshold");//創(chuàng)建窗口imshow("threshold", srcGray);//創(chuàng)建滑動(dòng)條createTrackbarcreateTrackbar("pos", "threshold",0, 255, onChangeTrackBar, &srcImage);waitKey(0);return 0;
}
?
3效果
?
part two 腐蝕與膨脹(Eroding and Dilating)
1原理
1.1形態(tài)學(xué)操作?
-
簡(jiǎn)單講,形態(tài)學(xué)操作就是基于形狀的一系列圖像處理操作。將 結(jié)構(gòu)元素 作用于輸入圖像來(lái)產(chǎn)生輸出圖像。
-
最基本的形態(tài)學(xué)操作有二:腐蝕與膨脹(Erosion 與 Dilation)。 他們的運(yùn)用廣泛:
-
消除噪聲
-
分割(isolate)獨(dú)立的圖像元素,以及連接(join)相鄰的元素。
-
尋找圖像中的明顯的極大值區(qū)域或極小值區(qū)域。
-
-
通過(guò)以下圖像,我們簡(jiǎn)要來(lái)討論一下膨脹與腐蝕操作(譯者注:注意這張圖像中的字母為黑色,背景為白色,而不是一般意義的背景為黑色,前景為白色):
-
?
?
1.2膨脹
-
此操作將圖像 與任意形狀的內(nèi)核 (),通常為正方形或圓形,進(jìn)行卷積。
-
內(nèi)核 有一個(gè)可定義的 錨點(diǎn), 通常定義為內(nèi)核中心點(diǎn)。
-
進(jìn)行膨脹操作時(shí),將內(nèi)核 劃過(guò)圖像,將內(nèi)核 覆蓋區(qū)域的最大相素值提取,并代替錨點(diǎn)位置的相素。顯然,這一最大化操作將會(huì)導(dǎo)致圖像中的亮區(qū)開始”擴(kuò)展” (因此有了術(shù)語(yǔ)膨脹 dilation )。對(duì)上圖采用膨脹操作我們得到:
背景(白色)膨脹,而黑色字母縮小了。
1.3腐蝕
-
腐蝕在形態(tài)學(xué)操作家族里是膨脹操作的孿生姐妹。它提取的是內(nèi)核覆蓋下的相素最小值。
-
進(jìn)行腐蝕操作時(shí),將內(nèi)核 劃過(guò)圖像,將內(nèi)核 覆蓋區(qū)域的最小相素值提取,并代替錨點(diǎn)位置的相素。
-
以與膨脹相同的圖像作為樣本,我們使用腐蝕操作。從下面的結(jié)果圖我們看到亮區(qū)(背景)變細(xì),而黑色區(qū)域(字母)則變大了。
-
2代碼
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "highgui.h"
#include <stdlib.h>
#include <stdio.h>
?
using namespace cv;
?
/// 全局變量
Mat src, erosion_dst, dilation_dst;
?
int erosion_elem = 0;
int erosion_size = 0;
int dilation_elem = 0;
int dilation_size = 0;
int const max_elem = 2;
int const max_kernel_size = 21;
?
/** Function Headers */
void Erosion(int, void*);
void Dilation(int, void*);
?
/** @function main */
int main(int argc, char** argv)
{/// Load 圖像src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\1.jpg", IMREAD_UNCHANGED);
?if (!src.data){return -1;}
?/// 創(chuàng)建顯示窗口namedWindow("Erosion Demo", CV_WINDOW_AUTOSIZE);namedWindow("Dilation Demo", CV_WINDOW_AUTOSIZE);cvMoveWindow("Dilation Demo", src.cols, 0);
?/// 創(chuàng)建腐蝕 TrackbarcreateTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo",&erosion_elem, max_elem,Erosion);
?createTrackbar("Kernel size:\n 2n +1", "Erosion Demo",&erosion_size, max_kernel_size,Erosion);
?/// 創(chuàng)建膨脹 TrackbarcreateTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo",&dilation_elem, max_elem,Dilation);
?createTrackbar("Kernel size:\n 2n +1", "Dilation Demo",&dilation_size, max_kernel_size,Dilation);
?/// Default startErosion(0, 0);Dilation(0, 0);
?waitKey(0);return 0;
}
?
/** @function Erosion */
void Erosion(int, void*)
{int erosion_type;if (erosion_elem == 0) { erosion_type = MORPH_RECT; }else if (erosion_elem == 1) { erosion_type = MORPH_CROSS; }else if (erosion_elem == 2) { erosion_type = MORPH_ELLIPSE; }
?Mat element = getStructuringElement(erosion_type,Size(2 * erosion_size + 1, 2 * erosion_size + 1),Point(erosion_size, erosion_size));
?/// 腐蝕操作erode(src, erosion_dst, element);imshow("Erosion Demo", erosion_dst);
}
?
/** @function Dilation */
void Dilation(int, void*)
{int dilation_type;if (dilation_elem == 0) { dilation_type = MORPH_RECT; }else if (dilation_elem == 1) { dilation_type = MORPH_CROSS; }else if (dilation_elem == 2) { dilation_type = MORPH_ELLIPSE; }
?Mat element = getStructuringElement(dilation_type,Size(2 * dilation_size + 1, 2 * dilation_size + 1),Point(dilation_size, dilation_size));///膨脹操作dilate(src, dilation_dst, element);imshow("Dilation Demo", dilation_dst);
}
?
3運(yùn)行結(jié)果
?
part three更多形態(tài)學(xué)變換?
這篇文檔將會(huì)簡(jiǎn)要介紹OpenCV提供的5種高級(jí)形態(tài)學(xué)操作:
1 原理
1.1開運(yùn)算 (Opening)
開運(yùn)算是通過(guò)先對(duì)圖像先腐蝕再膨脹實(shí)現(xiàn)的。
-
能夠排除小團(tuán)塊物體(假設(shè)物體較背景明亮)
-
請(qǐng)看下面,左圖是原圖像,右圖是采用開運(yùn)算轉(zhuǎn)換之后的結(jié)果圖。 觀察發(fā)現(xiàn)字母拐彎處的白色空間消失。
1.2閉運(yùn)算(Closing)
-
閉運(yùn)算是通過(guò)先對(duì)圖像
先膨脹再腐蝕實(shí)現(xiàn)的。 -
能夠排除小型黑洞(黑色區(qū)域)。
1.3形態(tài)梯度(Morphological Gradient)
-
膨脹圖與腐蝕圖之差
-
能夠保留物體的邊緣輪廓,如下所示:
1.4頂帽(Top Hat)
-
原圖像與開運(yùn)算結(jié)果圖之差
1.5黑帽(Black Hat)
-
閉運(yùn)算結(jié)果圖與原圖像之差
2 代碼
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
?
using namespace cv;
?
/// 全局變量
Mat src, dst;
?
int morph_elem = 0;
int morph_size = 0;
int morph_operator = 0;
int const max_operator = 4;
int const max_elem = 2;
int const max_kernel_size = 21;
?
char* window_name = "Morphology Transformations Demo";
?
/** 回調(diào)函數(shù)申明 */
void Morphology_Operations(int, void*);
?
/** @函數(shù) main */
int main(int argc, char** argv)
{/// 裝載圖像src = imread("C:\\Users\\guoqi\\Desktop\\ch7\\7.jpg", IMREAD_UNCHANGED);
?if (!src.data){return -1;}
?/// 創(chuàng)建顯示窗口namedWindow(window_name, CV_WINDOW_AUTOSIZE);
?/// 創(chuàng)建選擇具體操作的 trackbarcreateTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat", window_name, &morph_operator, max_operator, Morphology_Operations);
?/// 創(chuàng)建選擇內(nèi)核形狀的 trackbarcreateTrackbar("Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,&morph_elem, max_elem,Morphology_Operations);
?/// 創(chuàng)建選擇內(nèi)核大小的 trackbarcreateTrackbar("Kernel size:\n 2n +1", window_name,&morph_size, max_kernel_size,Morphology_Operations);
?/// 啟動(dòng)使用默認(rèn)值Morphology_Operations(0, 0);
?waitKey(0);return 0;
}
?
/**
* @函數(shù) Morphology_Operations
*/
void Morphology_Operations(int, void*)
{// 由于 MORPH_X的取值范圍是: 2,3,4,5 和 6int operation = morph_operator + 2;
?Mat element = getStructuringElement(morph_elem, Size(2 * morph_size + 1, 2 * morph_size + 1), Point(morph_size, morph_size));
?/// 運(yùn)行指定形態(tài)學(xué)操作morphologyEx(src, dst, operation, element);imshow(window_name, dst);
}
看一下程序的總體流程:
-
裝載圖像
-
創(chuàng)建顯示形態(tài)學(xué)操作的窗口
-
創(chuàng)建3個(gè)trackbar獲取用戶參數(shù):
-
第一個(gè)trackbar “Operator” 返回用戶選擇的形態(tài)學(xué)操作類型 (morph_operator).
createTrackbar("Operator:\n 0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat",window_name, &morph_operator, max_operator,Morphology_Operations ); -
第二個(gè)trackbar “Element” 返回 morph_elem, 指定內(nèi)核形狀:
createTrackbar( "Element:\n 0: Rect - 1: Cross - 2: Ellipse", window_name,&morph_elem, max_elem,Morphology_Operations ); -
第三個(gè)trackbar “Kernel Size” 返回內(nèi)核大小(morph_size)
createTrackbar( "Kernel size:\n 2n +1", window_name,&morph_size, max_kernel_size,Morphology_Operations );
-
-
每當(dāng)任一標(biāo)尺被移動(dòng), 用戶函數(shù) Morphology_Operations 就會(huì)被調(diào)用,該函數(shù)獲取trackbar的當(dāng)前值運(yùn)行指定操作并更新顯示結(jié)果圖像。
/*** @函數(shù) Morphology_Operations*/ void Morphology_Operations( int, void* ) {// 由于 MORPH_X的取值范圍是: 2,3,4,5 和 6int operation = morph_operator + 2; ?Mat element = getStructuringElement( morph_elem, Size( 2*morph_size + 1, 2*morph_size+1 ), Point( morph_size, morph_size ) ); ?/// 運(yùn)行指定形態(tài)學(xué)操作morphologyEx( src, dst, operation, element );imshow( window_name, dst );}運(yùn)行形態(tài)學(xué)操作的核心函數(shù)是 morphologyEx 。在本例中,我們使用了4個(gè)參數(shù)(其余使用默認(rèn)值):
-
src : 原 (輸入) 圖像
-
dst: 輸出圖像
-
operation
: 需要運(yùn)行的形態(tài)學(xué)操作。 我們有5個(gè)選項(xiàng):
-
***Opening*: MORPH_OPEN : 2** -
***Closing*: MORPH_CLOSE: 3** -
***Gradient*: MORPH_GRADIENT: 4** -
***Top Hat*: MORPH_TOPHAT: 5** -
***Black Hat*: MORPH_BLACKHAT: 6**
-
你可以看到, 它們的取值范圍是 <2-6>, 因此我們要將從tracker獲取的值增加(+2):
int operation = morph_operator + 2;-
element: 內(nèi)核,可以使用函數(shù):get_structuring_element:getStructuringElement <> 自定義。
-
3 結(jié)果
這里是顯示窗口的兩個(gè)截圖。第一幅圖顯示了使用交錯(cuò)內(nèi)核和?開運(yùn)算?之后的結(jié)果, 第二幅圖顯示了使用橢圓內(nèi)核和?黑帽?之后的結(jié)果。
橢圓內(nèi)核和 黑帽 之后的結(jié)果。
?
總結(jié)
以上是生活随笔為你收集整理的OpenCV 【十八】图像平滑处理/腐蚀与膨胀(Eroding and Dilating)/开闭运算,形态梯度,顶帽,黑帽运算的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 黄山风景区汤口南大门到屯溪老街怎么去
- 下一篇: OpenCV 【十九】图像金字塔/基本的