首页 > 其他分享 >求解向量和轮廓的交点

求解向量和轮廓的交点

时间:2022-12-25 21:31:19浏览次数:38  
标签:src eigen 求解 double 交点 Mat 轮廓 向量 255

在“学习OpenCV3"的QQ群众,网友且行且珍惜针对前期博客中的内容提出了以下问题:




求解向量和轮廓的交点_斜率


比如这张图,利用PCA求出了特征向量之后,我想要求解与轮廓的交点,不知道有没有简单的方法

求解向量和轮廓的交点_ci_02

@禾老师 


 


非常好的问题!在寻找到轮廓的”主方向“后,往往下一个动作就是寻找向量和轮廓的交点,因为往往这才是我们更关心的地方。为了解决这个问题,我认为的思路应该是这样的:


1、首先要界定范围。对于交点来说,肯定是在这个轮廓的“最小外接矩形”中的,所以先求出外接矩形作为限定;


2、向量只是一个方向,要将其变成一条直线(如果在“最小外接矩形”中就是线段),推荐使用LineIterator来表示直线;


3、最后,判断这条线段上的点是否在轮廓上,可以使用pointpolytest函数。


 


结合代码具体讲解。为了凸显本文知识重点,本文采用以下一幅图像来说明算法



求解向量和轮廓的交点_OpenCV_03


最后得到的结果是这样的,其中黄点为主方向向量和外界矩形交点,红点为和轮廓交点。



求解向量和轮廓的交点_OpenCV_04


 


全部代码为:


/************************************************************************/


// 求解向量和轮廓的交点


// by jsxyhelu(jsxyhelu.cnblogs.com)


// 2018/10/05


/************************************************************************/

# include "stdafx.h"
# include "opencv2/imgcodecs.hpp"
# include "opencv2/highgui.hpp"
# include "opencv2/imgproc.hpp"
# include "opencv2/photo.hpp"
using namespace std;
using namespace cv;

//寻找最大外接轮廓

vector <Point > FindBigestContour(Mat src){
int max_area_contour_idx = 0;
double max_area = - 1;

vector <vector <Point >
>contours;

findContours(src,contours,RETR_LIST,CHAIN_APPROX_SIMPLE);
//handle case if no contours are detected
CV_Assert( 0 != contours.size());
for (uint i = 0;i <contours.size();i
++){

double temp_area = contourArea(contours[i]);
if (max_area < temp_area ){
max_area_contour_idx = i;
max_area = temp_area;
}
}
return contours[max_area_contour_idx];
}

//程序主要部分

int main( int argc, char
*
* argv )

{
//读入图像,转换为灰度
Mat src = imread( "E:/sandbox/cloud.png");
Mat src_gray;
cvtColor(src, src_gray, COLOR_BGR2GRAY);
//阈值处理
Mat threshold_output;
cv : :threshold(src_gray,threshold_output, 150,
255,THRESH_OTSU
|THRESH_BINARY_INV);

//轮廓分析
vector <vector <Point >
> contours;

vector <Vec4i > hierarchy;
vector <Point > biggestContour = FindBigestContour(threshold_output);
//寻找最大轮廓

Rect boundRect = boundingRect( Mat(biggestContour) ); //获得轮廓最小外接矩形
cv : :rectangle(src,boundRect,Scalar( 0,
0,
255));

//pca分析,求出斜率和经过的一点
Mat data_pts = Mat(biggestContour.size(), 2, CV_64FC1); //Construct a buffer used by the pca analysis

for ( int i = 0; i < data_pts.rows;
++i)

{
data_pts.at < double
>(i,
0)
= biggestContour[i].x;

data_pts.at < double
>(i,
1)
= biggestContour[i].y;

}
PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); //执行PCA运算
Point pos = Point2f(pca_analysis.mean.at < double
>(
0,
0),

pca_analysis.mean.at < double
>(
0,
1));
//主方向直线经过的一点

vector <Point2d > eigen_vecs( 2);
//保存PCA分析结果,其中0组为主方向,1组为垂直方向

vector < double
> eigen_val(
2);

for ( int i = 0; i <
2;
++i)

