算法是基于A Fast Parallel Algorithm for Thinning Digital Patterns论文

https://blog.csdn.net/keneyr/article/details/88944563

简单的解释算法:

https://blog.csdn.net/xukaiwen_2016/article/details/53135866

#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <vector>
using namespace cv;
using namespace std;/**
* @brief 对输入图像进行细化,骨骼化
* @param src为输入图像,用cvThreshold函数处理过的8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
* @param maxIterations限制迭代次数,如果不进行限制,默认为-1,代表不限制迭代次数,直到获得最终结果
* @return 为对src细化后的输出图像,格式与src格式相同,元素中只有0与1,1代表有元素,0代表为空白
*/
cv::Mat thinImage(const cv::Mat & src, const int maxIterations = -1)
{assert(src.type() == CV_8UC1);cv::Mat dst;int width = src.cols;int height = src.rows;src.copyTo(dst);int count = 0;  //记录迭代次数  while (true){count++;if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达  break;std::vector<uchar *> 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 p4 = (j == width - 1) ? 0 : *(p + j + 1);uchar p8 = (j == 0) ? 0 : *(p + j - 1);uchar p2 = (i == 0) ? 0 : *(p - dst.step + j);uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - dst.step + j + 1);uchar p9 = (i == 0 || j == 0) ? 0 : *(p - dst.step + j - 1);uchar p6 = (i == height - 1) ? 0 : *(p + dst.step + j);uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + dst.step + j + 1);uchar p7 = (i == height - 1 || 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 * p6 == 0 && p4 * p6 * p8 == 0){//标记  mFlag.push_back(p + j);}}}}//将标记的点删除  for (std::vector<uchar *>::iterator i = mFlag.begin(); i != mFlag.end(); ++i){**i = 0;}//直到没有点满足,算法结束  if (mFlag.empty()){break;}else{mFlag.clear();//将mFlag清空  }}return dst;
}/**
* @brief 对骨骼化图数据进行过滤,实现两个点之间至少隔一个空白像素
* @param thinSrc为输入的骨骼化图像,8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
*/
void filterOver(cv::Mat thinSrc)
{assert(thinSrc.type() == CV_8UC1);int width = thinSrc.cols;int height = thinSrc.rows;for (int i = 0; i < height; ++i){uchar * p = thinSrc.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 p4 = (j == width - 1) ? 0 : *(p + j + 1);uchar p8 = (j == 0) ? 0 : *(p + j - 1);uchar p2 = (i == 0) ? 0 : *(p - thinSrc.step + j);uchar p3 = (i == 0 || j == width - 1) ? 0 : *(p - thinSrc.step + j + 1);uchar p9 = (i == 0 || j == 0) ? 0 : *(p - thinSrc.step + j - 1);uchar p6 = (i == height - 1) ? 0 : *(p + thinSrc.step + j);uchar p5 = (i == height - 1 || j == width - 1) ? 0 : *(p + thinSrc.step + j + 1);uchar p7 = (i == height - 1 || j == 0) ? 0 : *(p + thinSrc.step + j - 1);if (p2 + p3 + p8 + p9 >= 1){p[j] = 0;}}}
}/**
* @brief 从过滤后的骨骼化图像中寻找端点和交叉点
* @param thinSrc为输入的过滤后骨骼化图像,8位灰度图像格式,元素中只有0与1,1代表有元素,0代表为空白
* @param raudis卷积半径,以当前像素点位圆心,在圆范围内判断点是否为端点或交叉点
* @param thresholdMax交叉点阈值,大于这个值为交叉点
* @param thresholdMin端点阈值,小于这个值为端点
* @return 为对src细化后的输出图像,格式与src格式相同,元素中只有0与1,1代表有元素,0代表为空白
*/
std::vector<cv::Point> getPoints(const cv::Mat &thinSrc, unsigned int raudis = 4, unsigned int thresholdMax = 6, unsigned int thresholdMin = 4)
{assert(thinSrc.type() == CV_8UC1);int width = thinSrc.cols;int height = thinSrc.rows;cv::Mat tmp;thinSrc.copyTo(tmp);std::vector<cv::Point> points;cout << tmp.step << endl;for (int i = 0; i < height; ++i){for (int j = 0; j < width; ++j){if (*(tmp.data + tmp.step * i + j) == 0){continue;}int count = 0;for (int k = i - raudis; k < i + raudis + 1; k++){for (int l = j - raudis; l < j + raudis + 1; l++){if (k < 0 || l < 0 || k>height - 1 || l>width - 1){continue;}else if (*(tmp.data + tmp.step * k + l) == 1){count++;}}}if (count > thresholdMax || count<thresholdMin){Point point(j, i);points.push_back(point);}}}return points;
}int main(int argc, char*argv[])
{cv::Mat src;//获取图像  src = cv::imread("binary.jpg", cv::IMREAD_GRAYSCALE);//将原图像转换为二值图像  cv::threshold(src, src, 20, 1, cv::THRESH_BINARY);//图像细化,骨骼化  cv::Mat dst = thinImage(src);cv::Mat result = cv::Mat::zeros(dst.size(), CV_8UC3);//过滤细化后的图像filterOver(dst);//查找端点和交叉点  std::vector<cv::Point> points = getPoints(dst, 4,6, 4);//二值图转化成灰度图,并绘制找到的点dst = dst * 255;cv::cvtColor(dst, result, cv::COLOR_GRAY2BGR);vector<cv::Point>::iterator it = points.begin();for (; it != points.end(); it++){//circle(result, *it, 2, cv::Scalar(255,0,0), -1);}imwrite("dst.jpg", result);//显示图像  //cv::namedWindow("dst1", CV_WINDOW_AUTOSIZE);//cv::imshow("dst1", result);//cv::waitKey(0);
}

