本文的主要參考爲
1、官方文檔OpenCV249-floodFill
2、《Learning OpenCV 3》page361-364
OpenCV中提供的直線擬合API如下:
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
詳細參數含義參見官方文檔,這裏提出幾點注意:
(1)由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板爲(x+1,y+1)
(2)flag的設置包括四部分,設置方式見下面源碼
實例介紹:
使用函數floodFill對原圖像中的感興趣區域進行信息統計,處理的結果如下圖所示。
注意事項已經在源碼中做了詳細的註釋,參看源碼。
源碼:
#include "opencv2/opencv.hpp"
#include <iostream>
#include <numeric> //std::accumulate
#include <vector> // std::vector
using namespace cv;
using namespace std;
Mat g_srcImage, g_floodFillImage, g_maskImage, g_edgeImage, g_showImage;//定義原始圖、目標圖、灰度圖、掩模圖
int g_nLowDifference = 20, g_nUpDifference = 20;//負差最大值、正差最大值
int g_nConnectivity = 8;//表示floodFill函數標識符低八位的連通值
int g_nNewMaskVal = 255;//新的重新繪製的像素值
static void onMouse( int event, int x, int y, int, void* )
{
// 若鼠標左鍵沒有按下,便返回
if( event != CV_EVENT_LBUTTONDOWN )
return;
//若seed爲已統計過的區域則返回
//at(i,j)等價於point(y,x),一定注意對應關係:i->y,j->x
int nn = g_maskImage.at<uchar>(y+1,x+1);
if (nn == 128 || nn == 255)
return;
Point seed = Point(x,y);
//標識符的0-7位爲g_nConnectivity,8-15位爲g_nNewMaskVal左移8位的值
//16-23位爲CV_FLOODFILL_FIXED_RANGE或者0,24-31位爲CV_FLOODFILL_MASK_ONLY或者0;
//下面兩種寫法都可以
int flags = g_nConnectivity | (g_nNewMaskVal << 8) | CV_FLOODFILL_FIXED_RANGE | CV_FLOODFILL_MASK_ONLY;
//int flags = g_nConnectivity + (g_nNewMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
//定義重繪區域的最小邊界矩形區域
Rect roi_rect;
//掩膜灰度值爲三種:0/128/256,256表示剛生成的區域,128表示之前生成的區域
threshold(g_maskImage, g_maskImage, 1, 128, CV_THRESH_BINARY);
//漫水填充
int area = floodFill(g_floodFillImage, g_maskImage, seed, Scalar::all(255), &roi_rect,
Scalar::all(g_nLowDifference),Scalar::all(g_nUpDifference), flags);
//threshold(g_maskImage, g_edgeImage, 200, 255, CV_THRESH_BINARY);
//imshow( "mask", g_maskImage );
//填充區域的統計信息
vector<uchar> roi_info;
for( int i = roi_rect.y; i < roi_rect.y+roi_rect.height; ++i)
{
for( int j = roi_rect.x; j < roi_rect.x+roi_rect.width; ++j )
{
//由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板爲(x+1,y+1)
if(g_maskImage.at<uchar>(i+1,j+1)==255)
{
roi_info.push_back(g_floodFillImage.at<uchar>(i,j));
}
}
}
//均值
double sum = std::accumulate(std::begin(roi_info), std::end(roi_info), 0.0);
double mean = sum / roi_info.size();
//方差
double accum = 0.0;
std::for_each (std::begin(roi_info), std::end(roi_info), [&](const double d) {
accum += (d-mean)*(d-mean); });
double stdev = sqrt(accum/(roi_info.size()-1));
//最大值最小值
double min = *std::min_element(std::begin(roi_info), std::end(roi_info));
double max = *std::max_element(std::begin(roi_info),std::end(roi_info));
//顯示統計信息
int text_x = roi_rect.x+roi_rect.width;//寫字的位置
int text_y = roi_rect.y+roi_rect.height/4;
int line_gap = 20;//字體行間距
char text_area[1024];
sprintf(text_area,"area:%d",area);
putText(g_showImage,text_area,Point(text_x,text_y),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
char text_mean[1024];
sprintf(text_mean,"mean:%.2f",mean);
putText(g_showImage,text_mean,Point(text_x,text_y+line_gap),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
char text_max[1024];
sprintf(text_max,"max:%.2f",max);
putText(g_showImage,text_max,Point(text_x,text_y+line_gap*2),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
char text_min[1024];
sprintf(text_min,"min:%.2f",min);
putText(g_showImage,text_min,Point(text_x,text_y+line_gap*3),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
char text_std[1024];
sprintf(text_std,"stdev:%.2f",stdev);
putText(g_showImage,text_std,Point(text_x,text_y+line_gap*4),CV_FONT_HERSHEY_COMPLEX,0.5,Scalar(0,0,255),1,8);
//將統計區域的邊緣繪製在原圖上
Canny(g_maskImage,g_edgeImage,20,60);
for( int i = roi_rect.y; i < roi_rect.y+roi_rect.height; ++i)
{
for( int j = roi_rect.x; j < roi_rect.x+roi_rect.width; ++j )
{
//由於mask的尺寸大於原圖,所以原圖的座標(x,y)對應於模板爲(x+1,y+1)
if(g_edgeImage.at<uchar>(i+1,j+1)==255)
{
g_showImage.at<Vec3b>(i,j)[0]= 0;
g_showImage.at<Vec3b>(i,j)[1]= 0;
g_showImage.at<Vec3b>(i,j)[2]= 255;
}
}
}
//imshow("edge",g_edgeImage);
imshow("效果圖", g_showImage);
}
int main( int argc, char** argv )
{
//載入原圖
g_srcImage = imread("1.bmp", 0);
if( !g_srcImage.data ) { printf("Oh,no,讀取圖片image0錯誤~! \n"); return false; }
//拷貝源圖到目標圖--用於計算的圖
g_srcImage.copyTo(g_floodFillImage);
//構造顯示的圖像(僅用來顯示和獲取鼠標座標,不參與計算)
g_srcImage.copyTo(g_showImage);
//顯示的圖爲彩色格式,用來顯示彩色的邊緣和文字
cvtColor(g_showImage,g_showImage,CV_GRAY2RGB);
//初始化掩膜mask
g_maskImage.create(g_floodFillImage.rows+2, g_floodFillImage.cols+2, CV_8UC1);
g_maskImage = Scalar::all(0);
namedWindow( "效果圖",CV_WINDOW_AUTOSIZE );
imshow("效果圖",g_floodFillImage);
//創建Trackbar
createTrackbar( "負差最大值", "效果圖", &g_nLowDifference, 255, 0 );
createTrackbar( "正差最大值" ,"效果圖", &g_nUpDifference, 255, 0 );
//鼠標回調函數
setMouseCallback( "效果圖", onMouse, 0 );
waitKey();
return 0;
}