使用OpenCV獲取圖片連通域數量,並用不同顏色標記函
阿新 • • 發佈:2020-06-05
一,原圖和效果圖
二,程式碼
//#########################產生隨機顏色######################### cv::Scalar icvprGetRandomColor() { uchar r = 255 * (rand() / (1.0 + RAND_MAX)); uchar g = 255 * (rand() / (1.0 + RAND_MAX)); uchar b = 255 * (rand() / (1.0 + RAND_MAX)); return cv::Scalar(b,g,r); } //#########################產生隨機顏色######################### //########################種子填充法)######################### void ConnectedCountBySeedFill(const cv::Mat& _binImg,cv::Mat& _lableImg,int &iConnectedAreaCount) { //拓寬1個畫素的原因是:如果連通域在邊緣,執行此函式會異常崩潰,所以需要在周圍加一圈0值,確保連通域不在邊上 //==========影象周圍拓寬1個畫素============================================ int top,bottom; //【新增邊界後的影象尺寸】 int leftImage,rightImage; int borderType = BORDER_CONSTANT; //BORDER_REPLICATE //【初始化引數】 top = (int)(1); bottom = (int)(1); leftImage = (int)(1); rightImage = (int)(1); Mat _binImg2,_binImg3; _binImg.copyTo(_binImg2); //初始化引數value Scalar value(0); //填充值 //建立影象邊界 copyMakeBorder(_binImg2,_binImg3,top,bottom,leftImage,rightImage,borderType,value); //==========影象周圍拓寬1個畫素============================================ // connected component analysis (4-component) // use seed filling algorithm // 1. begin with a foreground pixel and push its foreground neighbors into a stack; // 2. pop the top pixel on the stack and label it with the same label until the stack is empty // // foreground pixel: _binImg(x,y) = 1 // background pixel: _binImg(x,y) = 0 if (_binImg3.empty() || _binImg3.type() != CV_8UC1) { return; } _lableImg.release(); _binImg3.convertTo(_lableImg,CV_32SC1); int icount = 0; int label = 1; // start by 2 int rows = _binImg3.rows - 1; int cols = _binImg3.cols - 1; for (int i = 1; i < rows - 1; i++) { int* data = _lableImg.ptr<int>(i); //取一行資料 for (int j = 1; j < cols - 1; j++) { if (data[j] == 1) //畫素不為0 { std::stack<std::pair<int,int>> neighborPixels; //新建一個棧 neighborPixels.push(std::pair<int,int>(i,j)); // 畫素座標: <i,j> ,以該畫素為起點,尋找連通域 ++label; // 開始一個新標籤,各連通域區別的標誌 while (!neighborPixels.empty()) { // 獲取堆疊中的頂部畫素並使用相同的標籤對其進行標記 std::pair<int,int> curPixel = neighborPixels.top(); int curX = curPixel.first; int curY = curPixel.second; _lableImg.at<int>(curX,curY) = label; //對影象對應位置的點進行標記 // 彈出頂部畫素 (頂部畫素出棧) neighborPixels.pop(); // 加入8鄰域點 if (_lableImg.at<int>(curX,curY - 1) == 1) {// 左點 neighborPixels.push(std::pair<int,int>(curX,curY - 1)); //左邊點入棧 } if (_lableImg.at<int>(curX,curY + 1) == 1) {// 右點 neighborPixels.push(std::pair<int,curY + 1)); //右邊點入棧 } if (_lableImg.at<int>(curX - 1,curY) == 1) {// 上點 neighborPixels.push(std::pair<int,int>(curX - 1,curY)); //上邊點入棧 } if (_lableImg.at<int>(curX + 1,curY) == 1) {// 下點 neighborPixels.push(std::pair<int,int>(curX + 1,curY)); //下邊點入棧 } //===============補充到8連通域====================================================== if (_lableImg.at<int>(curX - 1,curY - 1) == 1) {// 左上點 neighborPixels.push(std::pair<int,curY - 1)); //左上點入棧 } if (_lableImg.at<int>(curX - 1,curY + 1) == 1) {// 右上點 neighborPixels.push(std::pair<int,curY + 1)); //右上點入棧 } if (_lableImg.at<int>(curX + 1,curY - 1) == 1) {// 左下點 neighborPixels.push(std::pair<int,curY - 1)); //左下點入棧 } if (_lableImg.at<int>(curX + 1,curY + 1) == 1) {// 右下點 neighborPixels.push(std::pair<int,curY + 1)); //右下點入棧 } //===============補充到8連通域====================================================== } } } } iConnectedAreaCount = label - 1; //因為label從2開始計數的 int a = 0; } ########################################################### //#############新增顏色##################################### Mat PaintColor(Mat src,int iConnectedAreaCount) { int rows = src.rows; int cols = src.cols; //cv::Scalar(b,r); std::map<int,cv::Scalar> colors; for (int n = 1; n <= iConnectedAreaCount + 1; n++) { colors[n] = icvprGetRandomColor(); //根據不同標誌位產生隨機顏色 cv::Scalar color = colors[n]; int a = color[0]; int b = color[1]; int c = color[2]; int d = 0; } Mat dst2(rows,cols,CV_8UC3); dst2 = cv::Scalar::all(0); for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { int value = src.at<int>(i,j); if (value>1) { cv::Scalar color = colors[value]; int a = color[0]; int b = color[1]; int c = color[2]; dst2.at<Vec3b>(i,j)[0] = color[0]; dst2.at<Vec3b>(i,j)[1] = color[1]; dst2.at<Vec3b>(i,j)[2] = color[2]; } } } return dst2; } //#############新增顏色################################## //########呼叫########################################## Mat binImage = cv::imread("D:\\sxl\\處理圖片\\testImages\\22.jpg",0); threshold(binImage,binImage,50,1,CV_THRESH_BINARY_INV); // 連通域標記 Mat labelImg; int iConnectedAreaCount = 0; //連通域個數 ConnectedCountBySeedFill(binImage,labelImg,iConnectedAreaCount);//針對黑底白字 int a=iConnectedAreaCount; // 顯示結果 Mat dstColor2=PaintColor(labelImg,iConnectedAreaCount); imshow("colorImg",dstColor2); Mat grayImg; labelImg *= 10; labelImg.convertTo(grayImg,CV_8UC1); imshow("labelImg",grayImg); waitKey(0); //########呼叫##########################################
補充知識:Opencv快速獲取連通域
對於ndarray資料中的連通域查詢,opencv提供了介面,非常方便。
import cv2 import numpy as np img = np.array([ [0,255,],[0,0],255],[255,0] ],dtype=np.uint8) num,labels = cv2.connectedComponents(img) labels_dict = {i:[] for i in range(1,num+1)} height,width = img.shape for h in range(height): for w in range(width): if labels[h][w] in labels_dict: labels_dict[labels[h][w]].append([h,w])
cv2.connectedComponents()函式返回查詢到的連通域個數和對應的label。
上面程式碼返回連通域個數為4(包含值為0區域,可通過lables過濾),labels結果如圖所示:
以上這篇使用OpenCV獲取圖片連通域數量,並用不同顏色標記函就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援我們。