我这是测试了两个人的代码,似乎有些区别的,第二篇作者贴出来的代码还存在一些bug,我简单修改了一下,实现的效果上似乎是有一下差别,后续看看论文再做评价。

两个方法也都能满足一定的需求。

参考blog:

https://blog.csdn.net/qianchenglenger/article/details/19332011

https://blog.csdn.net/qq826309057/article/details/73008608

--------------------以下文字是第一篇的转载内容,代码我简单的测试demo--------------------------

在我们进行图像处理的时候,有可能需要对图像进行细化,提取出图像的骨架信息,进行更加有效的分析。

图像细化(Image Thinning),一般指二值图像的骨架化(Image Skeletonization) 的一种操作运算。

所谓的细化就是经过一层层的剥离,从原来的图中去掉一些点,但仍要保持原来的形状,直到得到图像的骨架。骨架,可以理解为图象的中轴。

好的细化算法一定要满足:

  • 收敛性;
  • 保证细化后细线的连通性;
  • 保持原图的基本形状;
  • 减少笔画相交处的畸变;
  • 细化结果是原图像的中心线;
  • 细化的快速性和迭代次数少;

这里,我们对“Zhang并行快速细化算法”进行了实现(注意,该算法为并行算法,而我们在实现过程中并没有并行化处理,所以,效率并没有达到最好)。

参考资料

细化算法
论文 A fast parallel algorithm for thinning digital patterns

