1. 程式人生 > >Opencv影象識別從零到精通(24)------漫水填充,種子填充,區域生長、孔洞填充

Opencv影象識別從零到精通(24)------漫水填充,種子填充,區域生長、孔洞填充

         可以說從這篇文章開始,就結束了影象識別的入門基礎,來到了第二階段的學習。在平時處理二值影象的時候,除了要進行形態學的一些操作,還有有上一節講到的輪廓連通區域的面積周長標記等,還有一個最常見的就是孔洞的填充,opencv這裡成為漫水填充,其實也可以叫種子填充,或者區域生長,基本的原理是一樣的,但是應用的時候需要注意一下,種子填充用遞迴的辦法,回溯演算法,漫水填充使用堆疊,提高效率,同時還提供了一種方式是掃描行。經常用來填充孔洞,現在來具體看看。

漫水填充:也就是用一定顏色填充聯通區域,通過設定可連通畫素的上下限以及連通方式來達到不同的填充效果;漫水填充經常被用來標記或分離影象的一部分以便對其進行進一步處理或分析,也可以用來從輸入影象獲取掩碼區域,掩碼會加速處理過程,或只處理掩碼指定的畫素點,操作的結果總是某個連續的區域。簡單的說,就是選中點seedPoint,然後選取出它周圍和它色彩差異不大的點,並將它們的值改為newVal。如果被選取的點,遇到mask掩碼,則放棄對該方向的

種子填充演算法,種子填充演算法是從多邊形區域內部的一點開始,由此出發找到區域內的所有畫素。種子填充演算法採用的邊界定義是區域邊界上所有畫素具有某個特定的顏色值,區域內部所有畫素均不取這一特定顏色,而邊界外的畫素則可具有與邊界相同的顏色值。

具體演算法步驟:

  • 標記種子(x,y)的畫素點 ;
  • 檢測該點的顏色,若他與邊界色和填充色均不同,就用填充色填   充該點,否則不填充 ;
  • 檢測相鄰位置,繼續 2。這個過程延續到已經檢測區域邊界範圍內的所有畫素為止。
  • 當然在搜尋的時候有兩種檢測相鄰畫素:四向連通和八向連通。四向連通即從區域上一點出發,通過四個方向上、下、左、右來檢索。而八向連通加上了左上、左下、右上、右下四個方向。這種演算法的有點是演算法簡單,易於實現,也可以填充帶有內孔的平面區域。但是此演算法需要更大的儲存空間以實現棧結構,同一個畫素多次入棧和出棧,效率低,運算量大。

掃描線種子填充演算法:該演算法屬於種子填充演算法,它是以掃描線上的區段為單位操作。所謂區段,就是一條掃描線上相連著的若干內部象素的集合。掃描線種子填充演算法思想:首先填充當前掃描線上的位於給定區域的一區段,然後確定於這一區段相鄰的上下兩條線上位於該區域內是否存在需要填充的新區段,如果存在,則依次把他們儲存起來,反覆這個過程,直到所儲存的各區段都填充完畢。

<span style="font-size:18px;">FloodFill函式
  C++: int floodFill(InputOutputArray image, InputOutputArray mask, 
                Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 );
  InputOutputArray:輸入和輸出影象。
  mask:            輸入的掩碼影象。
  seedPoint:      演算法開始處理的開始位置。 
  newVal:         影象中所有被演算法選中的點,都用這個數值來填充。
    rect:            最小包圍矩陣。
  loDiff:         最大的低亮度之間的差異。
    upDiff:         最大的高亮度之間的差異。
  flag:           選擇演算法連線方式。
</span>

根據上面的函式先看一個基礎的應用
<span style="font-size:18px;">#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;

