OpenCV學習筆記
以下兩種爲連通區域算法實現圖像分割
connectedComponents();
其定義如下:
int connectedComponents (
InputArrayn image,
OutputArray labels,
int connectivity = 8,
int ltype = CV_32S
);
>第一個參數 image表示8位單通道的二值圖像
>第二個參數 labels表示輸出的與輸入圖像相同大小的圖像
>第三個參數 connectivity表示區域的連接方式,有4和8兩種不同的連接方法
>第四個參數 ltype表示想要使用的標籤圖像類型
connectedComponentsWithStats();
其定義如下:
int connectedComponentsWithStats (
InputArrayn image,
OutputArray labels,
OutputArray stats,
OutputArray centroids,
int connectivity = 8,
int ltype = CV_32S
);
>第一個參數 image表示8位單通道的二值圖像
>
>第二個參數 labels表示輸出的與輸入圖像相同大小的圖像
>
>第三個參數 stats表示每個連通區域的外接矩形和麪積,它是一個n×5的矩陣,分別表示的是對應輪廓的x,y,width,height,area
>
- x 用參數 CC_STAT_LEFT 表示 ,代表連通區域對象的左邊x座標
- y 用參數 CC_STAT_TOP 表示 , 代表連通區域對象的頂部y座標
- width 用參數 CC_STAT_WIDTH 表示 , 代表連通區域對象邊框的寬度
- height 用參數 CC_STAT_HEIGHT 表示 , 代表連通區域對象邊框的高度
- area 用參數 CC_STAT_AREA 表示 , 代表連通區域對象的像素數量(面積)
>
>第四個參數 centroids表示輪廓的中心點,它是一個n×2的矩陣,表示的是對應輪廓的質心座標
>
>第五個參數 connectivity表示區域的連接方式,有4和8兩種不同的連接方法
>
>第六個參數 ltype表示想要使用的標籤圖像類型
該函數有返回值,返回一個int整型 n,函數返回值爲連通區域的總數N,範圍爲[0,N-1],其中0代表背景。
以下爲提取輪廓算法實現圖像分割
findContours();
這是一個輪廓提取函數,其定義如下:
void findContours( InputArray image,
OutputArrayOfArrays contours,
OutoutArray hierarchy,
int mode,
int method,
Point offset = Point() )
>第一個參數 image表示的是輸入圖像
>
>第二個參數 contours表示的是輸出輪廓(點向量)
>
>第三個參數 hierarchy表示的是存儲輪廓層次結構的可選輸出向量,也可以得到輪廓間的拓撲關係
>
>第四個參數 mode表示檢索輪廓的方法,有以下幾種方法:
>
- RETR_EXTERNAL: 檢索外部輪廓
- RETR_LIST : 檢索沒有建立層次結構的輪廓
- RETR_CCOMP : 檢索有兩個級別層次結構的所有輪廓
- RETR_TREE : 檢索所有輪廓,並創建輪廓之間完整的層次結構
>
>第五個參數 method表示允許執行檢索輪廓形狀的近似方法,有以下幾種方法:
>
- CV_CHAIN_APPROX_NONE : 並不適用於近似任何輪廓和存儲所有的輪廓點
- CV_CHAIN_APPROX_SIMPLE : 壓縮存儲水平、垂直和對角線段的起始點和結束點
>
>第六個參數 offset表示的是用於轉移所有輪廓的可選點值
drawContours();
這是一個輪廓繪製函數,其定義如下:
void drawContours( InputOutputArray image,
InputArrayOfArrays contours,
int contourId,
const Scalar& color,
int thickness = 1,
int lineType = 8,
InputArray hierarchy = noArray(),
int maxLevel = INT_MAX,
Point offset = Point()
)
>第一個參數 image表示要繪製輪廓的圖像
>
>第二個參數 contours表示所有輸入的輪廓,每個輪廓都被儲存成一個點向量
>
>第三個參數 contourId表示指定要繪製輪廓的編號,如果是負數,則繪製所有的輪廓
>
>第四個參數 color表示繪製輪廓所用的顏色
>
>第五個參數 thickness表示繪製輪廓的線的粗細,如果是負數,則輪廓內部被填充
>
>第六個參數 lineType表示繪製輪廓的線的連通性
>
>第七個參數 hierarchy表示輪廓層級的可選參數,只有繪製部分輪廓時纔會用到
>
>第八個參數 maxLevel表示繪製輪廓的最高級別,這個參數只有在hierarchy有效的時候纔可使用
>
- maxLevel=0 : 繪製與輸入輪廓屬於同一層次的所有輪廓即輸入輪廓和與其相鄰的輪廓
- maxLevel=1 : 繪製與輸入輪廓同一層次的所有輪廓與其子節點。
- maxLevel=2 : 繪製與輸入輪廓同一層次的所有輪廓與其子節點以及子節點的子節點
>
>第九個參數 offset表示的是用於轉移所有輪廓的可選點值
代碼演示結果如下
# CMakeLists.txt 內容如下
cmake_minimum_required(VERSION 2.8)
project(test11)
# add c++ standard of 11
set( CMAKE_CXX_FLAGS "-std=c++11" )
# find the library of OpenCV
find_package( OpenCV 3 REQUIRED )
# add head file
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( test11 test11.cpp )
#link the library of OpenCV
target_link_libraries( test11 ${OpenCV_LIBS} )
// test11 內容如下
#include<iostream>
#include<string>
#include<sstream>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/features2d/features2d.hpp>
using namespace std;
using namespace cv;
const char * keys = {
"{help h ? || print this message}"
"{@image || Image to process}"
"{@lightPattern || Image light pattern to apply to image input}"
"{lightMethod l| 1 | Method to remove background light , 0 difference, 1 division } "
"{segMethod s| 1 | Method to segment: 1 connected Components, 2 connected Components with stats , 3 find contours}"
};
//隨機顏色生成
static Scalar randomColor(RNG& rng)
{
int icolor = (unsigned)rng;
return Scalar(icolor & 255, (icolor >> 8) & 255, (icolor >> 16) & 255);
}
Mat removeLight( Mat img,Mat pattern , int method )
{
Mat aux;
//如果方法是歸一化
if(method==1){
//先將圖像轉爲32位浮點型方便計算
Mat img32 , pattern32;
img.convertTo(img32,CV_32F);
pattern.convertTo(pattern32,CV_32F);
//division
aux=1-(img32/pattern32);
aux=255*aux;
aux.convertTo(aux,CV_8U);
}else{
aux=pattern - img;
}
return aux;
}
Mat calculateLightPattern( Mat img )
{
Mat pattern;
//用基本和有效的方式來計算圖像光紋
blur(img ,pattern , Size(img.cols/3,img.cols/3));
return pattern;
}
void ConnectedComponents(Mat img)
{
//使用連通區域分離符合要求的部分圖像Mat標籤
Mat labels;
int num_objects = connectedComponents(img , labels);
if(num_objects <2 ){
cout<<"No objects detected"<<endl;
return ;
}else{
cout<<"Number of objects detected: "<<num_objects -1<<endl;
} //對象中還包括一個背景圖像
//創建彩色的輸出圖像
Mat output = Mat::zeros(img.rows,img.cols,CV_8UC3);
RNG rng( 0xFFFFFFFF );
for(int i=1 ; i<num_objects ; i++){
Mat mask = labels==i;
output.setTo(randomColor(rng),mask );
}
imshow("result",output);
}
void ConnectedComponentsStats(Mat img)
{
Mat labels , stats , centroids;
int num_objects = connectedComponentsWithStats(img , labels , stats , centroids);
if(num_objects<2){
cout<<"No objects detected"<<endl;
return ;
}else{
cout<<"Number of objects detected: "<<num_objects - 1<<endl;
}
Mat output = Mat::zeros(img.rows,img.cols,CV_8UC3);
RNG rng( 0xFFFFFFFF );
for(int i=1 ; i<num_objects ; i++){
cout<<"Object "<< i << "with position: "<<centroids.at<Point2d>(i) <<"with area : "<<stats.at<int>(i , CC_STAT_AREA)<<endl;
Mat mask = labels==i;
output.setTo(randomColor(rng),mask);
stringstream ss;
ss<<"area:"<<stats.at<int>(i,CC_STAT_AREA);
putText(output,ss.str(),centroids.at<Point2d>(i),FONT_HERSHEY_SIMPLEX,0.4,Scalar(255,255,255));
}
imshow("result",output);
}
void FindContoursBasic(Mat img)
{
vector<vector<Point>> contours;
findContours(img,contours,RETR_EXTERNAL,CHAIN_APPROX_SIMPLE);
Mat output=Mat::zeros(img.rows,img.cols,CV_8UC3);
if(contours.size()==0){
cout<<"No objects detected"<<endl;
}else{
cout<<"Number of objects detected: "<<contours.size()<<endl;
}
RNG rng( 0xFFFFFFFF );
for(int i=1;i<contours.size();i++){
drawContours(output , contours , i , randomColor(rng) );
}
imshow("result",output);
}
int main( int argc , char **argv)
{
CommandLineParser parser(argc , argv , keys );
parser.about("Let's Go");
if(parser.has("help")){
parser.printMessage();
return 0;
}
String img_file = parser.get<String>(0);
String light_pattern_file = parser.get<String>(1);
int method_light = parser.get<int>("lightMethod");
int method_seg = parser.get<int>("segMethod");
if(!parser.check()){
parser.printErrors();
return 0;
}
Mat img = imread(img_file , 0);
if(img.data==NULL){
cout<<"Error loading image"<<img_file<<endl;
return 0;
}
//使用中值濾波去除噪聲
Mat img_noise;
medianBlur(img , img_noise , 3 );
//創建一個光背景,在removeLight函數中作爲光紋遮擋
Mat img_pattern;
img_pattern=calculateLightPattern(img);
//使用光紋去除背景
Mat img_no_light1;
img_no_light1=removeLight(img_noise,img_pattern,method_light);
//爲了分割圖像,先進行二值化
Mat img_thr1;
if(method_light!=2){
threshold(img_no_light1,img_thr1,30,255,THRESH_BINARY);
}else{
threshold(img_no_light1,img_thr1,140,255,THRESH_BINARY_INV);
}
namedWindow("src",0);
imshow("src",img);
namedWindow("no_noise",1);
imshow("no_noise",img_noise);
namedWindow("no_light",1);
imshow("no_light",img_no_light1);
namedWindow("pattern",1);
imshow("pattern",img_pattern);
namedWindow("threshold",1);
imshow("threshold",img_thr1);
if(method_seg==1){
cout<<"connected Components"<<endl;
ConnectedComponents(img_thr1);
}else if(method_seg==2){
cout<<"connected Components with stats"<<endl;
ConnectedComponentsStats(img_thr1);
}else{
cout<<"find contours"<<endl;
FindContoursBasic(img_thr1);
}
waitKey(0);
return 0;
}
演示結果如下
$ ./test11 screw.jpeg -s=1 -l=1 #使用connected Components連通區域 使用除法除去光
除去光的過程爲:
$ ./test11 screw.jpeg -s=1 -l=0 #使用connected Components連通區域算法 使用減法除去光
除去光的過程爲:
我們可以發現 使用減法的去光過程得到的圖像 比 除法得到的圖像 的目標更加模糊,沒有那麼清晰
$ ./test11 screw.jpeg -s=2 -l=1 #使用connected Components with stats連通區域算法 使用除法除去光
result結果圖中顯示了具體的信息
$ ./test11 screw.jpeg -s=1 -l=1 #使用findContours算法 使用除法除去光
result結果圖中顯示的是輪廓信息