OpenCV 【十九】图像金字塔/基本的阈值操作/实现自己的线性滤波器
目錄
1.part one 圖像金字塔
1.1原理
1.1.1圖像金字塔
1.1.2高斯金字塔
1.2代碼
1.3運行結(jié)果
2.part two 基本的閾值操作?
2.1原理
2.1.1閾值化的類型:
2.1.2閾值類型1:二進制閾值化
2.1.3閾值類型2:反二進制閾值化
2.1.4閾值類型3:截斷閾值化
2.1.5閾值類型4:閾值化為0
2.1.6閾值類型5:反閾值化為0
2.2代碼
2.3運行結(jié)果
3.part three 實現(xiàn)自己的線性濾波器
3.1原理
3.1.1卷積
3.1.2核是什么?
3.1.3如何用核實現(xiàn)卷積?
3.2代碼
3.3運行結(jié)果
?
1.part one 圖像金字塔
1.1原理
-
當我們需要將圖像轉(zhuǎn)換到另一個尺寸的時候, 有兩種可能:
-
放大 圖像 或者
-
縮小 圖像。
-
-
盡管OpenCV 幾何變換 部分提供了一個真正意義上的圖像縮放函數(shù)(resize, 在以后的教程中會學到),不過在本篇我們首先學習一下使用 圖像金字塔 來做圖像縮放, 圖像金字塔是視覺運用中廣泛采用的一項技術(shù)。
1.1.1圖像金字塔
-
一個圖像金字塔是一系列圖像的集合 - 所有圖像來源于同一張原始圖像 - 通過梯次向下采樣獲得,直到達到某個終止條件才停止采樣。
-
有兩種類型的圖像金字塔常常出現(xiàn)在文獻和應用中:
-
高斯金字塔(Gaussian pyramid): 用來向下采樣
-
拉普拉斯金字塔(Laplacian pyramid): 用來從金字塔低層圖像重建上層未采樣圖像
-
1.1.2高斯金字塔
-
想想金字塔為一層一層的圖像,層級越高,圖像越小。
?
-
每一層都按從下到上的次序編號, 層級 (表示為 尺寸小于層級 ())。
-
為了獲取層級為 的金字塔圖像,我們采用如下方法:
-
將 與高斯內(nèi)核卷積:
-
將所有偶數(shù)行和列去除。
-
-
顯而易見,結(jié)果圖像只有原圖的四分之一。通過對輸入圖像 (原始圖像) 不停迭代以上步驟就會得到整個金字塔。
-
以上過程描述了對圖像的向下采樣,如果將圖像變大呢?:
-
首先,將圖像在每個方向擴大為原來的兩倍,新增的行和列以0填充()
-
使用先前同樣的內(nèi)核(乘以4)與放大后的圖像卷積,獲得 “新增像素” 的近似值。
-
-
這兩個步驟(向下和向上采樣) 分別通過OpenCV函數(shù) pyrUp 和 pyrDown 實現(xiàn), 我們將會在下面的示例中演示如何使用這兩個函數(shù)。
?
1.2代碼
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
?
using namespace cv;
?
/// 全局變量
Mat src, dst, tmp;
char* window_name = "Pyramids Demo";
?
?
/*** @函數(shù) main*/
int main( int argc, char** argv )
{/// 指示說明printf( "\n Zoom In-Out demo \n " );printf( "------------------ \n" );printf( " * [u] -> Zoom in \n" );printf( " * [d] -> Zoom out \n" );printf( " * [ESC] -> Close program \n \n" );
?/// 測試圖像 - 尺寸必須能被 2^{n} 整除src = imread( "../images/chicky_512.jpg" );if( !src.data ){ printf(" No data! -- Exiting the program \n");return -1; }
?tmp = src;dst = tmp;
?/// 創(chuàng)建顯示窗口namedWindow( window_name, CV_WINDOW_AUTOSIZE );imshow( window_name, dst );
?/// 循環(huán)while( true ){int c;c = waitKey(10);
?if( (char)c == 27 ){ break; }if( (char)c == 'u' ){ pyrUp( tmp, dst, Size( tmp.cols*2, tmp.rows*2 ) );printf( "** Zoom In: Image x 2 \n" );}else if( (char)c == 'd' ){ pyrDown( tmp, dst, Size( tmp.cols/2, tmp.rows/2 ) );printf( "** Zoom Out: Image / 2 \n" );}
?imshow( window_name, dst );tmp = dst;}return 0;
}
?
1.3運行結(jié)果
?
?
2.part two 基本的閾值操作?
2.1原理
-
最簡單的圖像分割的方法。
-
應用舉例:從一副圖像中利用閾值分割出我們需要的物體部分(當然這里的物體可以是一部分或者整體)。這樣的圖像分割方法是基于圖像中物體與背景之間的灰度差異,而且此分割屬于像素級的分割。
-
為了從一副圖像中提取出我們需要的部分,應該用圖像中的每一個像素點的灰度值與選取的閾值進行比較,并作出相應的判斷。(注意:閾值的選取依賴于具體的問題。即:物體在不同的圖像中有可能會有不同的灰度值。
-
一旦找到了需要分割的物體的像素點,我們可以對這些像素點設(shè)定一些特定的值來表示。(例如:可以將該物體的像素點的灰度值設(shè)定為:‘0’(黑色),其他的像素點的灰度值為:‘255’(白色);當然像素點的灰度值可以任意,但最好設(shè)定的兩種顏色對比度較強,方便觀察結(jié)果)。
2.1.1閾值化的類型:
-
OpenCV中提供了閾值(threshold)函數(shù): threshold 。
-
這個函數(shù)有5種閾值化類型,在接下來的章節(jié)中將會具體介紹。
-
為了解釋閾值分割的過程,我們來看一個簡單有關(guān)像素灰度的圖片,該圖如下。該圖中的藍色水平線代表著具體的一個閾值。
2.1.2閾值類型1:二進制閾值化
-
該閾值化類型如下式所示:
-
解釋:在運用該閾值類型的時候,先要選定一個特定的閾值量,比如:125,這樣,新的閾值產(chǎn)生規(guī)則可以解釋為大于125的像素點的灰度值設(shè)定為最大值(如8位灰度值最大為255),灰度值小于125的像素點的灰度值設(shè)定為0。
-
?
2.1.3閾值類型2:反二進制閾值化
-
該閾值類型如下式所示:
-
解釋:該閾值化與二進制閾值化相似,先選定一個特定的灰度值作為閾值,不過最后的設(shè)定值相反。(在8位灰度圖中,例如大于閾值的設(shè)定為0,而小于該閾值的設(shè)定為255)。
2.1.4閾值類型3:截斷閾值化
-
該閾值化類型如下式所示:
-
解釋:同樣首先需要選定一個閾值,圖像中大于該閾值的像素點被設(shè)定為該閾值,小于該閾值的保持不變。(例如:閾值選取為125,那小于125的閾值不改變,大于125的灰度值(230)的像素點就設(shè)定為該閾值)。
2.1.5閾值類型4:閾值化為0
-
該閾值類型如下式所示:
-
解釋:先選定一個閾值,然后對圖像做如下處理:1 像素點的灰度值大于該閾值的不進行任何改變;2 像素點的灰度值小于該閾值的,其灰度值全部變?yōu)?。
2.1.6閾值類型5:反閾值化為0
-
該閾值類型如下式所示:
-
解釋:原理類似于0閾值,但是在對圖像做處理的時候相反,即:像素點的灰度值小于該閾值的不進行任何改變,而大于該閾值的部分,其灰度值全部變?yōu)?。
-
2.2代碼
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>
?
using namespace cv;
?
/// 全局變量定義及賦值
?
int threshold_value = 0;
int threshold_type = 3;;
int const max_value = 255;
int const max_type = 4;
int const max_BINARY_value = 255;
?
Mat src, src_gray, dst;
char* window_name = "Threshold Demo";
?
char* trackbar_type = "Type: \n 0: Binary \n 1: Binary Inverted \n 2: Truncate \n 3: To Zero \n 4: To Zero Inverted";
char* trackbar_value = "Value";
?
/// 自定義函數(shù)聲明
void Threshold_Demo( int, void* );
?
/*** @主函數(shù)*/
int main( int argc, char** argv )
{/// 讀取一副圖片,不改變圖片本身的顏色類型(該讀取方式為DOS運行模式)src = imread( argv[1], 1 );
?/// 將圖片轉(zhuǎn)換成灰度圖片cvtColor( src, src_gray, CV_RGB2GRAY );
?/// 創(chuàng)建一個窗口顯示圖片namedWindow( window_name, CV_WINDOW_AUTOSIZE );
?/// 創(chuàng)建滑動條來控制閾值createTrackbar( trackbar_type,window_name, &threshold_type,max_type, Threshold_Demo );
?createTrackbar( trackbar_value,window_name, &threshold_value,max_value, Threshold_Demo );
?/// 初始化自定義的閾值函數(shù)Threshold_Demo( 0, 0 );
?/// 等待用戶按鍵。如果是ESC健則退出等待過程。while(true){int c;c = waitKey( 20 );if( (char)c == 27 ){ break; }}
?
}
?
?
/*** @自定義的閾值函數(shù)*/
void Threshold_Demo( int, void* )
{/* 0: 二進制閾值1: 反二進制閾值2: 截斷閾值3: 0閾值4: 反0閾值*/
?threshold( src_gray, dst, threshold_value, max_BINARY_value,threshold_type );
?imshow( window_name, dst );
}
?
2.3運行結(jié)果
3.part three 實現(xiàn)自己的線性濾波器
3.1原理
3.1.1卷積
高度概括地說,卷積是在每一個圖像塊與某個算子(核)之間進行的運算。
3.1.2核是什么?
核說白了就是一個固定大小的數(shù)值數(shù)組。該數(shù)組帶有一個 錨點 ,一般位于數(shù)組中央。
3.1.3如何用核實現(xiàn)卷積?
假如你想得到圖像的某個特定位置的卷積值,可用下列方法計算:
-
將核的錨點放在該特定位置的像素上,同時,核內(nèi)的其他值與該像素鄰域的各像素重合;
-
將核內(nèi)各值與相應像素值相乘,并將乘積相加;
-
將所得結(jié)果放到與錨點對應的像素上;
-
對圖像所有像素重復上述過程。
用公式表示上述過程如下:
幸運的是,我們不必自己去實現(xiàn)這些運算,OpenCV為我們提供了函數(shù) filter2D 。
3.2代碼
-
載入一幅圖像
-
對圖像執(zhí)行 歸一化塊濾波器 。舉例來說,如果該濾波器核的大小為 ,則它會像下面這樣:
程序?qū)?zhí)行核的大小分別為3、5、7、9、11的濾波器運算。
-
該濾波器每一種核的輸出將在屏幕上顯示500毫秒
#include "opencv2/imgproc/imgproc.hpp" #include "opencv2/highgui/highgui.hpp" #include <stdlib.h> #include <stdio.h> ? using namespace cv; ? /** @函數(shù)main */ int main ( int argc, char** argv ) {/// 聲明變量Mat src, dst; ?Mat kernel;Point anchor;double delta;int ddepth;int kernel_size;char* window_name = "filter2D Demo"; ?int c; ?/// 載入圖像src = imread( argv[1] ); ?if( !src.data ){ return -1; } ?/// 創(chuàng)建窗口namedWindow( window_name, CV_WINDOW_AUTOSIZE ); ?/// 初始化濾波器參數(shù)anchor = Point( -1, -1 );delta = 0;ddepth = -1; ?/// 循環(huán) - 每隔0.5秒,用一個不同的核來對圖像進行濾波int ind = 0;while( true ){c = waitKey(500);/// 按'ESC'可退出程序if( (char)c == 27 ){ break; } ?/// 更新歸一化塊濾波器的核大小kernel_size = 3 + 2*( ind%5 );kernel = Mat::ones( kernel_size, kernel_size, CV_32F )/ (float)(kernel_size*kernel_size); ?/// 使用濾波器filter2D(src, dst, ddepth , kernel, anchor, delta, BORDER_DEFAULT );imshow( window_name, dst );ind++;} ?return 0; }3.3運行結(jié)果
?
?
3.3運行結(jié)果
總結(jié)
以上是生活随笔為你收集整理的OpenCV 【十九】图像金字塔/基本的阈值操作/实现自己的线性滤波器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 张艺谋作品《影》,你认为其结局是啥意思
- 下一篇: 爱丽丝一套【魔力】换女帝一套亏吗?