Mat g_srcImage, g_dstImage, g_grayImage, g_maskImage;
int g_nFillMode = 1;
int g_nLowDifference = 20, g_nUpDifference = 20;
int g_nConnectivity = 4;
int g_bIsColor = true;
bool g_bUseMask = false;
int g_nNewMaskVal = 255;
static void onMouse( int event, int x, int y, int, void* )
{

	if( event != EVENT_LBUTTONDOWN )
		return;
	Point seed = Point(x,y);
	int LowDifference = g_nFillMode == 0 ? 0 : g_nLowDifference;
	int UpDifference = g_nFillMode == 0 ? 0 : g_nUpDifference;
	int flags = g_nConnectivity + (g_nNewMaskVal << 8) +(g_nFillMode == 1 ? FLOODFILL_FIXED_RANGE : 0);
	int b = (unsigned)theRNG() & 255;
	int g = (unsigned)theRNG() & 255;
	int r = (unsigned)theRNG() & 255;
	Rect ccomp;
	Scalar newVal = g_bIsColor ? Scalar(b, g, r) : Scalar(r*0.299 + g*0.587 + b*0.114);
	Mat dst = g_bIsColor ? g_dstImage : g_grayImage;//目標圖的賦值
	int area;
	if( g_bUseMask )
	{
		threshold(g_maskImage, g_maskImage, 1, 128, THRESH_BINARY);
		area = floodFill(dst, g_maskImage, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
			Scalar(UpDifference, UpDifference, UpDifference), flags);
		imshow( "mask", g_maskImage );
	}
	else
	{
		area = floodFill(dst, seed, newVal, &ccomp, Scalar(LowDifference, LowDifference, LowDifference),
			Scalar(UpDifference, UpDifference, UpDifference), flags);
	}

	imshow("效果圖", dst);
	cout << area << " 個畫素被重繪\n";
}

int main( int argc, char** argv )
{

	g_srcImage = imread("lena.jpg", 1);

	if( !g_srcImage.data ) { printf("讀取圖片image0錯誤~! \n"); return false; } 

	g_srcImage.copyTo(g_dstImage);
	cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
	g_maskImage.create(g_srcImage.rows+2, g_srcImage.cols+2, CV_8UC1);
	namedWindow( "效果圖",WINDOW_AUTOSIZE );
	createTrackbar( "負差最大值", "效果圖", &g_nLowDifference, 255, 0 );
	createTrackbar( "正差最大值" ,"效果圖", &g_nUpDifference, 255, 0 );
	setMouseCallback( "效果圖", onMouse, 0 );
	while(1)
	{
		//先顯示效果圖
		imshow("效果圖", g_bIsColor ? g_dstImage : g_grayImage);

		//獲取鍵盤按鍵
		int c = waitKey(0);
		//判斷ESC是否按下,若按下便退出
		if( (c & 255) == 27 )
		{
			cout << "程式退出...........\n";
			break;
		}

		//根據按鍵的不同,進行各種操作
		switch( (char)c )
		{
			//如果鍵盤“1”被按下,效果圖在在灰度圖,彩色圖之間互換
		case '1':
			if( g_bIsColor )//若原來為彩色,轉為灰度圖,並且將掩膜mask所有元素設定為0
			{
				cout << "鍵盤“1”被按下,切換彩色/灰度模式,當前操作為將【彩色模式】切換為【灰度模式】\n";
				cvtColor(g_srcImage, g_grayImage, COLOR_BGR2GRAY);
				g_maskImage = Scalar::all(0);	//將mask所有元素設定為0
				g_bIsColor = false;	//將識別符號置為false,表示當前影象不為彩色,而是灰度
			}
			else//若原來為灰度圖,便將原來的彩圖image0再次拷貝給image,並且將掩膜mask所有元素設定為0
			{
				cout << "鍵盤“1”被按下,切換彩色/灰度模式,當前操作為將【彩色模式】切換為【灰度模式】\n";
				g_srcImage.copyTo(g_dstImage);
				g_maskImage = Scalar::all(0);
				g_bIsColor = true;//將識別符號置為true,表示當前影象模式為彩色
			}
			break;
			//如果鍵盤按鍵“2”被按下,顯示/隱藏掩膜視窗
		case '2':
			if( g_bUseMask )
			{
				destroyWindow( "mask" );
				g_bUseMask = false;
			}
			else
			{
				namedWindow( "mask", 0 );
				g_maskImage = Scalar::all(0);
				imshow("mask", g_maskImage);
				g_bUseMask = true;
			}
			break;
			//如果鍵盤按鍵“3”被按下,恢復原始影象
		case '3':
			cout << "按鍵“3”被按下,恢復原始影象\n";
			g_srcImage.copyTo(g_dstImage);
			cvtColor(g_dstImage, g_grayImage, COLOR_BGR2GRAY);
			g_maskImage = Scalar::all(0);
			break;
			//如果鍵盤按鍵“4”被按下,使用空範圍的漫水填充
		case '4':
			cout << "按鍵“4”被按下,使用空範圍的漫水填充\n";
			g_nFillMode = 0;
			break;
			//如果鍵盤按鍵“5”被按下,使用漸變、固定範圍的漫水填充
		case '5':
			cout << "按鍵“5”被按下,使用漸變、固定範圍的漫水填充\n";
			g_nFillMode = 1;
			break;
			//如果鍵盤按鍵“6”被按下,使用漸變、浮動範圍的漫水填充
		case '6':
			cout << "按鍵“6”被按下,使用漸變、浮動範圍的漫水填充\n";
			g_nFillMode = 2;
			break;
			//如果鍵盤按鍵“7”被按下,操作標誌符的低八位使用4位的連線模式
		case '7':
			cout << "按鍵“7”被按下,操作標誌符的低八位使用4位的連線模式\n";
			g_nConnectivity = 4;
			break;
			//如果鍵盤按鍵“8”被按下,操作標誌符的低八位使用8位的連線模式
		case '8':
			cout << "按鍵“8”被按下,操作標誌符的低八位使用8位的連線模式\n";
			g_nConnectivity = 8;
			break;
		}
	}

	return 0;
}
</span>
                                         