{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at < double
>(i,
0),

pca_analysis.eigenvectors.at < double
>(i,
1));

eigen_val[i] = pca_analysis.eigenvalues.at < double
>(i,
0);

}
line(src, pos - 0. 02
* Point(eigen_vecs[
0].x
* eigen_val[
0],eigen_vecs[
0].y
* eigen_val[
0]),

pos + 0. 02
* Point(eigen_vecs[
0].x
* eigen_val[
0],eigen_vecs[
0].y
* eigen_val[
0]) , Scalar(
255,
255,
0));
//绘制概略主方向

//求出主方向直线和外接矩形的交点,
float k = eigen_vecs[ 0].y /eigen_vecs[
0].x;
//斜率

Point2f pt1 = Point2f(boundRect.x,k *(boundRect.x - pos.x)
+pos.y);

Point2f pt2 = Point2f((boundRect.x +boundRect.width),k *((boundRect.x
+boundRect.width)
-pos.x)
+pos.y);

circle(src,pt1, 5,Scalar( 0, 255,
255),
-
1);

circle(src,pt2, 5,Scalar( 0, 255,
255),
-
1);

//遍历两个交点之间的线段,得出和轮廓的交点
LineIterator it(src, pt1, pt2, 8);
for( int i = 0; i < it.count; i
++,
++it)

{
Point pt(it.pos()); //获得线段上的点
if (abs(pointPolygonTest(biggestContour,pt, true)) < 1)
circle(src,pt, 5,Scalar( 0, 0,
255),
-
1);

}
waitKey();
return 0;
}


知识重点:


 


1、FindBigestContour为寻找轮廓中最大轮廓的函数,目前这个函数还没有merge到OpenCV中,下一步有这个计划,注意这个函数的命名规则是按照OpenCV的方法定 义的;


 


2、我们采用Rect boundRect    = boundingRect( Mat(biggestContour) );


来获得轮廓的最小外接矩形。为什么要首先获得这个外接矩形了,因为我们这里来所有要求的点肯定都在这个矩形中,我们做这一步就能够降低算法的计算复杂程度;


 


3、PCA分析的具体原来和细节,请查看《如何获得物体的主要方向?》


     我们这里使用,主要是获得两个数据,一个是该轮廓的重心,这个点是我们最后要求的那条直线肯定经过的;二个是求出直线的斜率。那么对于一条直线,已经知道 斜率和经过的一点,就已经能够被定义出来;


 


4、最后在求该直线和轮廓的交点的时候,采用了LineIterator 和pointPolygonTest,前者是OpenCV中专门用来遍历直线的;后者是专门用来计算点和轮廓的关系的,应该说这里的应用还是非常高效的。


 


感谢阅读至此,希望有所帮助。


在“学习OpenCV3"的QQ群众,网友且行且珍惜针对前期博客中的内容提出了以下问题:



求解向量和轮廓的交点_斜率


比如这张图,利用PCA求出了特征向量之后,我想要求解与轮廓的交点,不知道有没有简单的方法

求解向量和轮廓的交点_ci_02

@禾老师 


 


非常好的问题!在寻找到轮廓的”主方向“后,往往下一个动作就是寻找向量和轮廓的交点,因为往往这才是我们更关心的地方。为了解决这个问题,我认为的思路应该是这样的:


1、首先要界定范围。对于交点来说,肯定是在这个轮廓的“最小外接矩形”中的,所以先求出外接矩形作为限定;


2、向量只是一个方向,要将其变成一条直线(如果在“最小外接矩形”中就是线段),推荐使用LineIterator来表示直线;


3、最后,判断这条线段上的点是否在轮廓上,可以使用pointpolytest函数。


 


结合代码具体讲解。为了凸显本文知识重点,本文采用以下一幅图像来说明算法



求解向量和轮廓的交点_OpenCV_03


最后得到的结果是这样的,其中黄点为主方向向量和外界矩形交点,红点为和轮廓交点。



求解向量和轮廓的交点_OpenCV_04


 


全部代码为:


/************************************************************************/


// 求解向量和轮廓的交点


// by jsxyhelu(jsxyhelu.cnblogs.com)


// 2018/10/05


