提取骨架常用的方法有zhang快速细化法,rosenfeld方法等。两种算法的原理可以参考:贾挺猛. 葡萄树冬剪机器人剪枝点定位方法研究[D]. 浙江工业大学, 2012.

这篇文章中有比较详细的解释。此外《数字图像处理》(第三版)-冈萨雷斯,9.5.7和11.1.7两个小节对骨架提取问题进行了比较详细的介绍。下面是实现两种算法的代码。

#define p1 SrcImage.at<uchar>(i, j)
#define p2 SrcImage.at<uchar>(i - 1, j)
#define p3 SrcImage.at<uchar>(i - 1, j + 1)
#define p4 SrcImage.at<uchar>(i, j + 1)
#define p5 SrcImage.at<uchar>(i + 1, j + 1)
#define p6 SrcImage.at<uchar>(i + 1, j)
#define p7 SrcImage.at<uchar>(i + 1, j - 1)
#define p8 SrcImage.at<uchar>(i, j - 1)
#define p9 SrcImage.at<uchar>(i - 1, j - 1)
#define p10 p2
void ZhangFindSkeleton(Mat SrcImage, Mat &DstImage)//输入的目标像素为1,背景像素为0
{  /*0:先对图像的边缘进行处理,避免图片的边缘对骨架提取造成的影响——如果图片四边都为0则没有必要*/  for (int j = 0;j < SrcImage.cols;j++)  {  SrcImage.at<uchar>(0, j) = 0;  SrcImage.at<uchar>(SrcImage.rows - 1, j) = 0;  }  for (int i = 0;i < SrcImage.rows;i++)  {  SrcImage.at<uchar>(i, 0) = 0;  SrcImage.at<uchar>(i, SrcImage.cols - 1) = 0;  }  /*0:记录所有非零点的坐标*/  vector<Point>target;  for (int i = 1;i < SrcImage.rows - 1;i++)  {  for (int j = 1;j < SrcImage.cols - 1;j++)  {  if (SrcImage.at<uchar>(i, j) == 1)  {  target.push_back(Point(i, j));  }  }  }  //--------------------------Zhang----------------------------------  int flag = 1;  while (flag)  {  flag = 0;//清空标志位  /*1:检测像素点是否满足Zhang快速细化法步骤1的四个条件*/  vector<Point> erase,reserve;  for (int k = 0;k < target.size();k++)//只检测已经记录的点  {  int i = target[k].x, j = target[k].y;  //检测条件a 2<=N(p1)<=6  int n = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;  if (n < 2 || n > 6)//不满足条件a,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件b T(p1)=1  int t = 0;  if (p2 == 0 && p3 == 1)  t++;  if (p3 == 0 && p4 == 1)  t++;  if (p4 == 0 && p5 == 1)  t++;  if (p5 == 0 && p6 == 1)  t++;  if (p6 == 0 && p7 == 1)  t++;  if (p7 == 0 && p8 == 1)  t++;  if (p8 == 0 && p9 == 1)  t++;  if (p9 == 0 && p10 == 1)  t++;  if (t != 1)//不满足条件b,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件c p2*p4*p6=0  if (p2 * p4 * p6 != 0)//不满足条件c,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件d  p4*p6*p8=0  if (p4 * p6 * p8 != 0)//不满足条件d,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //已经满足了所有的条件,该点可以被删除,使用vector记录下当前要删除的点的坐标  erase.push_back(Point(i, j));//其实这里是反的,在后面又重新反了回来  flag = 1;//置1标志位,表示有过修改  }  /*2:删除所有标记点*/  for (int i = 0;i < erase.size();i++)  {  SrcImage.at<uchar>(erase[i].x, erase[i].y) = 0;  }  erase.clear();  target.clear();  target = reserve;//更新当前的非零点坐标  reserve.clear();  /*3:检测像素点是否满足Zhang快速细化法步骤2的四个条件*/  for (int k = 0;k < target.size();k++)  {  int i = target[k].x, j = target[k].y;  //检测条件a 2<=N(p1)<=6  int n = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;  if (n < 2 || n > 6)//不满足条件a,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件b T(p1)=1  int t = 0;  if (p2 == 0 && p3 == 1)  t++;  if (p3 == 0 && p4 == 1)  t++;  if (p4 == 0 && p5 == 1)  t++;  if (p5 == 0 && p6 == 1)  t++;  if (p6 == 0 && p7 == 1)  t++;  if (p7 == 0 && p8 == 1)  t++;  if (p8 == 0 && p9 == 1)  t++;  if (p9 == 0 && p10 == 1)  t++;  if (t != 1)//不满足条件b,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件c p2*p4*p8=0  if (p2 * p4 * p8 != 0)//不满足条件c,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //检测条件d  p2*p6*p8=0  if (p2 * p6 * p8 != 0)//不满足条件d,该点不能删除,直接检测下一个点  {  reserve.push_back(Point(i, j));  continue;  }  //已经满足了所有的条件,该点可以被删除,使用vector记录下当前要删除的点的坐标  erase.push_back(Point(i, j));  flag = 1;//置1标志位,表示有过修改  }  /*4:删除所有标记点*/  for (int i = 0;i < erase.size();i++)  {  SrcImage.at<uchar>(erase[i].x, erase[i].y) = 0;  }  erase.clear();  target.clear();  target = reserve;//更新当前的非零点坐标  reserve.clear();  }  DstImage = SrcImage;
}
void rosenfeld(Mat src, Mat &dst)
{  for (int t = 0;t < 10;t++)//这里的迭代次数不限,可以检查到没有更新之后再跳出  {  /*可以事先保存下所有的为1的点,避免后面再全部遍历,提高运算速度*/  for (int i = 1;i < src.rows-1;i++)  {  for (int j = 1;j < src.cols-1;j++)  {  if (src.at<uchar>(i, j) == 1)//对该点为1的进行检测  {  if (src.at<uchar>(i, j + 1) == 0 || src.at<uchar>(i, j - 1) == 0 || src.at<uchar>(i + 1, j) == 0 || src.at<uchar>(i - 1, j) == 0)//第一轮模板匹配  {  /*模板匹配成功,目标点位于边界位置,进行第二轮模板匹配*/  //中间一列为0,1,0  if (src.at<uchar>(i - 1, j) + src.at<uchar>(i + 1, j) == 0)  {  if (src.at<uchar>(i, j + 1) + src.at<uchar>(i - 1, j + 1) + src.at<uchar>(i + 1, j + 1) + src.at<uchar>(i, j - 1) + src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i + 1, j - 1) >= 1)//至少有一个1  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  else  {  src.at<uchar>(i, j) = 0;  }  }  //中间一行为0,1,0  else if (src.at<uchar>(i, j - 1) + src.at<uchar>(i, j + 1) == 0)  {  if (src.at<uchar>(i + 1, j) + src.at<uchar>(i + 1, j - 1) + src.at<uchar>(i + 1, j + 1) + src.at<uchar>(i - 1, j) + src.at<uchar>(i - 1, j - 1) + src.at<uchar>(i - 1, j + 1) >= 1)  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  else  {  src.at<uchar>(i, j) = 0;  }  }  //左下角为1  else if (src.at<uchar>(i + 1, j - 1) == 1 && (src.at<uchar>(i, j - 1) + src.at<uchar>(i + 1, j) == 0))  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  //右下角为1  else if (src.at<uchar>(i + 1, j + 1) == 1 && (src.at<uchar>(i + 1, j) + src.at<uchar>(i, j + 1) == 0))  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  //左上角为1  else if (src.at<uchar>(i - 1, j - 1) == 1 && (src.at<uchar>(i - 1, j) + src.at<uchar>(i, j - 1) == 0))  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  //右上角为1  else if (src.at<uchar>(i - 1, j + 1) == 1 && (src.at<uchar>(i - 1, j) + src.at<uchar>(i, j + 1) == 0))  {  //匹配成功,不应该删除,继续寻找下一个点  continue;  }  //上述匹配都不成功  else  {  src.at<uchar>(i, j) = 0;  }  }  //模板匹配失败,进行下一个点的匹配  }  }  }  }  dst = src;
}

测试过程中,方法1效果比较好,但是花费的时间比较多,如果有必要可以对骨架的毛刺进行处理。

OpenCV骨架提取代码相关推荐

  1. opencv 骨架提取/图片细化 代码

    其实在opencv里面是有骨架提取的代码的 只不过是在扩展模块opencv-contrib void cv::ximgproc::thinning ( InputArray src,OutputArr ...

  2. opencv 骨架提取_抗爆墙方盛提取车间抗爆墙记录@温州贴吧

    抗爆墙方盛提取车间抗爆墙记录@温州贴吧 河北鼎卓安防设备科技公司pnXPz8uK抗爆墙,泄爆墙专业供应商,是一家集生产.销售及运营于一体的抗爆墙,泄爆墙生产的企业,拥有一支专业性强.技术精.素质高的生 ...

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

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

  4. (代码已更新)QT 环境下 用opencv 进行骨架细化(骨架提取)得到图像中心线

    之前的任务是把如下的一个直钢管图像进行处理,提取出中心线,用到了骨架细化算法以及一些常用的opencv处理.思路就是: 预处理通过灰度得到二值图像--二值图形态学处理--骨架细化提取中心线--霍夫概率 ...

  5. OpenCV PCA提取对象的方向的实例(附完整代码)

    OpenCV PCA提取对象的方向的实例 OpenCV PCA提取对象的方向的实例 OpenCV PCA提取对象的方向的实例 #include "opencv2/core.hpp" ...

  6. python人体行为识别代码_人体行为识别(骨架提取),搭建openpose环境,VS2019(python3.7)+openpose...

    这几天开始接触人体行为识别,经过多方对比后,选择了现在最热的人体骨架提取开源库,openpose. 下面就不多说了,直接开始openpose在win10下的配置: 需求如下: 1. VS2019    ...

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

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

  8. 基于zhang 的骨架提取

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

  9. cv python 样例_【CV实战】OpenCV—Hello world代码示例

    简介OpenCV OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数 ...

  10. python图片保存为txt文件_python + opencv实现提取png图像的像素信息并存储到txt文件中(附安装指导)...

    相关库安装指导: 这里我们需要 opencv_python,numpy,matplotlib库,另外我用的是python3.6.1版本. 一般库大家都是用pip install命令安装的,不过不知道为 ...

最新文章

  1. DWR之父作序推荐,国内第一部DWR著作
  2. python语法大全-python基本语法
  3. MySQl笔记7:MySQL在线模拟平台汇总
  4. Java 设计模式——外观模式
  5. 游戏里的角色都什么格式图片_二十年前是怎样开发游戏的?
  6. 转载一些关于博客的文章
  7. node.js简单爬虫
  8. 【codevs1368】【BZOJ1034】泡泡堂BNB,贪心思路
  9. python自动测试q_阿里大牛教你基于Python的 Selenium自动化测试示例解析
  10. 4.2.2 - Logical and/or Operators
  11. 天津盈克斯机器人科技_坐标天津,适合亲子游的科技馆,亮点是机器人展区
  12. matlab矩阵转置函数
  13. 指数族分布(2):矩母函数、累积量生成函数
  14. 周期信号的傅里叶级数
  15. windows和linux共用蓝牙鼠标,Ubuntu和Windows双系统蓝牙设备共享配对
  16. 战略盲区,是看不见,还是不想看见?
  17. HBase【付诸实践 01】hbase shell 常用命令详解(表操作+数据增删改查+2种查询操作)(hbase-2.4.5 单机版standalone模式)
  18. 华为首次自曝“天才少年”成果:入职不到一年就干成这件大事,网友:值 200 万年薪!...
  19. rsync下行同步和inotify实时同步部署
  20. Debian手动安装LNMPA环境及相关配置

热门文章

  1. Java面试知识点(零)Java零碎知识点
  2. 美的集团计算机综合面试群面,美的集团面试
  3. Android P 禁用OTG U盘使用
  4. Windows学习总结(12)——Windows 10系统开始运行-cmd命令大全
  5. 研究鸟类迁徙的目的和意义
  6. 轻量级openpose解析
  7. 【重点】心田花开:人教版初一语文上册《从百草园到三味书屋》知识点
  8. 一张图搞懂什么是M0、M1 、M2
  9. datasupport类删除_关于xcode:我可以从iOS DeviceSupport删除数据吗?
  10. Excel 行列转换的最简方法 1