OpenCV實(shí)現(xiàn)圖像細(xì)化算法
1.基礎(chǔ)概念
圖像細(xì)化(Image Thinning),一般指二值圖像的骨架化(Image Skeletonization)的一種操作運(yùn)算。細(xì)化是將圖像的線條從多像素寬度減少到單位像素寬度過程的簡(jiǎn)稱,一些文章經(jīng)常將細(xì)化結(jié)果描述為“骨架化”、“中軸轉(zhuǎn)換”和“對(duì)稱軸轉(zhuǎn)換”。
細(xì)化技術(shù)的一個(gè)主要應(yīng)用領(lǐng)域是位圖矢量化的預(yù)處理階段,相關(guān)研究表明,利用細(xì)化技術(shù)生成的位圖的骨架質(zhì)量受到多種因素的影響,其中包括圖像自身的噪聲、線條粗細(xì)不均勻、端點(diǎn)的確定以及線條交叉點(diǎn)選定等,因而對(duì)線劃圖像進(jìn)行細(xì)化從而生成高質(zhì)量骨架的方法進(jìn)行研究具有現(xiàn)實(shí)意義。
根據(jù)算法處理步驟的不同,細(xì)化算法分為迭代細(xì)化算法和非迭代細(xì)化算法。根據(jù)檢查像素方法的不同,迭代細(xì)化算法又分為串行細(xì)化算法和并行細(xì)化算法。
迭代算法:即重復(fù)刪除圖像邊緣滿足一定條件的像素,最終得到單像素寬帶骨架。
迭代方法依據(jù)其檢查像素的方法又可以再分成:
- 串行算法:在串行算法中,通過在每次迭代中用固定的次序檢查像素來判斷是否刪除像素,在第n次迭代中像素p的刪除取決于到執(zhí)行過的所有操作,也就是必須在第(n-1)次迭代結(jié)果和第n次檢測(cè)像素的基礎(chǔ)之上進(jìn)行像素刪除操作;即是否刪除像素在每次迭代的執(zhí)行中是固定順序的,它不僅取決于前次迭代的結(jié)果,也取決于本次迭代中已處理過像素點(diǎn)分布情況。
- 并行算法:在并行算法中,第n次迭代中像素的刪除只取決于(n-1)次迭代后留下的結(jié)果,因此所有像素能在每次迭代中以并行的方式獨(dú)立的被檢測(cè);即像素點(diǎn)刪除與否與像素值圖像中的順序無關(guān),僅取決于前次迭代效果。
2.細(xì)化過程
細(xì)化算法有ZS算法和查表法。ZS細(xì)化算法是一種基于8領(lǐng)域的并行細(xì)化算法,通過對(duì)目標(biāo)像素8領(lǐng)域進(jìn)行分布的算術(shù)邏輯運(yùn)算,來確定該像素是否能刪除。八領(lǐng)域如下圖所示。
細(xì)化判斷依據(jù)為:內(nèi)部點(diǎn)不能刪除、孤立不能刪除、直線端點(diǎn)不能刪除。
ZS細(xì)化過程如下:
第一次迭代,若P1滿足以下四個(gè)條件,說明P1為邊界點(diǎn),可以刪除,將P1值設(shè)為0:
(1)2 小于等于 Pi從i=2到i=9的和 小于等于6
(2)S(P1)=1;
(3)P2×P4×P6=0;
(4)P4×P6×P8=0;
條件(1)中若P2至P9的和在2至6之間,說明P1為邊界點(diǎn)。S(P1)表示目標(biāo)像素P1的8鄰域中,順時(shí)針變化一周像素由0變1的次數(shù)。在目標(biāo)點(diǎn)8鄰域P2-P9的范圍內(nèi),像素值由0變1的次數(shù)只能為1次。條件(2)保證了圖像細(xì)化后的連通性。
第二次迭代中,像素點(diǎn)如果滿足第一次迭代中的條件(1)和(2)及以下條件,則移除該像素點(diǎn):
(5)P2×P4×P8=0;
(6)P2×P6×P8=0;
重復(fù)以上迭代過程,直至處理完所有像素點(diǎn),此時(shí)細(xì)化完成。
查表法中,由于輸入的圖像是一張二值圖,將其歸一化為像素值只有0和1的圖像,然后對(duì)其進(jìn)行卷積操作。具體卷積操作為:將目標(biāo)點(diǎn)的八領(lǐng)域和卷積進(jìn)行點(diǎn)乘,接著將所有值相加即可得表的索引M,下一步用索引值M去找表中對(duì)應(yīng)的值,對(duì)應(yīng)的值為0或1,就把目標(biāo)點(diǎn)的像素值修改為0或1,其中1為不可刪除點(diǎn),0位可刪除點(diǎn)。重復(fù)上述步驟,遍歷完所有像素點(diǎn),對(duì)目標(biāo)點(diǎn)進(jìn)行查表、修改目標(biāo)像素值,最后得到細(xì)化結(jié)果。
3.代碼實(shí)現(xiàn)
#include<iostream> #include <opencv2\opencv.hpp> using namespace std; using namespace cv; //查表法// Mat lookUpTable(Mat& mat, int lut[]) { Mat mat_in; mat.convertTo(mat_in, CV_16UC1); //8 轉(zhuǎn) 16 int MatX = mat_in.rows; int MatY = mat_in.cols; int num = 512; //表的維數(shù)和卷積核中的數(shù)據(jù)有關(guān),小矩陣初始化按行賦值 Mat kern = (Mat_<int>(3, 3) << 1, 8, 64, 2, 16, 128, 4, 32, 256); //卷積核 Mat mat_out = Mat::zeros(MatX, MatY, CV_16UC1); Mat mat_expend = Mat::zeros(MatX + 2, MatY + 2, CV_16UC1); Rect Roi(1, 1, MatY, MatX); //(列,行,列,行) Mat mat_expend_Roi(mat_expend, Roi); //確定擴(kuò)展矩陣的Roi區(qū)域 mat_in.copyTo(mat_expend_Roi); //將傳入矩陣賦給Roi區(qū)域 Mat Mat_conv; //實(shí)用卷積核和和每一個(gè)八鄰域進(jìn)行點(diǎn)乘再相加,其結(jié)果為表的索引,對(duì)應(yīng)值為0能去掉,為1則不能去掉 filter2D(mat_expend, Mat_conv, mat_expend.depth(), kern); //卷積 Mat mat_index = Mat_conv(Rect(1, 1, MatY, MatX)); for (int i = 0; i < MatX; i++) { for (int j = 0; j < MatY; j++) { int matindex = mat_index.at<short>(i, j); if ((matindex < num) && (matindex > 0)) { mat_out.at<short>(i, j) = lut[matindex]; } else if (matindex > num) { mat_out.at<short>(i, j) = lut[num - 1]; } } } return mat_out; } //道路細(xì)化查表法// Mat img_bone(Mat& mat) { // mat 為細(xì)化后的圖像 Mat mat_in = mat; //在數(shù)字圖像處理時(shí),只有單通道、三通道 8bit 和 16bit 無符號(hào)(即CV_16U)的 mat 才能被保存為圖像 mat.convertTo(mat_in, CV_16UC1); int lut_1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; int lut_2[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1 }; Mat mat_bool; threshold(mat_in, mat_bool, 0, 1, THRESH_BINARY); //二值圖像歸一化 Mat mat_out; Mat image_iters; while (true) { mat_out = mat_bool; //查表:水平、垂直 image_iters = lookUpTable(mat_bool, lut_1); mat_bool = lookUpTable(image_iters, lut_2); Mat diff = mat_out != mat_bool; //countNonZero函數(shù)返回灰度值不為0的像素?cái)?shù) bool mat_equal = countNonZero(diff) == 0; //判斷圖像是否全黑 if (mat_equal) { break; } } Mat Matout; mat_bool.convertTo(Matout, CV_8UC1); return Matout; } //主函數(shù) int main() { Mat src_img, src_imgBool; //輸入道路二值圖,參數(shù) 0 是指imread按單通道的方式讀入圖像,即灰白圖像 src_img = imread("......png", 0); //去掉噪,例如過濾很小或很大像素值的圖像點(diǎn) //threshold(src_img, src_imgBool, 0, 255, THRESH_OTSU); //threshold(src_img, src_imgBool, 0, 155, THRESH_OTSU); //imshow("Binary Image", src_imgBool); Mat imgbone = img_bone(src_img); //保存結(jié)果 imwrite("D:\\Desktop\\......\\細(xì)化222.png", imgbone * 255); waitKey(); system("pause"); return 0; }
4.實(shí)驗(yàn)結(jié)果
細(xì)化前
細(xì)化后
到此這篇關(guān)于OpenCV實(shí)現(xiàn)圖像細(xì)化算法的文章就介紹到這了,更多相關(guān)OpenCV 圖像細(xì)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- python-opencv在有噪音的情況下提取圖像的輪廓實(shí)例
- python用opencv批量截取圖像指定區(qū)域的方法
- 基于OpenCV的PHP圖像人臉識(shí)別技術(shù)
- 詳解利用OpenCV提取圖像中的矩形區(qū)域(PPT屏幕等)
- python-opencv獲取二值圖像輪廓及中心點(diǎn)坐標(biāo)的代碼
- Python Opencv實(shí)現(xiàn)圖像輪廓識(shí)別功能
- Python OpenCV處理圖像之圖像像素點(diǎn)操作
- Python OpenCV讀取png圖像轉(zhuǎn)成jpg圖像存儲(chǔ)的方法
- python opencv 圖像拼接的實(shí)現(xiàn)方法
- python opencv判斷圖像是否為空的實(shí)例
相關(guān)文章
C++實(shí)現(xiàn)鼠標(biāo)控制的黑框象棋
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)鼠標(biāo)控制的黑框象棋,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-05-05C++控制權(quán)限關(guān)鍵字protected
這篇文章主要介紹了C++控制權(quán)限關(guān)鍵字protected,protected和private類似,而對(duì)于派生類來說,protected與public類似,下面來一起倆姐更多詳細(xì)內(nèi)容吧,需要的小伙伴可以參考一下2022-01-01C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
這篇文章主要介紹了C語言中計(jì)算正弦的相關(guān)函數(shù)總結(jié),包括正弦和雙曲線正弦以及反正弦的函數(shù),需要的朋友可以參考下2015-08-08