/************************************************************************/

# include "stdafx.h"
# include "opencv2/imgcodecs.hpp"
# include "opencv2/highgui.hpp"
# include "opencv2/imgproc.hpp"
# include "opencv2/photo.hpp"
using namespace std;
using namespace cv;

//寻找最大外接轮廓

vector <Point > FindBigestContour(Mat src){
int max_area_contour_idx = 0;
double max_area = - 1;
vector <vector <Point > >contours;
findContours(src,contours,RETR_LIST,CHAIN_APPROX_SIMPLE);
//handle case if no contours are detected
CV_Assert( 0 != contours.size());
for (uint i = 0;i <contours.size();i ++){
double temp_area = contourArea(contours[i]);
if (max_area < temp_area ){
max_area_contour_idx = i;
max_area = temp_area;
}
}
return contours[max_area_contour_idx];
}

//程序主要部分

int main( int argc, char
*
* argv )

{
//读入图像,转换为灰度
Mat src = imread( "E:/sandbox/cloud.png");
Mat src_gray;
cvtColor(src, src_gray, COLOR_BGR2GRAY);
//阈值处理
Mat threshold_output;
cv : :threshold(src_gray,threshold_output, 150, 255,THRESH_OTSU |THRESH_BINARY_INV);
//轮廓分析
vector <vector <Point > > contours;
vector <Vec4i > hierarchy;
vector <Point > biggestContour = FindBigestContour(threshold_output); //寻找最大轮廓
Rect boundRect = boundingRect( Mat(biggestContour) ); //获得轮廓最小外接矩形
cv : :rectangle(src,boundRect,Scalar( 0, 0, 255));
//pca分析,求出斜率和经过的一点
Mat data_pts = Mat(biggestContour.size(), 2, CV_64FC1); //Construct a buffer used by the pca analysis
for ( int i = 0; i < data_pts.rows; ++i)
{
data_pts.at < double
>(i,
0)
= biggestContour[i].x;

data_pts.at < double
>(i,
1)
= biggestContour[i].y;

}
PCA pca_analysis(data_pts, Mat(), CV_PCA_DATA_AS_ROW); //执行PCA运算
Point pos = Point2f(pca_analysis.mean.at < double
>(
0,
0),

pca_analysis.mean.at < double
>(
0,
1));
//主方向直线经过的一点

vector <Point2d > eigen_vecs( 2); //保存PCA分析结果,其中0组为主方向,1组为垂直方向
vector < double
> eigen_val(
2);

for ( int i = 0; i < 2; ++i)
{
eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at < double
>(i,
0),

pca_analysis.eigenvectors.at < double
>(i,
1));

eigen_val[i] = pca_analysis.eigenvalues.at < double
>(i,
0);

}
line(src, pos - 0. 02 * Point(eigen_vecs[ 0].x * eigen_val[
0],eigen_vecs[
0].y
* eigen_val[
0]),

pos + 0. 02 * Point(eigen_vecs[ 0].x * eigen_val[
0],eigen_vecs[
0].y
* eigen_val[
0]) , Scalar(
255,
255,
0));
//绘制概略主方向

//求出主方向直线和外接矩形的交点,
float k = eigen_vecs[ 0].y /eigen_vecs[ 0].x; //斜率
Point2f pt1 = Point2f(boundRect.x,k *(boundRect.x - pos.x) +pos.y);
Point2f pt2 = Point2f((boundRect.x +boundRect.width),k *((boundRect.x +boundRect.width) -pos.x) +pos.y);

circle(src,pt1, 5,Scalar( 0, 255, 255), - 1);

circle(src,pt2, 5,Scalar( 0, 255, 255), - 1);

//遍历两个交点之间的线段,得出和轮廓的交点
LineIterator it(src, pt1, pt2, 8);
for( int i = 0; i < it.count; i ++, ++it)
{
Point pt(it.pos()); //获得线段上的点
if (abs(pointPolygonTest(biggestContour,pt, true)) < 1)
circle(src,pt, 5,Scalar( 0, 0, 255), - 1);

}
waitKey();
return 0;
}


知识重点:


 


