您的位置:首页 > 运维架构

【opencv】两条平行线之间的距离

2014-03-10 23:05 309 查看
问题:一张输入图片,图片上有两条平行线,求出这两条平行线之间的距离

解决思路:

1. 对图像中的直线进行细化

2. 提取直线的轮廓坐标

3. 对轮廓上的坐标进行直线集合,从而得到直线方程

4. 计算两条直线之间的距离

参考:

问题来源 http://www.opencvchina.com/thread-854-1-1.html
图像细化 /article/9469481.html

图像轮廓提取 http://blog.csdn.net/augusdi/article/details/9000893
直线拟合 http://blog.csdn.net/zhuoyue08/article/details/6803040
两条直线之间的距离公式3:http://zhidao.baidu.com/link?url=ef_DHNkjyq1qq7VgubX3afL2KIUQIB4ukd3zHGp0zz8iPPKC046azyvG5ltHR-i0WaLI72eO7j0sOJI4wZSE4q

工具:

opencv 2.4.8 + VS2013

代码:

1.头文件 ProcessImage.h

//ProcessImage.h
#pragma once
#include <opencv2/highgui/highgui.hpp>

/* 对输入图像进行细化
* src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
* dst为对src细化后的输出图像,格式与src格式相同,调用前需要分配空间,元素中只有0与1,1代表有元素,0代表为空白
* maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
*/
void thinImage(IplImage* src, IplImage* dst, int maxIterations = -1);


2.代码实现 ProcessImage.cpp

//ProcessImage.cpp
#include "ProcessImage.h"
#include <utility>
#include <vector>
void thinImage(IplImage* src, IplImage* dst, int maxIterations)
{
using namespace cv;
CvSize size = cvGetSize(src);
cvCopy(src, dst);//将src中的内容拷贝到dst中
int count = 0;  //记录迭代次数
while (true)
{
count++;
if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达
break;
//std::cout << count << ' ';输出迭代次数
std::vector<std::pair<int, int> > mFlag; //用于标记需要删除的点
//对点标记
for (int i = 0; i<size.height; ++i)
{
for (int j = 0; j<size.width; ++j)
{
//如果满足四个条件,进行标记
//  p9 p2 p3
//  p8 p1 p4
//  p7 p6 p5
int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);

if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
{
int ap = 0;
if (p2 == 0 && p3 == 1) ++ap;
if (p3 == 0 && p4 == 1) ++ap;
if (p4 == 0 && p5 == 1) ++ap;
if (p5 == 0 && p6 == 1) ++ap;
if (p6 == 0 && p7 == 1) ++ap;
if (p7 == 0 && p8 == 1) ++ap;
if (p8 == 0 && p9 == 1) ++ap;
if (p9 == 0 && p2 == 1) ++ap;

if (ap == 1)
{
if (p2*p4*p6 == 0)
{
if (p4*p6*p8 == 0)
{
//标记
mFlag.push_back(std::make_pair(i, j));
}
}
}
}
}
}

//将标记的点删除
for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
{
CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
}

//直到没有点满足,算法结束
if (mFlag.size() == 0)
{
break;
}
else
{
mFlag.clear();//将mFlag清空
}

//对点标记
for (int i = 0; i<size.height; ++i)
{
for (int j = 0; j<size.width; ++j)
{
//如果满足四个条件,进行标记
//  p9 p2 p3
//  p8 p1 p4
//  p7 p6 p5
int p1 = CV_IMAGE_ELEM(dst, uchar, i, j);
if (p1 != 1) continue;
int p2 = (i == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j);
int p3 = (i == 0 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j + 1);
int p4 = (j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j + 1);
int p5 = (i == size.height - 1 || j == size.width - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j + 1);
int p6 = (i == size.height - 1) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j);
int p7 = (i == size.height - 1 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i + 1, j - 1);
int p8 = (j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i, j - 1);
int p9 = (i == 0 || j == 0) ? 0 : CV_IMAGE_ELEM(dst, uchar, i - 1, j - 1);

if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
{
int ap = 0;
if (p2 == 0 && p3 == 1) ++ap;
if (p3 == 0 && p4 == 1) ++ap;
if (p4 == 0 && p5 == 1) ++ap;
if (p5 == 0 && p6 == 1) ++ap;
if (p6 == 0 && p7 == 1) ++ap;
if (p7 == 0 && p8 == 1) ++ap;
if (p8 == 0 && p9 == 1) ++ap;
if (p9 == 0 && p2 == 1) ++ap;

if (ap == 1)
{
if (p2*p4*p8 == 0)
{
if (p2*p6*p8 == 0)
{
//标记
mFlag.push_back(std::make_pair(i, j));
}
}
}
}
}
}
//删除
for (std::vector<std::pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
{
CV_IMAGE_ELEM(dst, uchar, i->first, i->second) = 0;
}

//直到没有点满足,算法结束
if (mFlag.size() == 0)
{
break;
}
else
{
mFlag.clear();//将mFlag清空
}
}
}
3.主函数所在文件 Source.cpp

