1. 程式人生 > >OpenCV影象處理教程C++(二十二)基於距離變換與分水嶺的影象分割

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搜尋會在第一次搜尋的