基于C++的骨架提取的鼻祖算法相关推荐

  1. 单像素骨架提取算法c语言实现,【图像】骨架提取与分水岭算法

    1.骨架提取 骨架提取,也叫二值图像细化.这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示. morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize ...

  2. 基于zhang 的骨架提取

    最近在学图像处理的骨架提取,发现很多中文教材对这个方面讲的很有欠缺,于是我决定看英文原文的论文.看了2篇论文, "A fast parallel algorithm for thinning ...

  3. 骨架提取matlab细化算法,基于拉普拉斯收缩的三维模型骨架提取算法及其Matlab实现...

    3D skeleton extraction algorithm via Laplace contraction and its realization in Matlab JI Weijie 1 冀 ...

  4. 迷宫问题图解 : 基于骨架提取、四邻域

    目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 ...

  5. Zhang-Suen 图像骨架提取算法的原理和OpenCV实现

    记录一下图像骨架提取算法,转载至 两种图像骨架提取算法的研究(1)原理部分 基于OpenCV的实现代码如下,代码部分参考 opencv骨架提取/图像细化 void thinImage(Mat & ...

  6. 基于TextRank的关键词提取算法

    基于TextRank的关键词提取算法 前沿 TextRank是一种文本排序算法,是基于著名的网页排序算法PageRank改动而来.在介绍TextRank前,我们先简单介绍下什么是PageRank.另外 ...

  7. java骨架_基于Mat变换的骨架提取Java

    针对一副二值图像,区域内的点只有背景点(白点,0值)和前景点(黑点,1值).对于给定区域的像素点逐次应用两个基本步骤,以提取骨架: step1,如果一个像素点满足下列4个条件,那么将它标记为要删除的点 ...

  8. 两种图像骨架提取算法的研究原理及实现

    图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓.说白了就是以目标中心为准,对目标进行细化,一般细化后的目标都是单层像素宽度.比如输入图像是这样: 输出骨架图像(红色) 关于骨架提取,现存的算法 ...

  9. 基于QT和Node.js的八叉树算法提取图片主题色

    资源下载地址:https://download.csdn.net/download/sheziqiong/85883609 资源下载地址:https://download.csdn.net/downl ...

最新文章

  1. 1.3.2 向量化实现浅层神经网络
  2. Python学习之路--装饰器
  3. java大作业斗地主游戏_Java集合练习:斗地主游戏
  4. html显示docx,网页中显示PDF的HTML代码.docx
  5. [Alpha]Scrum Meeting#5
  6. 自定义类型详解:结构体(内存对齐、位段) + 枚举 + 联合
  7. JSP变量和方法声明
  8. 使用内存映射提高BufferedRandoAccessFile性能(测试可用)
  9. Ubuntu中如何安装安装QQ
  10. 百度地图获取规划路径信息
  11. Github优秀Android开源项目,值得引用与学习(图文结合~~~)
  12. A1088 Rational Arithmetic (20 分)
  13. unity-2D游戏地面检测 三射线检测
  14. 【HTML总复习】一文带你查漏补缺,暖你一整天
  15. 期权希腊值之delta【python复现】
  16. android 加密手机功能,怎么为安卓手机加密
  17. 织梦系统(DEDECMS)后台模板修改一
  18. 计算机软件著作权必须登记吗?根据国家法律法规规定,计算机软件著作权登记与取得著作权有没有关系?
  19. centsos7网络连接激活失败_CenOS 7初始安装后无网络的解决办法
  20. “驯服”振荡运算放大器(2)——外部容性负载

热门文章

  1. 吴恩达Drive.ai因经营困难“卖身”苹果
  2. python时间日期字符串各种
  3. ng new ng-pro 报错(创建angular6项目报错)
  4. react组件回顶部
  5. Leetcode(18)-四数之和
  6. 最后一片蓝海的终极狂欢-写在Win10发布前夕
  7. 自己动手写简单的web应用服务器(4)—利用socket实现文件的下载
  8. .net ConfigurationSectionDesigner插件使用
  9. DevExpress A field with the name '' was not found on the selected data source.
  10. EF-Entity Framework 相关技术点收集贴