// ThinImageDemo.cpp : 定义控制台应用程序的入口点。
//#include "stdafx.h"#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <vector>
using namespace cv;void zhangSkeleton(Mat &srcimage);/*** @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 p5uchar 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清空}//对点标记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 p5uchar 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 * p8 == 0 && p2 * 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;
}int main(int argc, char*argv[])
{//获取图像//if (argc != 2)//{//    std::cout << "参数个数错误!" << std::endl;//    return -1;//}cv::Mat src = cv::imread("1.jpg", cv::IMREAD_GRAYSCALE);//cv::Mat src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);if (src.empty()){std::cout << "读取文件失败!" << std::endl;return -1;}//将原图像转换为二值图像//cv::threshold(src, src, 200, 1, cv::THRESH_BINARY);cv::threshold(src, src, 200, 255, cv::THRESH_BINARY);//图像细化//cv::Mat dst = thinImage(src);zhangSkeleton(src);cv::Mat dst = src;//显示图像//dst = dst * 255;cv::namedWindow("src1", CV_WINDOW_AUTOSIZE);cv::namedWindow("dst1", CV_WINDOW_AUTOSIZE);cv::imshow("src1", src);cv::imshow("dst1", dst);cv::imwrite("dst.jpg", dst);cv::waitKey(0);
}void zhangSkeleton(Mat &srcimage)
{int kernel[9];int nl = srcimage.rows;int nc = srcimage.cols;vector<Point> delete_list;int A, B;while (true){for (int j = 1; j < nl - 2; j++){uchar* data_pre = srcimage.ptr<uchar>(j - 1);uchar* data = srcimage.ptr<uchar>(j);uchar* data_next = srcimage.ptr<uchar>(j + 1);for (int i = 1; i < (nc - 2); i++){if (data[i] == 255){kernel[0] = 1;if (data_pre[i] == 255) kernel[1] = 1;else  kernel[1] = 0;if (data_pre[i + 1] == 255) kernel[2] = 1;else  kernel[2] = 0;if (data[i + 1] == 255) kernel[3] = 1;else  kernel[3] = 0;if (data_next[i + 1] == 255) kernel[4] = 1;else  kernel[4] = 0;if (data_next[i] == 255) kernel[5] = 1;else  kernel[5] = 0;if (data_next[i - 1] == 255) kernel[6] = 1;else  kernel[6] = 0;if (data[i - 1] == 255) kernel[7] = 1;else  kernel[7] = 0;if (data_pre[i - 1] == 255) kernel[8] = 1;else  kernel[8] = 0;B=0;for (int k = 1; k < 9; k++){B = B + kernel[k];}if ((B >= 2) && (B <= 6)){A = 0;if (!kernel[1] && kernel[2]) A++;if (!kernel[2] && kernel[3]) A++;if (!kernel[3] && kernel[4]) A++;if (!kernel[4] && kernel[5]) A++;if (!kernel[5] && kernel[6]) A++;if (!kernel[6] && kernel[7]) A++;if (!kernel[7] && kernel[8]) A++;if (!kernel[8] && kernel[1]) A++;//if (A == 1){if ((kernel[1] * kernel[3] * kernel[5] == 0) && (kernel[3] * kernel[5] * kernel[7] == 0)){delete_list.push_back(Point(i, j));}}}               }}}int size = delete_list.size();if (size == 0){break;}for (int n = 0; n < size; n++){Point tem;tem = delete_list[n];uchar* data = srcimage.ptr<uchar>(tem.y);data[tem.x] = 0;}delete_list.clear();for (int j = 1; j < nl - 2; j++){uchar* data_pre = srcimage.ptr<uchar>(j - 1);uchar* data = srcimage.ptr<uchar>(j);uchar* data_next = srcimage.ptr<uchar>(j + 1);for (int i = 1; i < (nc - 2); i++){if (data[i] == 255){kernel[0] = 1;if (data_pre[i] == 255) kernel[1] = 1;else  kernel[1] = 0;if (data_pre[i + 1] == 255) kernel[2] = 1;else  kernel[2] = 0;if (data[i + 1] == 255) kernel[3] = 1;else  kernel[3] = 0;if (data_next[i + 1] == 255) kernel[4] = 1;else  kernel[4] = 0;if (data_next[i] == 255) kernel[5] = 1;else  kernel[5] = 0;if (data_next[i - 1] == 255) kernel[6] = 1;else  kernel[6] = 0;if (data[i - 1] == 255) kernel[7] = 1;else  kernel[7] = 0;if (data_pre[i - 1] == 255) kernel[8] = 1;else  kernel[8] = 0;B = 0;for (int k = 1; k < 9; k++){B = B + kernel[k];}if ((B >= 2) && (B <= 6)){A = 0;if (!kernel[1] && kernel[2]) A++;if (!kernel[2] && kernel[3]) A++;if (!kernel[3] && kernel[4]) A++;if (!kernel[4] && kernel[5]) A++;if (!kernel[5] && kernel[6]) A++;if (!kernel[6] && kernel[7]) A++;if (!kernel[7] && kernel[8]) A++;if (!kernel[8] && kernel[1]) A++;//if (A == 1){if ((kernel[1] * kernel[3] * kernel[7] == 0) && (kernel[1] * kernel[5] * kernel[7] == 0)){delete_list.push_back(Point(i, j));}}}}}}size = delete_list.size();if (size == 0){break;}for (int n = 0; n < size; n++){Point tem;tem = delete_list[n];if (tem.y > srcimage.rows-1 || tem.x > srcimage.cols-1)continue;uchar* data = srcimage.ptr<uchar>(tem.y);data[tem.x] = 0;}delete_list.clear();}
}

运行效果

1原图像

2.运行效果

图像细化 A fast parallel algorithm for thinning digital patterns相关推荐

  1. 《A fast parallel algorithm for thinning digital patterns》论文算法python代码实现

    论文地址:A fast parallel algorithm for thinning digital patterns 代码: def thinImage(src, maxIterations=-1 ...

  2. 对《A Fast Parallel Algorithm for Thinning Digital Patterns》一文的理解(上)

    该论文发表于IPCV(Image Processing and Computer Vision)1984年,是细化算法的经典算法.我们亲切的称它为Zhang算法,后续的细化算法大多都是基于该算法的改进 ...

  3. 【opencv】图像细化

    [opencv]图像细化 [opencv]图像细化 2014-02-17 21:03 5404人阅读 评论(14) 收藏 举报  分类: opencv(1)  版权声明:本文为博主原创文章,未经博主允 ...

  4. python图像线条提取_python3 图像细化(提取骨架线)

    图像细化_八连通法 图像细化 图像细化的方法 八连通-查表法 zhang的快速并行细化算法 八连通-查表法的改进 附上参考链接:https://www.cnblogs.com/xianglan/arc ...

  5. zhang 快速并行细化方法_一种改进的Zhang并行图像细化算法的制作方法

    本发明涉及图像处理技术,具体涉及一种改进的Zhang并行图像细化算法. 背景技术: 图像细化是将图像的线条从多像素宽度减少到单位像素宽度,简称骨架化.细化效果的好坏直接影响后期图像处理的效果.对于二值 ...

  6. 【opencv-c++】cv::ximgproc::thinning图像细化算法

    [opencv-c++]cv::ximgproc::thinning图像细化算法 1.背景 2.示例 3.说明 1.背景 cv::ximgproc::thinning函数官方文档: https://d ...

  7. 【阅读笔记】低照度图像增强-《Fast efficient algorithm for enhancement of low lighting video》

    本文介绍的是一种比较实用的低照度图像增强效果很好的方法,Xuan Dong论文<Fast efficient algorithm for enhancement of low lighting ...

  8. DBN训练学习-A fast Learning algorithm for deep belief nets

    转载自:http://blog.sciencenet.cn/blog-110554-889016.html DBN的学习一般都是从Hinton的论文A Fast Learning Algorithm ...

  9. Opencv——基于索引表的图像细化

    图像细化针对的是二值图像  或者用阀值处理的二值图像.基于索引表的细化算法大致是遍历被二值化图像的边缘,根据边缘点的八连通域情况查找索引表以确定该边缘点是否能够被删除.根据一些细化规则我们可以建立索引 ...

最新文章

  1. 我的 FPGA 学习历程(13)—— 电子钟项目
  2. 在OpenCV中利用卷积进行图像滤波
  3. EasyX识别不到VC++6.0
  4. java程序设计实用教程答案_Java程序设计实用教程(课本习题解答).doc
  5. 定时任务 - 定时任务弊端与优化方案
  6. linux mysql删除用户权限_linuxmysql增加用户,删除用户,以及用户权限_MySQL
  7. 【12-05】面试题
  8. Redis 不安全临时文件漏洞
  9. hdu 2295(DLX+二分)
  10. 极光尔沃3D打印《英雄联盟》精美手板
  11. visio添加箭头图标
  12. linux安装微信 安装百度网盘 下载大文件
  13. 诗词格律[5] 词的基本知识
  14. NOIP2011提高组初赛不定项选择第5题
  15. web前端开发工程师的竞争力体现在哪里?
  16. 原神抽卡模拟器(java简易版)
  17. 2022年全国最新高级消防设施操作员模拟题库及答案
  18. cocoapod安装过程中的幺蛾子
  19. 织梦dedecms响应式精密机械模具公司网站模板(自适应手机移动端)
  20. 济南药品保健品交易会,2022全国中医药产业展会,山东药品展

热门文章

  1. Kotlin协程核心库分析-5 Job异常处理器注意点
  2. 使用SSM为学校医务室开发一套管理系统
  3. 关于word2016保存失败
  4. 【毕业设计】基于单片机的便携式空气质量检测仪 - 物联网 嵌入式
  5. UI设计到底是什么:什么叫ui设计?
  6. android 日历动画的实现
  7. 买域名+配置SSL站点
  8. 光学红外雨量IFR202型传感器智慧检测雨量场景等行业
  9. 牛客网练习赛36 Ribbit的数列 分块
  10. mumu显示连接服务器超时,网易mumu模拟器安装不了 网易mumu模拟器安装好久解决方法...