OpenCV影象處理教程C++(二十二)基於距離變換與分水嶺的影象分割
影象分割是影象處理最重要的處理手段之一
影象分割的目標是將影象中畫素根據一定的規則分為若干個cluster集合每個集合包括一類畫素
根據演算法分為監督學習和無監督學習,影象分割的演算法多數都是無監督學習-KMenas
距離變換常見演算法有兩種
- 不斷膨脹/ 腐蝕得到
- 基於倒角距離
分水嶺變換常見的演算法
基於浸泡理論實現,假設顏色資料為一個個山頭,在山底不停加水,直到各大山頭之間形成了明顯的分水線
API:
watershed ( // 分水嶺變換
InputArray image,
InputOutputArray markers
)
distanceTransform ( // 距離變換
InputArray src, // 輸入的影象,一般為二值影象
OutputArray dst, // 輸出8位或者32位的浮點數,單一通道,大小與輸入影象一致
OutputArray labels, // 輸出 2D 的標籤(離散Voronoi(維諾)圖),型別為 CV_32SC1
,相同距離的算做同一個 label ,算出總共由多少個 labels
int distanceType, // 所用的求解距離的型別
CV_DIST_L1 distance = |x1-x2| + |y1-y2|
CV_DIST_L2 distance = sqrt((x1-x2)^2 + (y1-y2)^2) 歐幾里得距離
CV_DIST_C distance = max(|x1-x2|, |y1-y2|)
int maskSize, // 最新的支援5x5,推薦3x3
int labelType=DIST_LABEL_CCOMP // Type of the label array to build, see cv::DistanceTransformLabelTypes
)
步驟:
- 將白色背景變成黑色-目的是為後面的變換做準備
- 使用filter2D與拉普拉斯運算元實現影象對比度提高,sharp銳化
- 轉灰度轉為二值影象通過threshold
- 距離變換
- 對距離變換結果進行歸一化到0-1之間
- 使用閾值再次二值化得到標記
- 腐蝕得到每個peak-erode
- 發現輪廓-findContours
- 繪製輪廓-drawcontours
- 分水嶺變換watershed
- 對每個分割區域著色輸出結果
程式碼:
#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string>
#include<fstream>
using namespace cv;
using namespace std;
int main() {
Mat src;
src = imread("C:\\Users\\Administrator\\Desktop\\pic\\10.jpg");
imshow("input", src);
//printf("1 depth=%d, type=%d, channels=%d\n", src.depth(), src.type(), src.channels());
//將白色背景變成黑色
for (int row = 0; row < src.rows; row++) {
for (int col = 0; col < src.cols; col++) {
if (src.at<Vec3b>(row, col) == Vec3b(255, 255, 255)) {
src.at<Vec3b>(row, col)[0] = 0;
src.at<Vec3b>(row, col)[1] = 0;
src.at<Vec3b>(row, col)[2] = 0;
}
}
}
imshow("blacksrc", src);
//銳化
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);//拉普拉斯運算元
Mat lapulasimg;
Mat sharpimg = src;
//printf("2 depth=%d, type=%d, channels=%d\n", sharpimg.depth(), sharpimg.type(), sharpimg.channels());
filter2D(src, lapulasimg, CV_32F, kernel);// 這裡計算的顏色資料有可能是負值,所以深度傳 CV_32F, 不要傳 -1,原圖的深度是 CV_8U,不能儲存負值
src.convertTo(sharpimg, CV_32F); // mat.type 由 CV_8UC3 轉換為 CV_32FC3 ,為了下面的減法計算
Mat resultimg = sharpimg - lapulasimg;
lapulasimg.convertTo(lapulasimg, CV_8UC3);
resultimg.convertTo(resultimg, CV_8UC3);
imshow("ruihua", resultimg);
//轉為二值
Mat binimg;
cvtColor(resultimg, resultimg, CV_RGB2GRAY);
threshold(resultimg, binimg, 40, 255, THRESH_BINARY);
imshow("binimg", binimg);
//距離變化
Mat disimg;
distanceTransform(binimg, disimg, DIST_L1, 3,5);// CV_32F表示輸出影象的深度,通道數與輸入圖形一致
imshow("disimg", disimg);
//對距離變換結果進行歸一化到0-1之間
normalize(disimg, disimg, 0, 1, NORM_MINMAX);
imshow("normal", disimg);
//使用閾值再次二值化得到標記(即顏色值達到0.4的地方,表示輪廓的邊界,為發現輪廓做準備)
threshold(disimg, disimg, 0.4, 1, THRESH_BINARY);
imshow("erzhiimg", disimg);
//腐蝕得到每個peak - erode
Mat k1 = Mat::zeros(13, 13, CV_8UC1);
erode(disimg, disimg, k1);
imshow("erodeimg", disimg);
//發現輪廓
Mat dist_8u;
disimg.convertTo(dist_8u, CV_8UC1);
imshow("dist_8u*100", dist_8u*100 );//元素放大100倍
vector<vector<Point>>contours;
findContours(dist_8u, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
//繪製輪廓
RNG rng(12345);
Mat show_contours;
src.copyTo(show_contours);
Mat makers = Mat::zeros(src.size(), CV_32SC1);
for (size_t i = 0; i < contours.size(); i++) {
if (contours[i].size() <= 2)
continue;//過濾排除點數不夠的輪廓,最終影象分割效果更好
drawContours(makers, contours, i, Scalar::all(i + 1), -1);//thickness=-1表示填充輪廓
if (i == 1) {//腐蝕的mat尺寸為3*3時下標1的輪廓兩個點,在上面已經排除
printf("contours[1][0].x=%d, contours[1][0].y=%d, contours[1][1].x=%d,contours[1][1].y=%d\n",
contours[1][0].x, contours[1][0].y, contours[1][1].x, contours[1][1].y);
circle(show_contours, contours[1][0], 5, Scalar(0, 0, 255), -1);
circle(show_contours, contours[1][1], 5, Scalar(0, 0, 0), -1);
}
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
drawContours(show_contours, contours, i, color, -1);
}
//建立標記,標記的位置如果要分割的影象塊上會影響分割的效果,若果不建立,分水嶺變換會無效
circle(makers, Point(5, 5), 3, Scalar(255, 255, 255), -1);
imshow("maker*1000", makers * 1000);
imshow("show_contours", show_contours);
//分水嶺變換,將繪製的輪廓區域的顏色資料蔓延到各輪廓所在的分水嶺,這樣影象分割已完成,後續不同著色顯示
watershed(src, makers);
imshow("waterimg", makers * 1000);
Mat mark = Mat::zeros(makers.size(), CV_8UC1);
makers.convertTo(mark, CV_8UC1);
//bitwise_not(mark, mark, Mat()); // 顏色反差
imshow("markimg", mark);
// 為每個輪廓生成隨機顏色
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++) {
int r = theRNG().uniform(0, 255);
int g = theRNG().uniform(0, 255);
int b = theRNG().uniform(0, 255);
colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
}
// fill with color and display final result
Mat dst = Mat::zeros(makers.size(), CV_8UC3);
for (int row = 0; row < makers.rows; row++) {
for (int col = 0; col < makers.cols; col++) {
int index = makers.at<int>(row, col); // 對應上面傳的 Scalar::all(i + 1), -1)
if (index > 0 && index <= static_cast<int>(contours.size())) { // 給各輪廓上不同色
dst.at<Vec3b>(row, col) = colors[index - 1]; // 因為上面傳的是 Scalar::all(i + 1), -1) 所以要減1
}
else {
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0); // 輪廓之外全部黑色
}
}
}
imshow("Final Result", dst);
waitKey(0);
}
結果:
相關推薦
OpenCV影象處理教程C++(二十二)基於距離變換與分水嶺的影象分割
影象分割是影象處理最重要的處理手段之一 影象分割的目標是將影象中畫素根據一定的規則分為若干個cluster集合每個集合包括一類畫素 根據演算法分為監督學習和無監督學習,影象分割的演算法多數都是無監督學習-KMenas 距離變換常見演算法有兩種 - 不斷膨
(31)基於距離變換與分水嶺的影象分割
/* *基於距離變換與分水嶺的影象分割 *什麼是影象分割(Image Segmentation) 1:影象分割(Image Segmentation)是影象處理最重要的處理手段之一 2:影象分割的目標是將影象中畫素根據一定的規則分為若干(N)個cluster集合,
OpenCV之影象處理(三十四) 基於距離變換與分水嶺的影象分割
影象分割(Image Segmentation)是影象處理最重要的處理手段之一 影象分割的目標是將影象中畫素根據一定的規則分為若干(N)個cluster集合,每個集合包含一類畫素。 根據演算法分為監督學習方法和無監督學習方法,影象分割的演算法多數
OpenCv-C++-基於距離變換與分水嶺的影象分割
在這裡,先感謝賈志剛老師的教學,我今天學習了影象分水嶺分割,什麼是影象分割呢?借用賈志剛老師的課件,如下圖所示: 其實大致就是將下面圖1變成圖2的樣子: 圖1: 圖2: 或: 具體操作有什麼步驟?看下圖: 下面附上程式碼(具體解釋程式碼已註釋): #include
影象處理(二十一)基於資料驅動的人臉卡通動畫生成-Siggraph Asia 2014
基於資料驅動的人臉卡通動畫生成 作者:hjimce 在現實生活中,我們經常會去評價一個人,長得是否漂亮、是不是帥哥美女,然而如何用五官的資料去評價一個人是否長得五官比例協調,我們卻很難說出來,也就是
圖像處理(二十一)基於數據驅動的人臉卡通動畫生成-Siggraph Asia 2014
ssi 原來 大於 搜索 nbsp details 一起 fontsize man http://blog.csdn.net/garfielder007/article/details/50582018 在現實生活中,我們經常會去評價一個人,長得是否漂亮、是不是帥哥美女,然
深度學習(二十八)基於多尺度深度網路的單幅影象深度估計
基於多尺度深度網路的單幅影象深度估計作者:hjimce一、相關理論本篇博文主要講解來自2014年NIPS上的一篇paper:《Depth Map Prediction from a Single Image using a Multi-Scale Deep Network》,
OpenCV學習筆記(二十六)——小試SVM演算法ml OpenCV學習筆記(二十七)——基於級聯分類器的目標檢測objdect OpenCV學習筆記(二十八)——光流法對運動目標跟蹤Video Ope
OpenCV學習筆記(二十六)——小試SVM演算法ml 總感覺自己停留在碼農的初級階段,要想更上一層,就得靜下心來,好好研究一下演算法的東西。OpenCV作為一個計算機視覺的開源庫,肯定不會只停留在數字影象處理的初級階段,我也得加油,深入研究它的演算法庫。就從ml入手
C++(三十二) — 常物件、常成員變數、常成員函式
常量:對於既需要共享、又需要防止改變的資料。在程式執行期間不可改變。 1、常物件 資料成員值在物件的整個生存期內不能改變。在定義時必須初始化,而且不能被更新。 常物件,只能呼叫常成員函式,保證常物件的資料成員不被改變。 class point { public
(二十六)成員變量與局部變量
show run 作用 bench 變量 被垃圾回收 [] 局部變量 sha /* *局部變量和成員變量的區別: * 1.局部變量沒有默認值,成員變量有默認值 * 2.局部變量再棧中開辟內存,成員變量再堆中開辟內存 * 3.局部變量作用範圍有限旨在定義的環境中,成員
通證經濟大局觀(二十一):價值生產率與供給、需求
價值是個主觀的東西,一個東西對你有效用,你才覺得有價值。對應於供給和需求來說,那就是有人用,也就是有需求的供給才是有價值的供給。 傳統經濟裡的物品和勞務大多帶有“原子”屬性,你用了別人就沒法用,或者是你用的時候別人就沒辦法用,比如一個蘋果,你吃了別人就沒辦法吃;一個座位你坐了,別人就沒辦法坐。所
TiDB 原始碼閱讀系列文章(二十一)基於規則的優化 II
在 TiDB 原始碼閱讀系列文章(七)基於規則的優化 一文中,我們介紹了幾種 TiDB 中的邏輯優化規則,包括列剪裁,最大最小消除,投影消除,謂詞下推和構建節點屬性,本篇將繼續介紹更多的優化規則:聚合消除、外連線消除和子查詢優化。 聚合消除 聚合消除會檢查 SQL 查詢中 Group By 語句所使用的列是否
Opencv影象處理---基於距離變換和分水嶺演算法的影象分割
程式碼 #include <opencv2/opencv.hpp> #include <iostream> using namespace std; using namespace cv; int main(int, char** argv) {
數字影象處理MATLAB實現(第2版)岡薩雷斯 書中程式碼-2.1 intrans函式
持續更新。。。 function g = intrans(f, method,varargin) %INTRANS Performs intensity (gray-level) transformations. % G = INTRANS(F, 'n
模式識別(Pattern Recognition)學習筆記(二十七)-- 基於樹搜尋演算法的快速近鄰法
近鄰法中計算距離需要遍歷,帶來很大的計算量和儲存量,為了改善這兩方面的效能,有人提出採用分枝界定演算法(Branch-Bound Algorithm)來改進近鄰法,主要分為兩個階段:1)利用人工劃分或K-means聚類演算法或其他動態聚類演算法將樣本集X劃分
無人駕駛汽車系統入門(二十一)——基於Frenet優化軌跡的無人車動作規劃方法
動作規劃動作在無人車規劃模組的最底層,它負責根據當前配置和目標配置生成一序列的動作,我們前面討論的三次樣條插值實際上只是一個簡單的路徑,而非我們最終能夠執行的軌跡,本文介紹一種基於Frenet座標系的優化軌跡動作規劃方法,該方法在高速情況下的高階車道保持和
影象處理基礎知識(持續更新中)
本文由本文由 @DavidHan@DavidHan出品,轉載請注明出處出品,轉載請注明出處 文章鏈接:文章鏈接:http://blog.csdn.net/David_Han008/article/details/78883641 前言 最近
小波影象處理 —— 奇異點(不連續點)檢測
動態系統(dynamic system)中的訊號常常表現出瞬時(transient)急劇的變化,一般為幅度的突然跳躍或一階導二階導數值的尖銳變化。傅立葉分析通常無法檢測出這種瞬時的變化,小波分析卻可
Android產品研發(二十四)-->記憶體洩露場景與檢測
上一篇文章中本文我們講解了一個Android產品研發中可能會碰到的一個問題:如何在App中儲存靜態祕鑰以及保證其安全性。許多的移動app需要在app端儲存一些靜態字串常量,其可能是靜態祕鑰、第三方appId等。在儲存這些字串常量的時候就涉及到了如何保證祕鑰的
ElasticSearch最佳入門實踐(五十九)基於scoll技術滾動搜尋大量資料
如果一次性要查出來比如10萬條資料,那麼效能會很差,此時一般會採取用scoll滾動查詢,一批一批的查,直到所有資料都查詢完處理完 使用scoll滾動搜尋,可以先搜尋一批資料,然後下次再搜尋一批資料,以此類推,直到搜尋出全部的資料來 scoll搜尋會在第一次搜尋的