在上述博客中,我分别对平滑滤波、边缘检测、直线检测做了一定程度的了解,那么最终的目的我们是需要解决实际问题,那么今天就试着完成一个简单的综合训练,来在巩固前面所学知识的同时,学到新的东西!
题目如下:
1.分别检测两线段的长度;
2.算出这两平行线之间的距离。
题目参考:http://www.opencvchina.com/thread-854-1-2.html
先看第一问,要求直线的距离。很容易联想到上一回所说的霍夫变换中的PHTT方法,返回的是直线的方向以及范围,说的具体一点,实际上返回的就是检测到的直线的两端点。我们不妨用霍夫变换直接试试。具体步骤:
将图像转换成灰度图像->霍夫变换->将检测直线的端点(绿色圆圈)标记出来
得到下面的结果:
可以看到,我们检测到了很多条直线,如图有很多直线的端点,尤其是直线端点处,被检测到了多个端点(可以看得出有重叠效果)。其实也不难理解,这条直线在像素上来说是有厚度的,所以难免被检测到多条直线,这样的话就带来了问题,我们无法确定到底哪个是真正直线的两端点,于是就不能够得到线段的长度!那么我们在使用霍夫变换前需要做一些处理。-----------图像细化。
图像细化实际上就是图像骨架化,把图像核心的骨架留下,其他的赘余点删除,对于这个图来说就是把这条直线的厚度消除,得到一条单像素厚度的直线段。实际上,图像细化在图像处理、模式识别中使用的非常广发,也是非常关键的过程,这里我们介绍一种比较经典实用的图像细化方法----Zhang并行快速算法:
具体实现方法、步骤如下:
首先定义如下的8领域(注意顺序不能变):
其中P1是我们讨论的像素点,首先前提条件是P1是前景点!若要删除该点,需要满足一下条件:
过程1:
(1)2 <= N(P1) <= 6,N(x)为x的8领域中黑点(背景点)的个数;
(2)A(P1) = 1,A(x)为P2-P8之间按序前后分别为0、1的对数;
(3)P2*P4*P6 = 0;
(4)P4*P6*P8 = 0.
满足上述四个条件,可将P1点去除;
过程2:
(1)2 <= N(P1) <= 6,N(x)为x的8领域中黑点(背景点)的个数;
(2)A(P1) = 1,A(x)为P2-P8之间按序前后分别为0、1的对数;
(3)P2*P4*P8 = 0;
(4)P2*P6*P8 = 0.
满足上述四个条件,可将P1点去除;
以上即为Zhang并行算法的主要步骤,需要反复运行直到无法得到可删除点为止。对于图像细化,这里只做一个简单的了解,很多理论知识以及高级的算法我后续会再进行分析。
给出关键代码函数如下:
//寻找按序的前后分别是0、1的对数函数
int Findn(IplImage* img, int i, int j)
{
CvScalar s1 = cvGet2D(img, i, j);
CvScalar s2 = cvGet2D(img, i - 1, j);
CvScalar s3 = cvGet2D(img, i - 1, j + 1);
CvScalar s4 = cvGet2D(img, i, j + 1);
CvScalar s5 = cvGet2D(img, i + 1, j + 1);
CvScalar s6 = cvGet2D(img, i + 1, j);
CvScalar s7 = cvGet2D(img, i + 1, j - 1);
CvScalar s8 = cvGet2D(img, i, j - 1);
CvScalar s9 = cvGet2D(img, i - 1, j - 1);
int a = s1.val[0];
int b = s2.val[0];
int c = s3.val[0];
int d = s4.val[0];
int e = s5.val[0];
int f = s6.val[0];
int g = s7.val[0];
int h = s8.val[0];
int l = s9.val[0];
int find[] = { a, b, c, d, e, f, g, h, l };//按8领域顺序定义数组,方便操作
int n = 0;
for (int x = 2; x < 9; ++x)
{
if (find[x] == 0 && find[x + 1] == 255)
{
n = n + 1;
}
}
return n;
}
//这是细化直线的功能函数
IplImage* ThinImage(IplImage* img, int k) //确保输入的是二值图像
{
int condition = 0;//满足的条件个数
int mark = 0;//成功的标志位
int firstN = 0;//第一个条件黑点的个数
CvScalar s;
for (int n = 0; n < k; ++n)
{
for (int i = 1; i < img->height - 1; ++i)
{
for (int j = 1; j < img->width - 1;++j)
{//开始过程1的寻找
condition = 0;//初始化条件满足数
//cout << "1" << endl;
s = cvGet2D(img, i, j);
if (s.val[0] == 255)//如果这是前景点,即边缘
{
//cout << "2" << endl;
//*************************第一过程****************************
//*************************step1****************************
firstN = 0;
for (int ii = -1; ii < 1; ++ii)
{
for (int jj = -1; jj < 1; ++jj)
{
s = cvGet2D(img, i + ii, j + jj);
if (s.val[0] == 255)
{
firstN = firstN + 1;
}
}
}
if (firstN < 3 || firstN > 7)
{
continue;
}
else
{
condition = condition + 1;
}
//****************************************************************
//*************************step2*********************************
if (Findn(img, i, j) != 1)
{
continue;
}
else
{
condition = condition + 1;
}
//****************************************************************
//*************************step3*********************************
CvScalar s1 = cvGet2D(img, i - 1, j);
CvScalar s2 = cvGet2D(img, i, j + 1);
CvScalar s3 = cvGet2D(img, i + 1, j);
CvScalar s4 = cvGet2D(img, i, j - 1);
int a = s1.val[0];//2
int b = s2.val[0];//4
int c = s3.val[0];//6
int d = s4.val[0];//8
if (a * b * c != 0)
{
continue;
}
else
{
condition = condition + 1;
}
//*********************************************************************
//******************************step4**********************************
if (b * c * d != 0)
{
continue;
}
else
{
condition = condition + 1;
}
//*********************************************************************
//第一过程的结果操作
if (condition == 4)
{
mark = 1;
//((char *)(img->imageData + img->widthStep * (i)))[j] = 0;
CvScalar p;
p.val[0] = 0;
cvSet2D(img, i, j, p);
//cout << "11111111111111111111111111111111111" << endl;
}
}
}
}
//****************************过程2**************************
for (int i = 1; i < img->height - 1; ++i)
{
for (int j = 1; j < img->width - 1;++j)
{//开始过程1的寻找
condition = 0;//初始化条件满足数
//cout << "1" << endl;
s = cvGet2D(img, i, j);
if (s.val[0] == 255)//如果这是前景点,即边缘
{
//cout << "2" << endl;
//*************************第一过程****************************
//*************************step1****************************
firstN = 0;
for (int ii = -1; ii < 1; ++ii)
{
for (int jj = -1; jj < 1; ++jj)
{
s = cvGet2D(img, i + ii, j + jj);
if (s.val[0] == 255)
{
firstN = firstN + 1;
}
}
}
if (firstN < 3 || firstN > 7)
{
continue;
}
else
{
condition = condition + 1;
}
//****************************************************************
//*************************step2*********************************
if (Findn(img, i, j) != 1)
{
continue;
}
else
{
condition = condition + 1;
}
//****************************************************************
//*************************step3*********************************
CvScalar s1 = cvGet2D(img, i - 1, j);
CvScalar s2 = cvGet2D(img, i, j + 1);
CvScalar s3 = cvGet2D(img, i + 1, j);
CvScalar s4 = cvGet2D(img, i, j - 1);
int a = s1.val[0];//2
int b = s2.val[0];//4
int c = s3.val[0];//6
int d = s4.val[0];//8
if (a * b * d != 0)
{
continue;
}
else
{
condition = condition + 1;
}
//*********************************************************************
//******************************step4**********************************
if (a * c * d != 0)
{
continue;
}
else
{
condition = condition + 1;
}
//*********************************************************************
//第一过程的结果操作
if (condition == 4)
{
mark = 1;
//((char *)(img->imageData + img->widthStep * (i)))[j] = 0;
CvScalar p;
p.val[0] = 0;
cvSet2D(img, i, j, p);
//cout << "222222222222222222222222" << endl;
}
}
}
}
//cout << " end " << endl;
}
return img;
}
以上即为图像骨架化的编程实现,让我们看一看效果吧:
可以看到,直线已经骨架化成功了,那么接下来,我们再使用霍夫变换检测线段。使用PPHT方法,得到线段的方向以及范围信息,并将检测的直线端点标记出来,代码如下:
CvMemStorage* storageline = cvCreateMemStorage(0);
CvSeq* lines;
lines = cvHoughLines2(binaryline, storageline, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 300, 100, 10, 1000);
int n = 0;
CvPoint* point;
double temp[8];
for (int i = 0; i < lines->total; ++i)
{
point = (CvPoint*)cvGetSeqElem(lines, i);
temp[i * 4] = point[0].x;
temp[i * 4 + 1] = point[0].y;
temp[i * 4 + 2] = point[1].x;
temp[i * 4 + 3] = point[1].y;
cvLine(resultline, point[0], point[1], CV_RGB(255, 0, 0));
cvCircle(resultline, point[0], 3, CV_RGB(0, 255, 0), 1, 8, 3);
cvCircle(resultline, point[1], 3, CV_RGB(0, 255, 0), 1, 8, 3);
double distance = sqrt((point[1].x - point[0].x) * (point[1].x - point[0].x) + (point[1].y - point[0].y) * (point[1].y - point[0].y));
cout << "the " << i << " line's distance :" << distance << endl;
++n;
}
cout << n << endl;
n = 0;
结果如下:
成功得到了直线端点坐标的信息,并且输出n = 2,即刚刚好检测到两条直线!nice,这样的话直线长度就很好求了,使用数学方法,计算两坐标之间的距离即可。(结果将在后面显示)
第二问实际上在第一问的基础上,就是一个数学问题。
如图,正是求两平行线距离x。
x = |AC|/sin(a)
于是通过向量求得
cos = (AB·AC)/|AB||AC|
sin = sqrt(i - cos^2)
于是便可求得x。
按照如此思路,编程求得上述两问的结果:
相关推荐
在本压缩包“OpenCV-MinGW-Build-OpenCV-4.5.2-x64.zip”中,提供的是一份已经针对MinGW编译器优化的OpenCV 4.5.2版本,适用于64位Windows系统。 OpenCV 4.5.2是该库的一个稳定版本,它包含了众多新特性和性能提升...
OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib
赠送jar包:opencv-4.5.5-1.5.7.jar; 赠送原API文档:opencv-4.5.5-1.5.7-javadoc.jar; 赠送源代码:opencv-4.5.5-1.5.7-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.5-1.5.7.pom; 包含翻译后的API文档:...
赠送jar包:opencv-4.5.5-1.5.7.jar; 赠送原API文档:opencv-4.5.5-1.5.7-javadoc.jar; 赠送源代码:opencv-4.5.5-1.5.7-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.5-1.5.7.pom; 包含翻译后的API文档:...
赠送jar包:opencv-4.5.1-1.5.5.jar; 赠送原API文档:opencv-4.5.1-1.5.5-javadoc.jar; 赠送源代码:opencv-4.5.1-1.5.5-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.1-1.5.5.pom; 包含翻译后的API文档:...
赠送jar包:opencv-4.5.1-1.5.5.jar; 赠送原API文档:opencv-4.5.1-1.5.5-javadoc.jar; 赠送源代码:opencv-4.5.1-1.5.5-sources.jar; 赠送Maven依赖信息文件:opencv-4.5.1-1.5.5.pom; 包含翻译后的API文档:...
这个压缩包"OpenCV-MinGW-Build-OpenCV-4.5.0-with-contrib-32bit.zip"是专为在Windows环境下使用MinGW编译器进行C++开发而准备的。MinGW(Minimalist GNU for Windows)是一个小型的GNU开发工具集,它提供了一个不...
opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 opencv-4.1.2-vc14_vc15 opencv 安装包 ...
Python版本的OpenCV库,也就是`opencv-python`,为Python程序员提供了方便的接口来利用这个功能丰富的库。在本案例中,我们讨论的是OpenCV的Python接口的4.10.0.84版本,它是一个经过编译的二进制包,文件名为`...
3. **贡献模块**(opencv_contrib):包含了额外的模块,如Xfeatures2d(特征检测和描述符),ximgproc(图像处理算法),xobjdetect(对象检测),xphoto(照片修复),aruco(二维码和AR标记识别),face(人脸...
opencv-4.5.4-android-sdk .zip
这个压缩包"opencv-4.6.0-vc14-vc15.exe"很可能是OpenCV的特定版本,兼容Visual Studio 2014 (VC14) 和 Visual Studio 2015 (VC15) 的编译环境。 OpenCV 4.6.0是该库的一个稳定版本,可能包含了自4.0.0版本以来的...
opencv-4.5.5-vc14_vc15.exe opencv_contrib-4.5.5.zip cmake-3.24.0-rc1-windows-x86_64.msi jdk-17_windows-x64_bin.msi VTK-8.2.0.zip
在`OpenCV-android-sdk\samples`目录下,OpenCV提供了一些示例应用,这些应用展示了如何在Android上使用OpenCV进行实际的图像处理任务。比如,`FaceDetection`示例演示了如何检测图像中的面部,而`TessTwo`则展示了...
总的来说,OpenCV-4.6.0-android-sdk.zip为Android开发者提供了一个全面的环境,使他们能够利用强大的计算机视觉功能开发创新的应用程序,如增强现实、图像识别、安全监控等。通过深入研究和实践,开发者可以充分...
这个"opencv-4.9.0-android"版本是OpenCV针对Android平台的特定优化版本,包含了最新的特性和性能改进。 1. **OpenCV库的核心功能**: - 图像处理:包括滤波、边缘检测(如Canny、Sobel、Laplacian)、色彩转换...
这个压缩包“opencv-4.5.5-vc14_vc15最新版本”包含了OpenCV库的4.5.5版,适用于Visual Studio 2017(vc14)和Visual Studio 2019(vc15)编译环境。OpenCV库不仅支持C++,还提供了Python、Java等其他编程语言的接口...
Python版本的OpenCV(即`opencv-python`)提供了丰富的接口,使得Python开发者能够轻松地利用这个库进行图像和视频操作。`opencv-python-4.5.5.64.tar.gz`是一个包含了OpenCV Python接口的特定版本4.5.5.64的源码或...
这个压缩包“opencv-python-4.10.0.82.tar.gz”包含了Python接口的OpenCV库版本4.10.0.82,是开发人员在Python环境中进行图像和视频处理的重要资源。 OpenCV-Python是OpenCV库的一个Python绑定,它使得Python程序员...
3. **LineDescriptor**:用于线段检测和描述,对图像中的直线结构进行分析和匹配。 4. **aruco**:AR(增强现实)相关的标记检测和校准,常用于实现虚拟对象与真实世界的融合。 5. **BackgroundSubtractorMOG2**:...