不规则轮廓等距外扩方法:
1、多边形外扩
2、使用图像膨胀
3、基于轮廓上的所有点进行画圆
方法1:
void expand_polygon(vector<cv::Point> pList,vector<cv::Point> &out)
{
int count = pList.size();
vector<cv::Point> dpList,ndpList;
for(int i = 0; i < count; i++)
{
int next = (i == (count - 1)) ? 0:(i + 1);
dpList.push_back(pList.at(next) - pList.at(i));
float unitLen = 1.0f/sqrt(dpList.at(i).dot(dpList.at(i)));
ndpList.push_back(dpList.at(i) * unitLen);
//cout<<"i="<<i<<",pList:"<<pList.at(next)<<","<<pList.at(i)<<",dpList:"<<dpList.at(i)<<",ndpList:"<<ndpList.at(i)<<endl;
}
float SAFELINE = 20.0f;//负数为内缩, 正数为外扩。 需要注意算法本身并没有检测内缩多少后折线会自相交,那不是本代码的示范意图
for(int i = 0; i < count; i++)
{
int startIndex = (i==0 ? (count-1):(i-1));
int endIndex = i;
float sinTheta = ndpList.at(startIndex).cross(ndpList.at(endIndex));
cv::Point2f orientVector = ndpList.at(endIndex) - ndpList.at(startIndex);//i.e. PV2-V1P=PV2+PV1
cv::Point2f temp_out;
temp_out.x = pList.at(i).x + SAFELINE/sinTheta * orientVector.x;
temp_out.y = pList.at(i).y + SAFELINE/sinTheta * orientVector.y;
out.push_back(temp_out);
}
}
void MainWindow::on_pushButton_103_clicked()
{
cv::Mat img = cv::imread("TestROI.bmp");
cv::Mat gray,thresh,imgC;
imgC = img.clone();
cv::cvtColor(img,gray,cv::COLOR_BGR2GRAY);
cv::threshold(gray,thresh,20,255,cv::THRESH_BINARY);
vector<vector<cv::Point>> contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(thresh, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
qDebug() << "轮廓数量:" << contours.size();
for(uint k = 0 ; k < contours.size(); k++)
{
vector<cv::Point> points_expand;
expand_polygon(contours[k],points_expand);
for(int i=0; i<points_expand.size();i++)
{
int next = (i==(points_expand.size()-1) ? 0: (i+1));
line(imgC,points_expand[i],points_expand[next],cv::Scalar(0,255,0),1);
}
}
cv::imshow("waikuo1",imgC);
}
方法2:
void MainWindow::on_pushButton_104_clicked()
{
cv::Mat img = cv::imread("TestROI.bmp");
cv::Mat gray,thresh,imgC;
imgC = img.clone();
cv::cvtColor(img,gray,cv::COLOR_BGR2GRAY);
cv::threshold(gray,thresh,20,255,cv::THRESH_BINARY);
cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(21, 21)); //第一个参数MORPH_RECT表示矩形的卷积核,当然还可以选择椭圆形的、交叉型的
//膨胀操作
dilate(thresh, thresh, element);
vector<vector<cv::Point>> contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(thresh, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
qDebug() << "轮廓数量:" << contours.size();
for(uint k = 0 ; k < contours.size(); k++)
{
cv::drawContours(imgC,contours,k,cv::Scalar(0,255,0),1,8);
}
cv::imshow("waikuo2",imgC);
}
方法3:
void MainWindow::on_pushButton_105_clicked()
{
cv::Mat img = cv::imread("TestROI.bmp");
cv::Mat gray,thresh,imgC;
imgC = img.clone();
cv::cvtColor(img,gray,cv::COLOR_BGR2GRAY);
cv::threshold(gray,thresh,20,255,cv::THRESH_BINARY);
vector<vector<cv::Point>> contours;
vector<cv::Vec4i> hierarchy;
cv::findContours(thresh, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
qDebug() << "轮廓数量:" << contours.size();
for(uint k = 0 ; k < contours.size(); k++)
{
for(uint l = 0; l < contours[k].size(); l++)
{
cv::circle(img,contours[k][l],10,cv::Scalar(255,0,0));
}
}
contours.clear();
hierarchy.clear();
cv::Mat gray1,thresh1;
cv::cvtColor(img,gray1,cv::COLOR_BGR2GRAY);
cv::threshold(gray1,thresh1,20,255,cv::THRESH_BINARY);
cv::findContours(thresh1, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);
for(uint k = 0 ; k < contours.size(); k++)
{
cv::drawContours(imgC,contours,k,cv::Scalar(0,255,0),1,8);
}
cv::imshow("waikuo3",imgC);
}
使用画圆方法外扩时需要获取轮廓边缘所有点位信息,所以findcontours函数需要使用 cv::CHAIN_APPROX_NONE
效果图 - 方法1:
图像拐角处效果不佳,适用于多边形,不太适合这种不规则图形的外扩
效果图 - 方法2:
图像拐角处效果不佳,适用于多边形,不太适合这种不规则图形的外扩
效果图 - 方法3
图像效果满足实际需求,效果较好。