1. 程式人生 > >OPENCV二值化影象內孔洞填充/小區域去除

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]條件下,