問題的提出:
所謂內切圓,是指“與
多邊形各邊都
相切的
圓“。我們這里需要找的是所謂”內接圓“,可以簡單認為是”圓點在輪廓中,到輪廓中所有點的距離一樣的圖像“。在這所有的”內接圓“中,尋找半徑最大的哪一個。
這個問題已經廣泛討論了,比如
這樣的圖像,尋找輪廓的最大內接圓。
解決方法:
利用計算機圖像學技術中輪廓的相關思路,可以直接從圓的定義解決此問題。基於OpenCV的代碼和注釋如下:
#
include
"stdafx.h"
#
include
<iostream
>
using
namespace std;
using
namespace cv;
VP FindBigestContour(Mat src){
int imax
=
0;
//代表最大輪廓的序號
int imaxcontour
=
-
1;
//代表最大輪廓的大小
std
:
:vector
<std
:
:vector
<cv
:
:Point
>>contours;
findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
for (
int i
=
0;i
<contours.size();i
++){
int itmp
= contourArea(contours[i]);
//這里采用的是輪廓大小
if (imaxcontour
< itmp ){
imax
= i;
imaxcontour
= itmp;
}
}
return contours[imax];
}
int main(
int argc,
char
* argv[])
{
Mat src
= imread(
"e:/template/cloud.png");
Mat temp;
cvtColor(src,temp,COLOR_BGR2GRAY);
threshold(temp,temp,
100,
255,THRESH_OTSU);
imshow(
"src",temp);
//尋找最大輪廓
VP VPResult
= FindBigestContour(temp);
//尋找最大內切圓
int dist
=
0;
int maxdist
=
0;
Point center;
for(
int i
=
0;i
<src.cols;i
++)
{
for(
int j
=
0;j
<src.rows;j
++)
{
dist
= pointPolygonTest(VPResult,cv
:
:Point(i,j),
true);
if(dist
>maxdist)
{
maxdist
=dist;
center
=cv
:
:Point(i,j);
}
}
}
//繪制結果
circle(src,center,maxdist,Scalar(
0,
0,
255));
imshow(
"dst",src);
waitKey();
}
其中
PointPolygonTest
測試點是否在多邊形中
double cvPointPolygonTest(
const CvArr
* contour, CvPoint2D32f pt,
int measure_dist );
contour 輸入輪廓.
pt 針對輪廓需要測試的點。
measure_dist 如果非
0,函數將估算點到輪廓最近邊的距離。
函數cvPointPolygonTest 決定測試點是否在輪廓內,輪廓外,還是輪廓的邊上(或者共邊的交點上),它的返回值是正負零,相對應的,當measure_dist
=
0時,返回值是
1,
-
1,
0, 同樣當 measure_dist≠
0 ,它是返回一個從點到最近的邊的帶符號距離。
結果:
優化的思路:
這里對圓心的遍歷,是遍歷了所有的圖像上面的點。然而根據”內接圓心一定在輪廓內部“這個先驗知識,可以縮小循環范圍,提高算法效率。
2018年7月27日22:01:01 對opencv的官方例子進行修改,並提交github
/**
* @function pointPolygonTest_demo.cpp
* @brief Demo code to use the pointPolygonTest function...fairly easy
* @author OpenCV team
*/
#include "stdafx.h"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
using namespace cv;
using namespace std;
//return the biggest contour by size
vector<Point> FindBiggestContour(Mat src){
int icount = 0;
int imaxcontour = -1;
std::vector<std::vector<cv::Point>>contours;
findContours(src,contours,CV_RETR_LIST,CV_CHAIN_APPROX_SIMPLE);
for (int i=0;i<contours.size();i++){
int itmp = contourArea(contours[i]);
if (imaxcontour < itmp ){
icount = i;
imaxcontour = itmp;
}
}
return contours[icount];
}
/**
* @function main
*/
int main( void )
{
/// Create an image
const int r = 100;
Mat src = Mat::zeros( Size( 4*r, 4*r ), CV_8U );
/// Create a sequence of points to make a contour
vector<Point2f> vert(6);
vert[0] = Point( 3*r/2, static_cast<int>(1.34*r) );
vert[1] = Point( 1*r, 2*r );
vert[2] = Point( 3*r/2, static_cast<int>(2.866*r) );
vert[3] = Point( 5*r/2, static_cast<int>(2.866*r) );
vert[4] = Point( 3*r, 2*r );
vert[5] = Point( 5*r/2, static_cast<int>(1.34*r) );
/// Draw it in src
for( int i = 0; i < 6; i++ )
{
line( src, vert[i], vert[(i+1)%6], Scalar( 255 ), 3 );
}
/// Get the contours
vector<vector<Point> > contours;
findContours( src, contours, RETR_TREE, CHAIN_APPROX_SIMPLE);
/// Calculate the distances to the contour
Mat raw_dist( src.size(), CV_32F );
for( int i = 0; i < src.rows; i++ )
{
for( int j = 0; j < src.cols; j++ )
{
raw_dist.at<float>(i,j) = (float)pointPolygonTest( contours[0], Point2f((float)j, (float)i), true );
}
}
double minVal, maxVal;
minMaxLoc( raw_dist, &minVal, &maxVal );
minVal = abs(minVal);
maxVal = abs(maxVal);
/// Depicting the distances graphically
Mat drawing = Mat::zeros( src.size(), CV_8UC3 );
for( int i = 0; i < src.rows; i++ )
{
for( int j = 0; j < src.cols; j++ )
{
if( raw_dist.at<float>(i,j) < 0 )
{
drawing.at<Vec3b>(i,j)[0] = (uchar)(255 - abs(raw_dist.at<float>(i,j)) * 255 / minVal);
}
else if( raw_dist.at<float>(i,j) > 0 )
{
drawing.at<Vec3b>(i,j)[2] = (uchar)(255 - raw_dist.at<float>(i,j) * 255 / maxVal);
}
else
{
drawing.at<Vec3b>(i,j)[0] = 255;
drawing.at<Vec3b>(i,j)[1] = 255;
drawing.at<Vec3b>(i,j)[2] = 255;
}
}
}
//get the biggest Contour
vector<Point> biggestContour = FindBiggestContour(src);
//find the maximum enclosed circle
int dist = 0;
int maxdist = 0;
Point center;
for(int i=0;i<src.cols;i++)
{
for(int j=0;j<src.rows;j++)
{
dist = pointPolygonTest(biggestContour,cv::Point(i,j),true);
if(dist>maxdist)
{
maxdist=dist;
center=cv::Point(i,j);
}
}
}
circle(drawing,center,maxdist,Scalar(255,255,255));
/// Show your results
imshow( "Source", src );
imshow( "Distance and maximum enclosed circle", drawing );
waitKey();
return 0;
}
附件列表
