OpenCV 【四】————Watershed Algorithm(图像分割)——分水岭算法的原理及实现
分水嶺算法實現(C++、opencv)
1.作用:
? ? ? ?通常用于分割圖像,主要實現以臨近像素間的相似性作為重要的參考依據,從而將在空間位置上相近并且灰度值相近的像素點互相連接起來構成一個封閉的輪廓,封閉性是分水嶺算法的一個重要特征。 相對于基于閾值的圖像分割,邊緣檢測等都不會考慮像素在空間關系上的相似性和封閉性這一概念,彼此像素間互相獨立,沒有統一性。分水嶺算法較其他分割方法更具有思想性,更符合人眼對圖像的印象。
2.實現:
#include <cmath>
#include <iostream>
#include <memory>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/background_segm.hpp>
#include "opencv2/calib3d/calib3d.hpp"
#include <opencv2/opencv.hpp>
#include <time.h>
#include <thread>
#include <future>
#include <chrono>
#include <vector>
#include "time.h"
#include <string>
#include <fstream>using namespace std;
using namespace cv;Vec3b RandomColor(int value) //生成隨機顏色函數
{value = value % 255; //生成0~255的隨機數RNG rng;int aa = rng.uniform(0, value);int bb = rng.uniform(0, value);int cc = rng.uniform(0, value);return Vec3b(aa, bb, cc);
}int main(int argc, char** argv) {cv::Mat rgb_image = cv::imread("../example/timg.jpg", CV_LOAD_IMAGE_UNCHANGED);cv::Mat rgb_image_blur;GaussianBlur(rgb_image, rgb_image_blur, Size(5, 5), 0, 0);cv::Mat rgb_image_canny;Canny(rgb_image_blur, rgb_image_canny, 10, 120, 3, false);cv::imshow("rgb_roi_binary", rgb_image_canny);vector<vector<Point>>contours;vector<Vec4i>hierarchy;findContours(rgb_image_canny, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE, Point());Mat imageContours = Mat::zeros(rgb_image.size(), CV_8UC1); //輪廓 Mat marks(rgb_image.size(), CV_32S); //Opencv分水嶺第二個矩陣參數marks = Scalar::all(0);int index = 0;int compCount = 0;for (; index >= 0; index = hierarchy[index][0], compCount++){//對marks進行標記,對不同區域的輪廓進行編號,相當于設置注水點,有多少輪廓,就有多少注水點drawContours(marks, contours, index, Scalar::all(compCount + 1), 1, 8, hierarchy);drawContours(imageContours, contours, index, Scalar(255), 1, 8, hierarchy);}//我們來看一下傳入的矩陣marks里是什么東西Mat marksShows;convertScaleAbs(marks, marksShows);imshow("marksShow", marksShows);imshow("輪廓", imageContours);cv::watershed(rgb_image, marks);Mat afterWatershed;convertScaleAbs(marks, afterWatershed);imshow("After Watershed", afterWatershed);//對每一個區域進行顏色填充Mat PerspectiveImage = Mat::zeros(rgb_image.size(), CV_8UC3);for (int i = 0; i < marks.rows; i++){for (int j = 0; j < marks.cols; j++){int index = marks.at<int>(i, j);if (marks.at<int>(i, j) == -1){PerspectiveImage.at<Vec3b>(i, j) = Vec3b(255, 255, 255);}else{PerspectiveImage.at<Vec3b>(i, j) = RandomColor(index);}}}imshow("After ColorFill", PerspectiveImage);//分割并填充顏色的結果跟原始圖像融合Mat wshed;addWeighted(rgb_image, 0.4, PerspectiveImage, 0.6, 0, wshed);imshow("AddWeighted Image", wshed);cv::waitKey(0);return 0;
}
3.效果
4.函數原型
void watershed( InputArray image, InputOutputArray markers );
第一個參數 image,必須是一個8bit 3通道彩色圖像矩陣序列。第二個參數 markers,根據Opencv官方文檔主要可分為以下幾步:?
step 1:圖像灰度化、濾波、Canny邊緣檢測、二值化等一系列戲臺學操作,保證下一步輸入可以正常傳入findContours即可。
參數markers它應該包含不同區域的輪廓,每個輪廓有一個自己唯一的編號,輪廓的定位可以cv::findContours(輸入時二值化圖像或者灰度圖)方法,這個是執行分水嶺之前的要求。?
step2:查找輪廓,并且把輪廓信息按照不同的編號繪制到watershed的第二個入參merkers上,相當于標記注水點。
算法會根據markers傳入的輪廓作為種子(注水點),對圖像上其他的像素點根據分水嶺算法規則進行判斷,并對每個像素點的區域歸屬進行劃定,直到處理完圖像上所有像素點。而區域與區域之間的分界處的值被置為“-1”,以做區分。
step3:watershed分水嶺運算,繪制分割出來的區域,視覺控還可以使用隨機顏色填充,或者跟原始圖像融合以下,以得到更好的顯示效果。
就是說第二個入參markers必須包含了種子點信息。Opencv官方例程中使用鼠標劃線標記,其實就是在定義種子,只不過需要手動操作,而使用findContours可以自動標記種子點。而分水嶺方法完成之后并不會直接生成分割后的圖像,還需要進一步的顯示處理。
5.原理
? ? ? ?將上述三個步驟對應到三維空間中,findContours發現的輪廓值就是分水嶺的“ling”,在三維空間中,在該處滴上一滴水,這滴水會滑向最小值平面,輪廓中間就是最小值點,也即注水點。
實際上,?在真實圖像中,由于噪聲點或者其它干擾因素的存在,使用分水嶺算法常常存在過度分割的現象,這是因為很多很小的局部極值點的存在。為了解決過度分割的問題,可以使用基于標記(mark)圖像的分水嶺算法,就是通過先驗知識,來指導分水嶺算法,以便獲得更好的圖像分段效果。通常的mark圖像,都是在某個區域定義了一些灰度層級,在這個區域的洪水淹沒過程中,水平面都是從定義的高度開始的,這樣可以避免一些很小的噪聲極值區域的分割。
6.參考
【1】?https://docs.opencv.org/3.4.9/d7/d1b/group__imgproc__misc.html#ga3267243e4d3f95165d55a618c65ac6e1
【2】https://blog.csdn.net/dcrmg/article/details/52498440
【3】https://www.cnblogs.com/mikewolf2002/p/3304118.html
總結
以上是生活随笔為你收集整理的OpenCV 【四】————Watershed Algorithm(图像分割)——分水岭算法的原理及实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenCV 【三】————contou
- 下一篇: 烤瓷牙一颗多少钱啊?