OPENCV二值化影象內孔洞填充/小區域去除
來源:http://lib.csdn.net/article/opencv/28353
原作者:robberjohn 部落格已刪除了,原始碼下載連結在
http://download.csdn.net/download/robberjohn/8474913
http://blog.csdn.net/robberjohn/article/details/44081571
對於二值化影象,去除孔洞時採用的方法實際上與去除小區域相同,因此完全可以用同一個函式進行。
這兩個功能可以採取區域生長法來實現。須注意,去除小區域時為儲存有用資訊,可採用8鄰域探測,去除孔洞時則4鄰域即可,否則容易洩露,出現靠邊緣的孔洞未去除的情況。
效果(區域面積閾值為700):
原影象:
小面積區域去除:
孔洞填充結果:
原始碼
#include <cv.h> #include <highgui.h> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <vector> using namespace cv; using namespace std; void RemoveSmallRegion(Mat& Src, Mat& Dst, int AreaLimit=50, int CheckMode=1, int NeihborMode=0); int main() { double t = (double)getTickCount(); char* imagePath = "E:\\SVM\\區域性.jpg"; char* OutPath = "E:\\SVM\\區域性_去除孔洞.jpg"; Mat Src = imread(imagePath, CV_LOAD_IMAGE_GRAYSCALE); Mat Dst = Mat::zeros(Src.size(), CV_8UC1); //二值化處理 for(int i = 0; i < Src.rows; ++i) { uchar* iData = Src.ptr<uchar>(i); for(int j = 0; j < Src.cols; ++j) { if(iData[j] == 0 || iData[j]==255) continue; else if (iData[j] < 10) { iData[j] = 0; //cout<<'#'; } else if (iData[j] > 10) { iData[j] = 255; //cout<<'!'; } } } cout<<"Image Binary processed."<<endl; RemoveSmallRegion(Src, Dst, 20, 1, 1); RemoveSmallRegion(Dst, Dst, 20, 0, 0); cout<<"Done!"<<endl; imwrite(OutPath, Dst); t = ((double)getTickCount() - t)/getTickFrequency(); cout<<"Time cost: "<<t<<" sec."<<endl; return 0; } //CheckMode: 0代表去除黑區域,1代表去除白區域; NeihborMode:0代表4鄰域,1代表8鄰域; void RemoveSmallRegion(Mat& Src, Mat& Dst, int AreaLimit, int CheckMode, int NeihborMode) { int RemoveCount=0; //記錄除去的個數 //記錄每個畫素點檢驗狀態的標籤,0代表未檢查,1代表正在檢查,2代表檢查不合格(需要反轉顏色),3代表檢查合格或不需檢查 Mat Pointlabel = Mat::zeros( Src.size(), CV_8UC1 ); if(CheckMode==1) { cout<<"Mode: 去除小區域. "; for(int i = 0; i < Src.rows; ++i) { uchar* iData = Src.ptr<uchar>(i); uchar* iLabel = Pointlabel.ptr<uchar>(i); for(int j = 0; j < Src.cols; ++j) { if (iData[j] < 10) { iLabel[j] = 3; } } } } else { cout<<"Mode: 去除孔洞. "; for(int i = 0; i < Src.rows; ++i) { uchar* iData = Src.ptr<uchar>(i); uchar* iLabel = Pointlabel.ptr<uchar>(i); for(int j = 0; j < Src.cols; ++j) { if (iData[j] > 10) { iLabel[j] = 3; } } } } vector<Point2i> NeihborPos; //記錄鄰域點位置 NeihborPos.push_back(Point2i(-1, 0)); NeihborPos.push_back(Point2i(1, 0)); NeihborPos.push_back(Point2i(0, -1)); NeihborPos.push_back(Point2i(0, 1)); if (NeihborMode==1) { cout<<"Neighbor mode: 8鄰域."<<endl; NeihborPos.push_back(Point2i(-1, -1)); NeihborPos.push_back(Point2i(-1, 1)); NeihborPos.push_back(Point2i(1, -1)); NeihborPos.push_back(Point2i(1, 1)); } else cout<<"Neighbor mode: 4鄰域."<<endl; int NeihborCount=4+4*NeihborMode; int CurrX=0, CurrY=0; //開始檢測 for(int i = 0; i < Src.rows; ++i) { uchar* iLabel = Pointlabel.ptr<uchar>(i); for(int j = 0; j < Src.cols; ++j) { if (iLabel[j] == 0) { //********開始該點處的檢查********** vector<Point2i> GrowBuffer; //堆疊,用於儲存生長點 GrowBuffer.push_back( Point2i(j, i) ); Pointlabel.at<uchar>(i, j)=1; int CheckResult=0; //用於判斷結果(是否超出大小),0為未超出,1為超出 for ( int z=0; z<GrowBuffer.size(); z++ ) { for (int q=0; q<NeihborCount; q++) //檢查四個鄰域點 { CurrX=GrowBuffer.at(z).x+NeihborPos.at(q).x; CurrY=GrowBuffer.at(z).y+NeihborPos.at(q).y; if (CurrX>=0&&CurrX<Src.cols&&CurrY>=0&&CurrY<Src.rows) //防止越界 { if ( Pointlabel.at<uchar>(CurrY, CurrX)==0 ) { GrowBuffer.push_back( Point2i(CurrX, CurrY) ); //鄰域點加入buffer Pointlabel.at<uchar>(CurrY, CurrX)=1; //更新鄰域點的檢查標籤,避免重複檢查 } } } } if (GrowBuffer.size()>AreaLimit) CheckResult=2; //判斷結果(是否超出限定的大小),1為未超出,2為超出 else {CheckResult=1; RemoveCount++;} for (int z=0; z<GrowBuffer.size(); z++) //更新Label記錄 { CurrX=GrowBuffer.at(z).x; CurrY=GrowBuffer.at(z).y; Pointlabel.at<uchar>(CurrY, CurrX) += CheckResult; } //********結束該點處的檢查********** } } } CheckMode=255*(1-CheckMode); //開始反轉面積過小的區域 for(int i = 0; i < Src.rows; ++i) { uchar* iData = Src.ptr<uchar>(i); uchar* iDstData = Dst.ptr<uchar>(i); uchar* iLabel = Pointlabel.ptr<uchar>(i); for(int j = 0; j < Src.cols; ++j) { if (iLabel[j] == 2) { iDstData[j] = CheckMode; } else if(iLabel[j] == 3) { iDstData[j] = iData[j]; } } } cout<<RemoveCount<<" objects removed."<<endl; }
一、對於二值圖,0代表黑色,255代表白色。去除小連通區域與孔洞,小連通區域用8鄰域,孔洞用4鄰域。
函式名字為:void RemoveSmallRegion(Mat &Src, Mat &Dst,int AreaLimit, int CheckMode, int NeihborMode)
CheckMode: 0代表去除黑區域,1代表去除白區域; NeihborMode:0代表4鄰域,1代表8鄰域;
如果去除小連通區域CheckMode=1,NeihborMode=1去除孔洞CheckMode=0,NeihborMode=0
記錄每個畫素點檢驗狀態的標籤,0代表未檢查,1代表正在檢查,2代表檢查不合格(需要反轉顏色),3代表檢查合格或不需檢查 。
1.先對整個影象掃描,如果是去除小連通區域,則將黑色的背景圖作為合格,畫素值標記為3,如果是去除孔洞,則將白色的色素點作為合格,畫素值標記為3。
2.掃面整個影象,對影象進行處理。
void RemoveSmallRegion(Mat &Src, Mat &Dst,int AreaLimit, int CheckMode, int NeihborMode)
{
int RemoveCount = 0;
//新建一幅標籤影象初始化為0畫素點,為了記錄每個畫素點檢驗狀態的標籤,0代表未檢查,1代表正在檢查,2代表檢查不合格(需要反轉顏色),3代表檢查合格或不需檢查
//初始化的影象全部為0,未檢查
Mat PointLabel = Mat::zeros(Src.size(), CV_8UC1);
if (CheckMode == 1)//去除小連通區域的白色點
{
cout << "去除小連通域.";
for (int i = 0; i < Src.rows; i++)
{
for (int j = 0; j < Src.cols; j++)
{
if (Src.at<uchar>(i, j) < 10)
{
PointLabel.at<uchar>(i, j) = 3;//將背景黑色點標記為合格,畫素為3
}
}
}
}
else//去除孔洞,黑色點畫素
{
cout << "去除孔洞";
for (int i = 0; i < Src.rows; i++)
{
for (int j = 0; j < Src.cols; j++)
{
if (Src.at<uchar>(i, j) > 10)
{
PointLabel.at<uchar>(i, j) = 3;//如果原圖是白色區域,標記為合格,畫素為3
}
}
}
}
vector<Point2i>NeihborPos;//將鄰域壓進容器
NeihborPos.push_back(Point2i(-1, 0));
NeihborPos.push_back(Point2i(1, 0));
NeihborPos.push_back(Point2i(0, -1));
NeihborPos.push_back(Point2i(0, 1));
if (NeihborMode == 1)
{
cout << "Neighbor mode: 8鄰域." << endl;
NeihborPos.push_back(Point2i(-1, -1));
NeihborPos.push_back(Point2i(-1, 1));
NeihborPos.push_back(Point2i(1, -1));
NeihborPos.push_back(Point2i(1, 1));
}
else cout << "Neighbor mode: 4鄰域." << endl;
int NeihborCount = 4 + 4 * NeihborMode;
int CurrX = 0, CurrY = 0;
//開始檢測
for (int i = 0; i < Src.rows; i++)
{
for (int j = 0; j < Src.cols; j++)
{
if (PointLabel.at<uchar>(i, j) == 0)//標籤影象畫素點為0,表示還未檢查的不合格點
{ //開始檢查
vector<Point2i>GrowBuffer;//記錄檢查畫素點的個數
GrowBuffer.push_back(Point2i(j, i));
PointLabel.at<uchar>(i, j) = 1;//標記為正在檢查
int CheckResult = 0;
for (int z = 0; z < GrowBuffer.size(); z++)
{
for (int q = 0; q < NeihborCount; q++)
{
CurrX = GrowBuffer.at(z).x + NeihborPos.at(q).x;
CurrY = GrowBuffer.at(z).y + NeihborPos.at(q).y;
if (CurrX >= 0 && CurrX<Src.cols&&CurrY >= 0 && CurrY<Src.rows) //防止越界
{
if (PointLabel.at<uchar>(CurrY, CurrX) == 0)
{
GrowBuffer.push_back(Point2i(CurrX, CurrY)); //鄰域點加入buffer
PointLabel.at<uchar>(CurrY, CurrX) = 1; //更新鄰域點的檢查標籤,避免重複檢查
}
}
}
}
if (GrowBuffer.size()>AreaLimit) //判斷結果(是否超出限定的大小),1為未超出,2為超出
CheckResult = 2;
else
{
CheckResult = 1;
RemoveCount++;//記錄有多少區域被去除
}
for (int z = 0; z < GrowBuffer.size(); z++)
{
CurrX = GrowBuffer.at(z).x;
CurrY = GrowBuffer.at(z).y;
PointLabel.at<uchar>(CurrY,CurrX)+=CheckResult;//標記不合格的畫素點,畫素值為2
}
//********結束該點處的檢查**********
}
}
}
CheckMode = 255 * (1 - CheckMode);
//開始反轉面積過小的區域
for (int i = 0; i < Src.rows; ++i)
{
for (int j = 0; j < Src.cols; ++j)
{
if (PointLabel.at<uchar>(i,j)==2)
{
Dst.at<uchar>(i, j) = CheckMode;
}
else if (PointLabel.at<uchar>(i, j) == 3)
{
Dst.at<uchar>(i, j) = Src.at<uchar>(i, j);
}
}
}
cout << RemoveCount << " objects removed." << endl;
}
相關推薦
OPENCV二值化影象內孔洞填充/小區域去除
來源:http://lib.csdn.net/article/opencv/28353 原作者:robberjohn 部落格已刪除了,原始碼下載連結在 http://download.csdn.net/download/robberjoh
OpenCV二值化影象畫素操作
二值化影象畫素不是0就是255,資料型別為uchar。所以訪問方法是: // 這裡inputmat是二值化影象的mat inputmat.at<uchar>(y, x); 判斷是否為白色的方法: if (inputmat.at<uchar&g
opencv 刪除二值化影象中面積較小的連通域
對於上圖的二值化影象,要去除左下角和右上角的噪點,方法:使用opencv去掉黑色面積較小的連通域。程式碼 CvSeq* contour = NULL; double minarea = 100.0; double tmparea = 0.0;
macOS python3 opencv PIL.Image 二值化影象
Image 影象 二值化 1,遍歷所有畫素 1,遍歷所有畫素 0 黑色 255 白色 設定閥值為127,大於閥值的白色 #! /usr/local/bin/python3 # coding:utf-8 from PIL import Im
基於區域生長的二值化影象連通域標記
//左 if ((n > 0) && (obj == *(p + m*LineBytes + n - 1)) && (0 == *(pConnDomainMark + m*LineBytes + n - 1))) { *(pConnDom
一個基於Qt的用cvAdaptiveThreshold二值化影象的程式
//AdaptiveThreshold.h #ifndef ADAPTIVETHRESHOLD_H #define ADAPTIVETHRESHOLD_H #include "cv.h" #include "highgui.h" #include <QtCor
macOS opencv python 影象簡單二值化
python opencv 影象二值化 全域性閥值 1,cv2.threshold 全域性閥值 1,cv2.threshold 全域性閥值 #! /usr/local/bin/python3 # coding:utf-8 """ 影象二值化 全
[python-opencv]超大影象二值化方法
*分塊 *全域性閾值 VS 區域性閾值 1 import cv2 as cv 2 import numpy as np 3 4 def big_image_binary(image): 5 print(image.shape) 6 cw = 213 7
opencv學習(十六):超大影象二值化
超大影象二值化的方法 1.可以採用分塊方法;2.先縮放處理就行二值化,然後還原大小。 一:分塊處理超大影象的二值化問題 #匯入cv模組 import cv2 as cv import numpy as np #超大影象二值化 de
opencv學習(十六):影象的二值化
影象二值化介紹:https://blog.csdn.net/qq_30490125/article/details/80458500 &nbs
【OpenCV入門指南】第四篇 影象的二值化
【OpenCV入門指南】第四篇 影象的二值化 在上一篇《【OpenCV入門指南】第三篇Canny邊緣檢測》中介紹了使用Canny運算元對影象進行邊緣檢測。與邊緣檢測相比,輪廓檢測有時能更好的反映影象的內容。而要對影象進行輪廓檢測,則必須要先對影象進行二值化,影象的二值化就是
opencv-python(cv2)影象二值化函式threshold函式詳解及引數cv2.THRESH_OTSU使用
cv2.threshold()函式的作用是將一幅灰度圖二值化,基本用法如下: #ret:暫時就認為是設定的thresh閾值,mask:二值化的影象 ret,mask = cv2.threshold(img2gray,175,255,cv2.THRESH_BINARY) pl
影象處理(二)opencv處理影象二值化,灰度化等
這裡主要實現的 opencv 基於 android 對影象進行常用處理,比如說灰度化,二值化,rgb的轉換,這裡就不貼主要程式碼,只是工具程式碼。 Utils.xxx方法的使用需要在MainActivity.class中新增此方法(好像是掉用opencv,an
opencv二值圖孔洞填充演算法(方法系轉載)
方法原文見:http://geniusshare.i.sohu.com/blog/view/149854097.htm 作者:天才的自省 孔洞者,不與大背景聯通卻具有大背景顏色的畫素也,所以思路如下:將大背景全部用前景色(白色)填充(cvFloodFill函式,一(0
OpenCV基於區域性自適應閾值的影象二值化
在影象處理應用中二值化操作是一個很常用的處理方式,例如零器件圖片的處理、文字圖片和驗證碼圖片中字元的提取、車牌識別中的字元分割,以及視訊影象中的運動目標檢測中的前景分割,等等。 較為常用的影象二值化方法有:1)全域性固定閾值;2)區域性自適應閾值;3)OTSU等。 全域性固
基於opencv的Niblack二值化演算法--影象分割方法
Niblack演算法是比較出名的二值化演算法,網上很多Niblack程式碼是基於Matlab的,本人覺得其速度比較慢,所以便基於OpenCV改寫了其演算法,具體參考的部落格連結已經忘記了,希望博主原諒。如果缺少某些函式,比如最大值最小值函式,可以參考本人其他部落格,裡面會提供
一些基本的opencv影象預處理函式直接用法(灰度化,otsu二值化,腐蝕膨脹,canny)
#include <cv.h> #include <cxcore.h> #include <highgui.h> using namespace std; using namespace cv; int main(int a
利用opencv對影象進行二值化處理
利用該方法對圖形進行二值化處理,能夠很好的除去光線對圖片的影響 #include<iostream> #include<opencv2\opencv.hpp> using namespace cv; using namespace
【OpenCV學習筆記 004】 影象的縮放、Canny邊緣檢測和影象的二值化
一、影象的縮放 本篇將介紹使用OpenCV來縮放圖片。首先介紹幾個關鍵函式——cvResize和cvCreateImage 1.主要函式介紹 1.1 cvResize 函式功能:影象大小變換 函式原型: voidcvResize( const CvArr* src,
《opencv學習》 之 二值化
http input namespace turn 實現 ble 技術 jpg row 主要講解OTSU算法實現圖像二值化: 1.統計灰度級圖像中每個像素值的個數。 2.計算第一步個數占整個圖像的比例。 3.計算每個閾值[0-255]條件下,