相关介绍 http://www.doc88.com/p-7724593025971.html
比较好的介绍 https://www.cnblogs.com/xianglan/archive/2011/01/01/1923779.html
https://www.cnblogs.com/Summerio/p/8284602.html
https://blog.csdn.net/weixinhum/article/details/80261296
//#include "stdafx.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <time.h>
#include <iostream>
using namespace cv;
using namespace std;
/*
* @brief 对输入图像进行细化,骨骼化
* @param src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
* @param dst为对src细化后的输出图像,格式与src格式相同,元素中只有0与1,1代表有元素,0代表为空白
*/
void thinImage(Mat & src, Mat & dst)
{
int width = src.cols;
int height = src.rows;
src.copyTo(dst);
vector<uchar *> mFlag; //用于标记需要删除的点
while (true)
{
//步骤一
for (int i = 0; i < height; ++i)
{
uchar * p = dst.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
//获得九个点对象,注意边界问题
uchar p1 = p[j];
if (p1 != 1) continue;
uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);
uchar p8 = (j == 0) ? 0 : *(p + j - 1);
uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);
if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)//条件1判断
{
//条件2计算
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;
//条件2、3、4判断
if (ap == 1 && p2 * p4 * p6 == 0 && p4 * p6 * p8 == 0)
{
//标记
mFlag.push_back(p + j);
}
}
}
}
//将标记的点删除
for (vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
{
**i = 0;
}
//直到没有点满足,算法结束
if (mFlag.empty())
{
break;
}
else
{
mFlag.clear();//将mFlag清空
}
//步骤二,根据情况该步骤可以和步骤一封装在一起成为一个函数
for (int i = 0; i < height; ++i)
{
uchar * p = dst.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
//如果满足四个条件,进行标记
// p9 p2 p3
// p8 p1 p4
// p7 p6 p5
uchar p1 = p[j];
if (p1 != 1) continue;
uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);
uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);
uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);
uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);
uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + dst.step + j - 1);
uchar p8 = (j == 0) ? 0 : *(p + j - 1);
uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + 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 && p2 * p4 * p8 == 0 && p2 * p6 * p8 == 0)
{
//标记
mFlag.push_back(p + j);
}
}
}
}
//将标记的点删除
for (vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
{
**i = 0;
}
//直到没有点满足,算法结束
if (mFlag.empty())
{
break;
}
else
{
mFlag.clear();//将mFlag清空
}
}
}
/*
* @brief 对输入图像进行端点和交叉点检测,前提是输入图像已经经过骨骼化提取
*/
void endPointAndintersectionPointDetection(Mat & src)
{
int width = src.cols;
int height = src.rows;
vector<CvPoint> endpoint;
vector<CvPoint> intersectionPoint;
//遍历骨骼化后的图像,找到端点和交叉点,分别放入容器中
for (int i = 0; i < height; ++i)
{
uchar * p = src.ptr<uchar>(i);
for (int j = 0; j < width; ++j)
{
//获得九个点对象,注意边界问题
uchar p1 = p[j];
if (p1 != 1) continue;
uchar p2 = (i == 0) ? 0 : *(p - src.step + j);
uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - src.step + j + 1);
uchar p4 = (j == width - 1) ? 0 : *(p + j + 1);
uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + src.step + j + 1);
uchar p6 = (i == height - 1) ? 0 : *(p + src.step + j);
uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + src.step + j - 1);
uchar p8 = (j == 0) ? 0 : *(p + j - 1);
uchar p9 = (i == 0 || j == 0) ? 0 : *(p - src.step + j - 1);
if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) == 1)//端点判断
{
printf("端点:%d %d\n", i, j);
endpoint.push_back(cvPoint(j, i));
}
else //交叉点判断
{
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 >= 3)
{
printf("交叉点:%d %d\n", i, j);
intersectionPoint.push_back(cvPoint(j, i));
}
}
}
}
//画出端点
for (vector<CvPoint>::iterator i = endpoint.begin(); i != endpoint.end(); ++i)
{
circle(src, cvPoint(i->x, i->y), 5, Scalar(255), -1);
}
//画出交叉点
for (vector<CvPoint>::iterator i = intersectionPoint.begin(); i != intersectionPoint.end(); ++i)
{
circle(src, cvPoint(i->x, i->y), 5, Scalar(255));
}
endpoint.clear();//数据回收
intersectionPoint.clear();
}
int main()
{
Mat src = imread("5.png", IMREAD_GRAYSCALE);
GaussianBlur(src, src, Size(7, 7), 0, 0);//高斯滤波
threshold(src, src, 140, 1, cv::THRESH_BINARY_INV);//二值化,前景为1,背景为0
Mat dst;
thinImage(src, dst);//图像细化(骨骼化)
endPointAndintersectionPointDetection(dst);
src = src * 255;
imshow("原始图像", src);
dst = dst * 255;
imshow("细化图像", dst);
waitKey(0);
}
在源文件的同级目录下放入要编辑的图片(png格式),命名为5.png
编译(需安装opencv):
g++ gujiatiqu.cpp -o gujia `pkg-config opencv –cflags –libs`
上面的引号是键盘左上角的`,就是数字键1左边的那个键,谨记!