再來看看漫水填充在填充孔洞上的應用

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\imgproc\imgproc.hpp>
using namespace std;
using namespace cv;  
void chao_fillHole(const cv::Mat srcimage, cv::Mat &dstimage)  
{  
    Size m_Size = srcimage.size();  
    Mat temimage = Mat::zeros(m_Size.height + 2, m_Size.width + 2, srcimage.type());   
    srcimage.copyTo(temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));  
    floodFill(temimage, Point(0, 0), Scalar(255));  
    Mat cutImg;
    temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);  
    dstimage = srcimage | (~cutImg);  
}  
int main()  
{  
    Mat src=imread("111.png");
	Mat dst;
   chao_fillHole(src, dst)  ;
   imshow("tianchong",dst);
   waitKey(0);
   return 0;

}  
                                                                           

matlab

<span style="font-size:18px;">I=imread('tire.tif');
figure,imshow(I)
BW=imfill(I);
figure,imshow(BW)</span>

影象識別演算法交流 QQ群:145076161,歡迎影象識別與影象演算法,共同學習與交流


相關推薦

Opencv影象識別精通24------填充種子填充區域生長孔洞填充

         可以說從這篇文章開始,就結束了影象識別的入門基礎,來到了第二階段的學習。在平時處理二值影象的時候,除了要進行形態學的一些操作,還有有上一節講到的輪廓連通區域的面積周長標記等,還有一個最常見的就是孔洞的填充,opencv這裡成為漫水填充,其實也可以叫種子填

Opencv影象識別精通33----moravec角點harris角點

一、角點     影象處理和與計算機視覺領域,興趣點(interest points),或稱作關鍵點(keypoints)、特徵點(feature points) 被大量用於解決物體識別,影象識別、影象匹配、視覺跟蹤、三維重建等一系列的問題。我們不再觀察整幅圖,而是選擇某些

Opencv影象識別精通37----KNN演算法

#include <opencv2/ml/ml.hpp> #include <iostream> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp>

Opencv影象識別精通32----直方圖對比模版匹配方向投影