1、FindBigestContour为寻找轮廓中最大轮廓的函数,目前这个函数还没有merge到OpenCV中,下一步有这个计划,注意这个函数的命名规则是按照OpenCV的方法定 义的;


 


2、我们采用Rect boundRect    = boundingRect( Mat(biggestContour) );


来获得轮廓的最小外接矩形。为什么要首先获得这个外接矩形了,因为我们这里来所有要求的点肯定都在这个矩形中,我们做这一步就能够降低算法的计算复杂程度;


 


3、PCA分析的具体原来和细节,请查看《如何获得物体的主要方向?》

     我们这里使用,主要是获得两个数据,一个是该轮廓的重心,这个点是我们最后要求的那条直线肯定经过的;二个是求出直线的斜率。那么对于一条直线,已经知道 斜率和经过的一点,就已经能够被定义出来;


 


4、最后在求该直线和轮廓的交点的时候,采用了LineIterator 和pointPolygonTest,前者是OpenCV中专门用来遍历直线的;后者是专门用来计算点和轮廓的关系的,应该说这里的应用还是非常高效的。


 


感谢阅读至此,希望有所帮助。

标签:src,eigen,求解,double,交点,Mat,轮廓,向量,255
From: https://blog.51cto.com/jsxyhelu2017/5968401

相关文章

  • 支持向量机
    一、简介支持向量机是一类按监督学习方式对数据进行二元分类的广义线性分类器,其决策边界是对学习样本求解的最大边距超平面。SVM使用铰链损失函数计算经验风险并在求解系......
  • MATLAB数学建模(十) | 粒子群优化算法求解二维路径规划问题MATLAB代码讲解
    愿你搜索到人生的最优解hello,大家好。各位可点击左下方阅读原文,访问公众号官方店铺。谨防上当受骗,感谢各位支持!我们在​​MATLAB数学建模(九)|粒子群优化(PSO)算法求解二维路......
  • 差分进化算法求解TSP问题(附MATLAB代码)
    愿你搜索到人生的最优解文案:挽月排版:随心390hello,大家好。各位可点击左下方阅读原文,访问公众号官方店铺。谨防上当受骗,感谢各位支持!在上周的运筹学课中,听老师吹了一波差分进......
  • 改进差分进化算法求解容量受限的车辆路径(CVRP)问题MATLAB代码
    ​​▎​​​​号内搜索​​hello,大家好。各位可点击上方号内搜索搜索往期推文,点击左下方阅读原文,即可访问公众号官方店铺。谨防上当受骗,感谢各位支持!在​​改进差分进化算......
  • 机器学习 | 自组织神经网络求解TSP问题
    今天我们来讲一讲使用自组织神经网络求解TSP问题,本次推文分为以下两部分:1、自组织神经网络的基本原理2、利用自组织神经网络解决TSP问题的基本思路▎自组织神经网络的基本原......
  • 【221224-4】求解:2的9次方的x次方/8的3次方的x次方=1/4
    ......
  • KMP算法解释:理解关于next[j]数组的求解问题
    一、算法背景介绍(我们为什么要采用这种算法?)1.补充定义:(1)主串:待匹配的大字符串(2)模式串:我们希望在主串中匹配到的字符串2.从暴力匹配到KMP算法(1)暴力匹配算法谈到KMP算法......
  • RISCV-V-1.0向量扩展指令集学习
    大部分内容翻译自riscv-v-spec-1.0部分参考:【《RISC-V“V“VectorExtensionVersion1.0》阅读笔记】_LPL之芯的博客-CSDN博客RISC-V"V"(向量)扩展规范v0.9+文档(2......
  • 基于word2vec的中文词向量训练
    基于word2vec的中文词向量训练一、引言  在绝大多数的自然语言处理任务中,语料是无法直接用来特征提取,需要将其转化为计算机可以读取的数值,因此引入独热编码,即对于语料库中......
  • 求解| 小程序怎样在硬件设备上运行?
    伴随着科技技术的日益发展、小程序技术的不断成熟,很多开发运营者们都发现小程序“即用即走、轻量开发”的特性十分符合多种硬件设备的使用方式。由此,创作者们在创作过程中也......