基于C++的骨架提取的鼻祖算法
算法是基于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++的骨架提取的鼻祖算法相关推荐
- 单像素骨架提取算法c语言实现,【图像】骨架提取与分水岭算法
1.骨架提取 骨架提取,也叫二值图像细化.这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示. morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize ...
- 基于zhang 的骨架提取
最近在学图像处理的骨架提取,发现很多中文教材对这个方面讲的很有欠缺,于是我决定看英文原文的论文.看了2篇论文, "A fast parallel algorithm for thinning ...
- 骨架提取matlab细化算法,基于拉普拉斯收缩的三维模型骨架提取算法及其Matlab实现...
3D skeleton extraction algorithm via Laplace contraction and its realization in Matlab JI Weijie 1 冀 ...
- 迷宫问题图解 : 基于骨架提取、四邻域
目录 1. 迷宫的连通域 2. How to remove branch ? 3. 基于4邻域的 remove 分支 3.1 找到分支的端点 3.2 4邻域的 remove 分支 3.3 循环移除分支 ...
- Zhang-Suen 图像骨架提取算法的原理和OpenCV实现
记录一下图像骨架提取算法,转载至 两种图像骨架提取算法的研究(1)原理部分 基于OpenCV的实现代码如下,代码部分参考 opencv骨架提取/图像细化 void thinImage(Mat & ...
- 基于TextRank的关键词提取算法
基于TextRank的关键词提取算法 前沿 TextRank是一种文本排序算法,是基于著名的网页排序算法PageRank改动而来.在介绍TextRank前,我们先简单介绍下什么是PageRank.另外 ...
- java骨架_基于Mat变换的骨架提取Java
针对一副二值图像,区域内的点只有背景点(白点,0值)和前景点(黑点,1值).对于给定区域的像素点逐次应用两个基本步骤,以提取骨架: step1,如果一个像素点满足下列4个条件,那么将它标记为要删除的点 ...
- 两种图像骨架提取算法的研究原理及实现
图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓.说白了就是以目标中心为准,对目标进行细化,一般细化后的目标都是单层像素宽度.比如输入图像是这样: 输出骨架图像(红色) 关于骨架提取,现存的算法 ...
- 基于QT和Node.js的八叉树算法提取图片主题色
资源下载地址:https://download.csdn.net/download/sheziqiong/85883609 资源下载地址:https://download.csdn.net/downl ...
最新文章
- 1.3.2 向量化实现浅层神经网络
- Python学习之路--装饰器
- java大作业斗地主游戏_Java集合练习:斗地主游戏
- html显示docx,网页中显示PDF的HTML代码.docx
- [Alpha]Scrum Meeting#5
- 自定义类型详解:结构体(内存对齐、位段) + 枚举 + 联合
- JSP变量和方法声明
- 使用内存映射提高BufferedRandoAccessFile性能(测试可用)
- Ubuntu中如何安装安装QQ
- 百度地图获取规划路径信息
- Github优秀Android开源项目,值得引用与学习(图文结合~~~)
- A1088 Rational Arithmetic (20 分)
- unity-2D游戏地面检测 三射线检测
- 【HTML总复习】一文带你查漏补缺,暖你一整天
- 期权希腊值之delta【python复现】
- android 加密手机功能,怎么为安卓手机加密
- 织梦系统(DEDECMS)后台模板修改一
- 计算机软件著作权必须登记吗?根据国家法律法规规定,计算机软件著作权登记与取得著作权有没有关系?
- centsos7网络连接激活失败_CenOS 7初始安装后无网络的解决办法
- “驯服”振荡运算放大器(2)——外部容性负载
热门文章
- 吴恩达Drive.ai因经营困难“卖身”苹果
- python时间日期字符串各种
- ng new ng-pro 报错(创建angular6项目报错)
- react组件回顶部
- Leetcode(18)-四数之和
- 最后一片蓝海的终极狂欢-写在Win10发布前夕
- 自己动手写简单的web应用服务器(4)—利用socket实现文件的下载
- .net ConfigurationSectionDesigner插件使用
- DevExpress A field with the name '' was not found on the selected data source.
- EF-Entity Framework 相关技术点收集贴