0、預備知識 歸一化就是要把需要處理的資料經過處理後(通過某種演算法)限制在你需要的一定範圍內。 函式原型: <span style="font-size:18px;">void normalize(InputArray src,OutputArray dst

Opencv影象識別精通21-----canny運算元邊緣檢測

          最後來看看canny運算元,這個是被成為最好的運算元,因為過程多,有準測,後面會列出來,也是邊緣檢測的最後一個,所以這裡作為結尾,來看看各個邊緣檢測的效果。 邊緣檢測結果比較

cocos2dx 3.1學習——菜單場景切換場景傳值

天空 ptr select 特效 new 要點 綁定 使用 water 回想一下上一篇的內容,我們已經學會了創建一個新的場景scene,加入sprite和label到層中。掌握了定時事件schedule。我們能夠順利的寫出打飛機的主場景框架。 上一篇的內容我練習了七個新

Git開始

16px pop 普通 遠程服務 git clone one img 分享 模式 一、遠程倉庫管理   1、將本地內容推送到遠程庫   先關聯遠程庫,執行命令: git remote add origin https://github.com/Hollydan/gitsto

Hyperledger Fabric 1.0 開始——公網環境構建

1.3 項目 htm move 自己 lvm2 fast 情況 tor 1:環境構建 在本文中用到的宿主機環境是Centos ,版本為Centos.x86_647.2,通過Docker 容器來運行Fabric的節點,版本為v1.0。因此,啟動Fabric網絡中的節點需要先安

Hyperledger Fabric 1.0 開始——創建Fabric多節點集群

_id 測試 es2017 xtra 去掉 compose 多個 服務 執行命令 4:創建Fabric多節點集群 4.1、配置說明 首先可以根據官方Fabric自帶的e2e_cli列子中的集群方案來生成我們自己的集群,與案例不同的是我們需要把容器都分配到不同的服務器上,彼此

docker開始容器初體驗

osi build 技術分享 框架 log 註冊表 代碼 content doc 使用定義容器 Dockerfile Dockerfile定義容器內所需要的環境。對網絡接口和磁盤驅動器等資源的訪問在此環境中進行虛擬化,該環境與系統的其他部分隔離,因此您需要將端口映射到外部

docker開始堆棧初體驗stacks

開始 services 信息 工作 run cer cal tail int 先決條件 安裝Docker 1.13或更高版本。 獲取Docker Compose,請參考第三節 按照第四節中的描述獲取Docker Machine。 在第二節中了解如何創建容器。

DevOps環境搭建

images blue roc ces 檢查 user nag col 技術分享 DevOps環境從零搭建(一) 前文說到jenkins已經安裝好了,安裝pipeline及blue-ocean(好看)插件。 切換到blue-ocean風格,創建一個pipeline,網上很多

Python3學習

# -*- coding: utf-8 -*- #數字型別轉換 a = 1.0 print(int(a)) a = 1 print(float(a)) #complex(x) 將x轉換到一個複數,實數部分為 x,虛數部分為 0。 a = 1.1 print(complex(a)) #com

Python3學習

# python 3.+ # -*- coding: utf-8 -*- #檢視版本號python -V #列印 print("Hello Python") #等待使用者輸入 input("請按下回車鍵") #同一行顯示多條語句 import sys;x = '同一行顯示多條語句';sy

微服務閘道器搭建——搭建api閘道器不帶驗證

環境準備 建立空的core2.1 api專案  演示使用名稱APIGateWay  過程參考上一篇 完成後在appsettings.json 新增節點 "Setting": { "Port": "5000" } 搭建過程 新增檔案configuration.json

微服務閘道器搭建——ocelot配置追蹤功能

butterfly 準備工作 首先下載buterfly release版本  解壓並通過命令啟動:dotnet Butterfly.Web.dll --EnableHttpCollector=true 可以採用bat檔案的方式  cd C:\Users\Lenovo\Desk

【視訊】Kubernetes1.12開始程式碼編譯到自動部署

作者: 李佶澳   轉載請保留:原文地址   釋出時間:2018/11/10 16:14:00 說明 kubefromscratch-ansible和kubefromscratch介紹 使用前準備

Centos Docker 開始2之 mssql 的資料庫檔案儲存在主機

Docker mmsql新建資料庫如果能夠把資料庫檔案儲存在主機上就好了,centos好像可以掛載的 docker 的 run 命令: -v ~/nginx/www:/www :將主機中專案的目錄www掛載到容器的/www 準備命令一下: docker run -e

Hyperledger Fabric 1.0 開始——執行測試e2e

3:執行測試e2e 3.1、執行fabric-samples的問題說明 該問題說明能夠解決6.1、平臺特定使用的二進位制檔案配置第一步的問題。可以選擇繼續閱讀該說明,或者等參考到6.1小節時再反向閱讀本說明,具體在6.1中會重新指向本步驟。 一般情況下,我們會參照官網來完成第一個網路測試,在該線上文件中會讓我

Hyperledger Fabric 1.0 開始——Fabric多節點叢集生產部署

6.1、平臺特定使用的二進位制檔案配置 該方案與Hyperledger Fabric 1.0 從零開始(五)——執行測試e2e類似,根據企業需要,可以控制各節點的域名,及聯盟鏈的統一域名。可以指定單獨節點的訪問,生成指定的公私鑰、證書等檔案。具體的引數配置可以參考generateArtifacts.sh檔案,