此篇文章转载自:https://www.cnblogs.com/jsxyhelu/p/10295373.html
首先上图:
这里的目标是寻找六条线段的交叉点,我一开始看到这个图片的时候觉得很简单,认为通过霍夫找线段然后求取交叉点就能实现,果不其然,我想太多了,经过对OpenCV里HoughLinesP()函数的参数的各种调节,始终找不到所有的完整的线段,我觉得很大的原因可能是因为图片像素过高,后面我会继续摸索。
下面带来博客大神实现的方法(也存在一定的瑕疵,但效果已经很不错了):
1、这样一个图像,单通道所以无法做HSV分解,必须考虑到光照影响,所以不能采用OSTU,经验告诉我,直接二值化能够得到较好效果。
2、得到的结果,可能会有孤立点,需要通过形态学方法去除
这里我们需要做的事“开运算”,根据观察,我们采用5*5的核:
得到的结果是奇异点全部消失,但是现有轮廓会有所衰弱
3、进一步执行细化,精确定位,也会出现细微偏差
4、那么,这个图像质量,我们如何建立特征提取模型了? 对于这样一个点
4个象限,那么对于我们这些交点来说,肯定是有特征的,比如上图表示,对于这幅图像中的所有的点,可以区分为1-4个象限;就目前这4个象限中来看,每个象限都存在有数据的区域。但是经过编码发现,由于原图中有圆形等其他图像,干扰比较大,所以可以进一步增加约束为“寻找所有该点本身、其左边、右边、上边、下边都为255的点,由于这是一个比较明显的特征,所以我们可以直接编写代码去提取。
#include
#include "opencv2\core.hpp"
#include "opencv2/shape.hpp"
#include "opencv2\opencv_modules.hpp"
#include
#include
using namespace cv;
using namespace std;
#define DIRECTION_X 0
#define FALSE 0
#define TRUE 1
void thin(const Mat &src, Mat &dst, const int iterations = 100);
void projection2(Mat src, vector& vup, vector& vdown, int direction = DIRECTION_X, int gap = 10);
int main()
{
Mat src = imread("./image/寻找激光的交叉点.bmp", 0);
Mat srcClone = src.clone();
Mat srcClone1 = src.clone();
Mat srcBoard(src.size(), CV_8U, Scalar::all(0));
//阈值处理
threshold(src, src, 100, 255, THRESH_BINARY);
//形态学去噪音
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
//开运算,先腐蚀再膨胀,平滑物体轮廓,断开较窄的地方,清除细突
morphologyEx(src, src, CV_MOP_OPEN, element);
//二值图像细化处理
thin(src, src);
namedWindow("src", WINDOW_NORMAL);
imshow("src", src);
imwrite("srccc.jpg", src);
//遍历图像,这里我在遍历的时候缩了一个像素
vector vecPoints;
for (int i = 1; i < src.rows - 1; i++) {
for (int j = 1; j < src.cols - 1; j++) {
if (src.at(i, j) == 255) //首先该像素要有数据
{
if (src.at(i - 1, j) == 255 && src.at(i + 1, j) == 255 && src.at(i, j - 1) == 255 && src.at(i, j + 1) == 255)
{
vecPoints.push_back(Point(j, i));
}
}
}
}
//在底图上进行绘制
cvtColor(srcClone, srcClone, COLOR_GRAY2BGR);
cvtColor(srcClone1, srcClone1, COLOR_GRAY2BGR);
//Point a;
//a.x = vecPoints[0].x;
//a.y = vecPoints[4].y;
//circle(srcClone1, a, 10, Scalar(0, 0, 255), -1);
//namedWindow("srcClone1", WINDOW_NORMAL);
//imshow("srcClone1", srcClone1);
//imwrite("srcClone1.jpg", srcClone1);
for (int i = 0; i < vecPoints.size(); i++)
{
circle(srcClone, vecPoints[i], 10, Scalar(0, 0, 255), -1);
circle(srcBoard, vecPoints[i], 1, Scalar(255), -1);
}
namedWindow("srcClone", WINDOW_NORMAL);
imshow("srcClone", srcClone);
imwrite("srcClone.jpg", srcClone);
waitKey();
return 0;
}
//将 DEPTH_8U型二值图像进行细化 经典的Zhang并行快速细化算法
void thin(const Mat &src, Mat &dst, const int iterations) {
const int height = src.rows - 1;
const int width = src.cols - 1;
//拷贝一个数组给另一个数组
if (src.data != dst.data)
src.copyTo(dst);
int n = 0, i = 0, j = 0;
Mat tmpImg;
uchar *pU, *pC, *pD;
bool isFinished = FALSE;
for (n = 0; n < iterations; n++) {
dst.copyTo(tmpImg);
isFinished = FALSE; //一次 先行后列扫描 开始
//扫描过程一 开始
for (i = 1; i < height; i++) {
pU = tmpImg.ptr(i - 1);
pC = tmpImg.ptr(i);
pD = tmpImg.ptr(i + 1);
for (int j = 1; j < width; j++) {
if (pC[j] > 0) {
int ap = 0;
int p2 = (pU[j] > 0);
int p3 = (pU[j + 1] > 0);
if (p2 == 0 && p3 == 1)
ap++;
int p4 = (pC[j + 1] > 0);
if (p3 == 0 && p4 == 1)
ap++;
int p5 = (pD[j + 1] > 0);
if (p4 == 0 && p5 == 1)
ap++;
int p6 = (pD[j] > 0);
if (p5 == 0 && p6 == 1)
ap++;
int p7 = (pD[j - 1] > 0);
if (p6 == 0 && p7 == 1)
ap++;
int p8 = (pC[j - 1] > 0);
if (p7 == 0 && p8 == 1)
ap++;
int p9 = (pU[j - 1] > 0);
if (p8 == 0 && p9 == 1)
ap++;
if (p9 == 0 && p2 == 1)
ap++;
if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > 1 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < 7) {
if (ap == 1) {
if ((p2*p4*p6 == 0) && (p4*p6*p8 == 0)) {
dst.ptr(i)[j] = 0;
isFinished = TRUE;
}
}
}
}
} //扫描过程一 结束
dst.copyTo(tmpImg);
//扫描过程二 开始
for (i = 1; i < height; i++) {
pU = tmpImg.ptr(i - 1);
pC = tmpImg.ptr(i);
pD = tmpImg.ptr(i + 1);
for (int j = 1; j < width; j++) {
if (pC[j] > 0) {
int ap = 0;
int p2 = (pU[j] > 0);
int p3 = (pU[j + 1] > 0);
if (p2 == 0 && p3 == 1)
ap++;
int p4 = (pC[j + 1] > 0);
if (p3 == 0 && p4 == 1)
ap++;
int p5 = (pD[j + 1] > 0);
if (p4 == 0 && p5 == 1)
ap++;
int p6 = (pD[j] > 0);
if (p5 == 0 && p6 == 1)
ap++;
int p7 = (pD[j - 1] > 0);
if (p6 == 0 && p7 == 1)
ap++;
int p8 = (pC[j - 1] > 0);
if (p7 == 0 && p8 == 1)
ap++;
int p9 = (pU[j - 1] > 0);
if (p8 == 0 && p9 == 1)
ap++;
if (p9 == 0 && p2 == 1)
ap++;
if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > 1 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < 7) {
if (ap == 1) {
if ((p2*p4*p8 == 0) && (p2*p6*p8 == 0)) {
dst.ptr(i)[j] = 0;
isFinished = TRUE;
}
}
}
}
}
} //一次 先行后列扫描完成
//如果在扫描过程中没有删除点,则提前退出
if (isFinished == FALSE)
break;
}
}
}
得到以下结果:
可以看到,并没有得到全部的交叉点,我们需要对得到有限的点进行补全和排序,具体情况还是要具体分析,这里只做到这了!