//Source.cpp
#include "ProcessImage.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#define _TEST
using namespace cv;
int main(int argc, char * argv[])
{
//判断输入是否满足要求
if (argc != 2)
{
std::cout << "argument error!";
return -1;
}
IplImage *pSrc = cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
if (!pSrc)
{
std::cout << "read file failed!";
return -1;
}

//显示原图
namedWindow("原图", CV_WINDOW_AUTOSIZE);
cvShowImage("原图", pSrc);

IplImage *pTemp = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
IplImage *pDst = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);

//将原图像转换为二值图像
cvThreshold(pSrc, pTemp, 128, 1, CV_THRESH_BINARY_INV);
//细化
thinImage(pTemp, pDst);

#ifdef _TEST
//显示细化后的图像
IplImage *pThinImage = cvCreateImage(cvGetSize(pSrc), pSrc->depth, pSrc->nChannels);
cvCopy(pDst, pThinImage);
cvThreshold(pThinImage, pThinImage, 0.5, 255,CV_THRESH_BINARY);
namedWindow("1 图像细化的结果", CV_WINDOW_AUTOSIZE);
cvShowImage("1 图像细化的结果", pThinImage);
cvReleaseImage(&pThinImage);
#endif

//求轮廓
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contours = 0;
cvFindContours(pDst	, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_NONE, cvPoint(0, 0));

#ifdef _TEST
//将轮廓画出来
IplImage *pDrawing1 = cvCreateImage(cvGetSize(pSrc),8,3);
cvZero(pDrawing1);
cvDrawContours(pDrawing1, contours, Scalar(255, 0, 0), Scalar(0, 0, 255), 1, 2, 8, cvPoint(0, 0));
namedWindow("2 求轮廓", CV_WINDOW_AUTOSIZE);
cvShowImage("2 求轮廓", pDrawing1);
cvReleaseImage(&pDrawing1);
#endif

//轮廓已经寻找到,均在contours中存放,我们需要对轮廓进行拟合
//FitLine函数的用法:
// 二维空间点拟合时 是 float[4]
// 三位空间点拟合时 是 float[6]
float *line1 = new float[4];
float *line2 = new float[4];
// 第一个参数: 存储点序列
// 第二个参数: 拟合算法,其中 CV_DIST_L2 就是平常的最小二乘法
// 第三,第四,第五参数推荐值是 0,   0.01,  0.01,
// 第六参数: line中存储返回值
// 二维空间时: line[0--3] 分别为 (vx, vy, x0, y0)
//      其中 vx, vy 是正规化之后的斜率向量。 x0,y0 是直线经过的点。
// 三维空间时: line[0--5]  分别是 (vx, vy, vz, x0, y0, z0) 。意义同上
cvFitLine(contours, CV_DIST_L2, 0, 0.01, 0.01, line1);
cvFitLine(contours->h_next, CV_DIST_L2, 0, 0.01, 0.01, line2);

//输出四个点
std::cout << "第一条线: " << line1[0] << " " << line1[1] << " " << line1[2] << " " << line1[3] << std::endl;
std::cout << "第二条线: " << line2[0] << " " << line2[1] << " " << line2[2] << " " << line2[3] << std::endl;

#ifdef _TEST
//根据直线方程公式,我们从直线上取点,并画出来
IplImage *pDrawing2 = cvCreateImage(cvGetSize(pSrc), 8, 3);
cvZero(pDrawing2);
cvLine(pDrawing2, cvPoint(0, (int)(line1[3] - line1[1] / line1[0] * line1[2])),
cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line1[2])*line1[1] / line1[0] + line1[3])),
cvScalar(255, 0, 0));
cvLine(pDrawing2, cvPoint(0, (int)(line2[3] - line2[1] / line2[0] * line2[2])),
cvPoint(pDrawing2->width - 1, (int)((pDrawing2->width - 1 - line2[2])*line2[1] / line2[0] + line2[3])),
cvScalar(0, 0, 255));
namedWindow("3 直线拟合", CV_WINDOW_AUTOSIZE);
cvShowImage("3 直线拟合", pDrawing2);
cvReleaseImage(&pDrawing2);
#endif

//我们根据距离方程,求出两条直线的距离
double distance = abs(line1[0] * (line2[3]-line1[3]) - line1[1] * (line2[2]-line1[2]));	//注意,vx,vy已经正规化了
std::cout << "两条直线之间的距离为: " << distance << std::endl;
delete[] line1;
delete[] line2;

cvReleaseMemStorage(&storage);
cvReleaseImage(&pSrc);
cvReleaseImage(&pTemp);
cvReleaseImage(&pDst);

waitKey(0);

return 0;
}
运行效果:

输入:



输出:







内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: