OpenCV骨架提取代码
提取骨架常用的方法有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骨架提取代码相关推荐
- opencv 骨架提取/图片细化 代码
其实在opencv里面是有骨架提取的代码的 只不过是在扩展模块opencv-contrib void cv::ximgproc::thinning ( InputArray src,OutputArr ...
- opencv 骨架提取_抗爆墙方盛提取车间抗爆墙记录@温州贴吧
抗爆墙方盛提取车间抗爆墙记录@温州贴吧 河北鼎卓安防设备科技公司pnXPz8uK抗爆墙,泄爆墙专业供应商,是一家集生产.销售及运营于一体的抗爆墙,泄爆墙生产的企业,拥有一支专业性强.技术精.素质高的生 ...
- Zhang-Suen 图像骨架提取算法的原理和OpenCV实现
记录一下图像骨架提取算法,转载至 两种图像骨架提取算法的研究(1)原理部分 基于OpenCV的实现代码如下,代码部分参考 opencv骨架提取/图像细化 void thinImage(Mat & ...
- (代码已更新)QT 环境下 用opencv 进行骨架细化(骨架提取)得到图像中心线
之前的任务是把如下的一个直钢管图像进行处理,提取出中心线,用到了骨架细化算法以及一些常用的opencv处理.思路就是: 预处理通过灰度得到二值图像--二值图形态学处理--骨架细化提取中心线--霍夫概率 ...
- OpenCV PCA提取对象的方向的实例(附完整代码)
OpenCV PCA提取对象的方向的实例 OpenCV PCA提取对象的方向的实例 OpenCV PCA提取对象的方向的实例 #include "opencv2/core.hpp" ...
- python人体行为识别代码_人体行为识别(骨架提取),搭建openpose环境,VS2019(python3.7)+openpose...
这几天开始接触人体行为识别,经过多方对比后,选择了现在最热的人体骨架提取开源库,openpose. 下面就不多说了,直接开始openpose在win10下的配置: 需求如下: 1. VS2019 ...
- 迷宫问题图解 : 基于骨架提取、四邻域
目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 ...
- 基于zhang 的骨架提取
最近在学图像处理的骨架提取,发现很多中文教材对这个方面讲的很有欠缺,于是我决定看英文原文的论文.看了2篇论文, "A fast parallel algorithm for thinning ...
- cv python 样例_【CV实战】OpenCV—Hello world代码示例
简介OpenCV OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,可以运行在Linux.Windows.Android和Mac OS操作系统上.它轻量级而且高效--由一系列 C 函数 ...
- python图片保存为txt文件_python + opencv实现提取png图像的像素信息并存储到txt文件中(附安装指导)...
相关库安装指导: 这里我们需要 opencv_python,numpy,matplotlib库,另外我用的是python3.6.1版本. 一般库大家都是用pip install命令安装的,不过不知道为 ...
最新文章
- DWR之父作序推荐,国内第一部DWR著作
- python语法大全-python基本语法
- MySQl笔记7:MySQL在线模拟平台汇总
- Java 设计模式——外观模式
- 游戏里的角色都什么格式图片_二十年前是怎样开发游戏的?
- 转载一些关于博客的文章
- node.js简单爬虫
- 【codevs1368】【BZOJ1034】泡泡堂BNB,贪心思路
- python自动测试q_阿里大牛教你基于Python的 Selenium自动化测试示例解析
- 4.2.2 - Logical and/or Operators
- 天津盈克斯机器人科技_坐标天津,适合亲子游的科技馆,亮点是机器人展区
- matlab矩阵转置函数
- 指数族分布(2):矩母函数、累积量生成函数
- 周期信号的傅里叶级数
- windows和linux共用蓝牙鼠标,Ubuntu和Windows双系统蓝牙设备共享配对
- 战略盲区,是看不见,还是不想看见?
- HBase【付诸实践 01】hbase shell 常用命令详解(表操作+数据增删改查+2种查询操作)(hbase-2.4.5 单机版standalone模式)
- 华为首次自曝“天才少年”成果:入职不到一年就干成这件大事,网友:值 200 万年薪!...
- rsync下行同步和inotify实时同步部署
- Debian手动安装LNMPA环境及相关配置