精华内容
下载资源
问答
  • 基于图像处理的骨架提取算法的应用研究-计算机科学.PDF
    2021-05-24 09:04:40

    基于图像处理的骨架提取算法的应用研究-计算机科学

    Vo 1. 43 No. 6A

    第 43 卷第6A 期 计算机科学

    2016 年 6 月 Computer Science June 2016

    基于图像处理的骨架提取算法的应用研究

    刁智华吴贝贝毋攘攘魏玉泉

    (郑州轻工业学院电气信息工程学院 郑州I 450002) (河南省信息化电器重点实验室 郑州I 450002)

    摘 要 骨架在图像分析与形状描述中是一个非常重要的变换,是图像几何形态中普遍存在而又难以描述的重要拓

    扑结构。骨架提取技术在图像处理学领域一直是人们关注的焦点。在广泛调研文献的基础上,对骨架提取方法进行

    了较为全面的综述,重点总结了其在农业领域中的应用,并对骨架提取技术在其他方面的应用也进行了概述。最后指

    出了目前存在的问题以及骨架提取算法的主要发展趋势,并对其进行了总结和展望,以期为该领域的发展和相关研究

    提供借鉴与参考。

    关键词 骨架才是取,精准施药,特征才是取

    中固法分类号 TP39 1. 4 文献标识码 A

    Application Research of Skeleton Extraction Algorithm ßased on Image Processing

    DIAO Zhi-hua WU Bei-bei WU Yuan-yuan WEI Yu-quan

    (College of Electric and Inforrnation Engineering ,Zhengzhou University of Light Industry ,Zhengzhou 450002 ,China)

    (Henan Key Lab of Inforrnation-based Electrical Appliances ,Zhengzhou 450002 ,China)

    Abstract The skeleton is an important transform in image analysis and shape description ,which is ubiquitous and an

    important topological structure that is difficult to describe in image geometry. Skeleton extraction technology in image

    processing has been the focus of scholars' attention. Based on extensive literature research ,the extraction methods of

    skeleton were reviewed ,its applications in agriculture were summarized , and the skeleton extraction technique used in

    other aspects were introduced. Finally ,we indicated the main problems and the trends of the skeleton extraction algo

    rithm ,and carried on the summary and outlook to provide reference to the development and related research of the field.

    Keywords Skeleton extraction ,Precision spraying ,Feature extraction

    更多相关内容
  • 骨架提取算法

    2017-11-27 17:05:05
    算法主要是在图像处理过程中对图像的骨架进行提取
  • 基于拉普拉斯收缩的三维模型骨架提取算法及其Matlab实现,冀伟杰,谢鑫,介绍了三维模型骨架的概念及其数学表达,介绍了几种常用三维模型骨架提取算法的原理及适用性。选择基于拉普拉斯收缩的三维模型骨
  • 针对复杂环境下运动人体难以检测及由人体运动自身的复杂性而引起的骨架提取难的问题,提出了一种复杂环境下视频序列中的运动人体骨架提取算法。算法首先利用区域背景建模获取复杂环境下的背景图像,利用最大色差分量...
  • 利用查表索引法提取并细化骨架,Mat类型实现、iplimage类型实现
  • 以冠状动脉血管为例
  • python骨架提取算法

    2021-04-15 10:17:53
    python骨架提取算法
  • 骨架提取算法应用

    千次阅读 2020-01-16 10:22:11
    根据个人理解,骨架提取(顾名思义)就是根据各个连通区域,将其抽离出与其轮廓近似的单像素表示形态。以便于直观观察、图像的后继处理。因此可以将其视为图像处理中的预处理,其操作是基于二值图。为了更好的提取...

    1、引言
    根据个人理解,骨架提取(顾名思义)就是根据各个连通区域,将其抽离出与其轮廓近似的单像素表示形态。以便于直观观察、图像的后继处理。因此可以将其视为图像处理中的预处理,其操作是基于二值图。为了更好的提取图像骨架,必要时需要对图像进行相应的预处理(比如去噪、滤波、形态学变换等)。
    我的应用主要集中在对一些包含线条型的零件检测,除此之外,骨架提取的应用特别广泛,比如文字的检测/识别、道路观测等。

    2、原理
    Zhang和Suen提出了一种带有模板匹配的并行细化算法,生成一个像素宽的骨架,不仅保持图像的连通性,并且产生更薄的结果,保持快速的处理速度。
    Zhang-Suen细化算法通常是一个迭代算法,整个迭代过程分为两步

    第一步:循环所有前景像素点,对符合如下条件的像素点标记为删除:
    1)2<=N(P1)<=6
    2)S(P1)=1
    3)P2P4P6=0
    4)P4P6P8=0
    其中N(P1)表示跟P1相邻的8个像素点中,为前景像素点的个数,S(P1)表示从P2-P9-P2像素中出现0-1的累积次数,其中0表示背景,1表示前景,完整的P1-P9的像素位置分布如表1:
    在这里插入图片描述
    第二步:
    1)2<=N(P1)<=6
    2)S(P1)=1
    3)P2P4P8=0
    4)P2P6P8=0
    循环以上两个步骤,直到两步中没有像素被标记为删除为止,输出的结果即为二值图像细化后的骨架。

    3、案例核心代码

    //Zhang-Sun细化算法
    void SkeletonExtraction()
    {
    	//原图像名称
    	string Img_name = "TEST.png";
    	//载入源图像
    	Mat Src = imread(Img_name);
    	Mat src = Src.clone();
    	//灰度化
    	cvtColor(src, src, COLOR_RGB2GRAY);
    	//Otsu求阈值
    	int thre = Otsu(src);
    	Mat Img;
    	//二值化
    	threshold(src, Img, thre, 255, THRESH_BINARY_INV);
    	namedWindow("原始二值化图像", 0);
    	imshow("原始二值化图像", Img);
    
    	Mat srcImg = Img.clone();
    	/****************骨架提取算法:Zhang-Suen法*****检测焊条数量************************/
    	vector<Point> deleteList;
    	int neighbourhood[9];
    	int row = srcImg.rows;
    	int col = srcImg.cols;
    	bool inOddIterations = true;
    	while (true) {
    		for (int j = 1; j < (row - 1); j++) {
    			uchar* data_last = srcImg.ptr<uchar>(j - 1);
    			uchar* data = srcImg.ptr<uchar>(j);
    			uchar* data_next = srcImg.ptr<uchar>(j + 1);
    			for (int i = 1; i < (col - 1); i++) {
    				if (data[i] == 255) {
    					int whitePointCount = 0;
    					neighbourhood[0] = 1;
    					//判断中心点8邻域的像素特征
    					if (data_last[i] == 255) neighbourhood[1] = 1;
    					else  neighbourhood[1] = 0;
    					if (data_last[i + 1] == 255) neighbourhood[2] = 1;
    					else  neighbourhood[2] = 0;
    					if (data[i + 1] == 255) neighbourhood[3] = 1;
    					else  neighbourhood[3] = 0;
    					if (data_next[i + 1] == 255) neighbourhood[4] = 1;
    					else  neighbourhood[4] = 0;
    					if (data_next[i] == 255) neighbourhood[5] = 1;
    					else  neighbourhood[5] = 0;
    					if (data_next[i - 1] == 255) neighbourhood[6] = 1;
    					else  neighbourhood[6] = 0;
    					if (data[i - 1] == 255) neighbourhood[7] = 1;
    					else  neighbourhood[7] = 0;
    					if (data_last[i - 1] == 255) neighbourhood[8] = 1;
    					else  neighbourhood[8] = 0;
    					for (int k = 1; k < 9; k++) {
    						//二进制值为1的个数
    						whitePointCount += neighbourhood[k];
    					}
    					//条件①2<=B(p1)<=6
    					if ((whitePointCount >= 2) && (whitePointCount <= 6)) {
    						int ap = 0;
    						//条件②A(p1)值
    						if ((neighbourhood[1] == 0) && (neighbourhood[2] == 1)) ap++;
    						if ((neighbourhood[2] == 0) && (neighbourhood[3] == 1)) ap++;
    						if ((neighbourhood[3] == 0) && (neighbourhood[4] == 1)) ap++;
    						if ((neighbourhood[4] == 0) && (neighbourhood[5] == 1)) ap++;
    						if ((neighbourhood[5] == 0) && (neighbourhood[6] == 1)) ap++;
    						if ((neighbourhood[6] == 0) && (neighbourhood[7] == 1)) ap++;
    						if ((neighbourhood[7] == 0) && (neighbourhood[8] == 1)) ap++;
    						if ((neighbourhood[8] == 0) && (neighbourhood[1] == 1)) ap++;
    						if (ap == 1) {
    							if (inOddIterations && (neighbourhood[3] * neighbourhood[5] * neighbourhood[7] == 0)
    								&& (neighbourhood[1] * neighbourhood[3] * neighbourhood[5] == 0)) {
    								deleteList.push_back(Point(i, j));
    							}
    							else if (!inOddIterations && (neighbourhood[1] * neighbourhood[5] * neighbourhood[7] == 0)
    								&& (neighbourhood[1] * neighbourhood[3] * neighbourhood[7] == 0)) {
    								deleteList.push_back(Point(i, j));
    							}
    						}
    					}
    				}
    			}
    		}
    		if (deleteList.size() == 0)
    			break;
    		for (size_t i = 0; i < deleteList.size(); i++) {
    			Point tem;
    			tem = deleteList[i];
    			uchar* data = srcImg.ptr<uchar>(tem.y);
    			data[tem.x] = 0;
    		}
    		deleteList.clear();
    
    		inOddIterations = !inOddIterations;
    	}
    	namedWindow("骨架提取", 0);
    	imshow("骨架提取", srcImg);
    }
    

    在这里插入图片描述

    展开全文
  • 基于区域分割的算法点云骨架提取
  • 计算二维图像的距离变换图,在距离图的基础上求出梯度图,综合0°、45°、90°和135°4个方向的梯度图,形成新的包含...最后,利用“细”中脊线构建最短路径算法所需的邻接矩阵,大大地减少骨架提取算法的运行时间.
  • 图像骨架提取算法

    2018-10-13 11:36:23
    基于visual studio 2010 的c++ 图像骨架提取算法,可以实现对图像骨架的提取
  • 骨架提取m文件,骨架提取算法,matlab源码.zip
  • 针对复杂环境下运动人体难以检测及由人体运动自身的复杂性而引起的骨架提取难的问题,提出了一种复杂环境下视频序列中的运动人体骨架提取算法。算法首先利用区域背景建模获取复杂环境下的背景图像,利用最大色差分量...
  • 三维点云模型骨架提取算法的研究与实现_曹嘉.caj
  • 视觉组学习内容:Zhang-Suen骨架提取算法欢迎使用Markdown编辑器任务要求如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也...

    前言

    这是视觉组dalao给大家布置的学期末学习任务。
    因为之前没有接触过linux,环境也没有配置好,对很多操作不够熟悉,做这个任务从头到尾大约花了两天合计15个小时的时间,中间还问过dalao两个小时左右的问题(此处给大佬比心),虽然和大佬说的三个小时相去甚远但是最后还是赶在ddl之前完成了任务。
    总的来说收获很多,了解了ZhangSuen骨架提取算法的原理和算法实现,复习了面向对象编程的C++,还对linux系统(Ubuntu)有了进一步的了解,写第一篇博文记录一下大致过程和心得。

    任务要求

    使用ZhangSuen骨架提取算法实现简化数字轮廓

    效果如图所示: Alt

    实现要求

    • 使用类简化代码量,增强可读性;类的声明放在.h头文件中,函数实现单独放在.cpp文件中
    • 不允许包含<opencv2/opencv.hpp>;把要用到的模块搞清楚,包含最少的头文件
    • Linux下使用命令行或makefile编译程序,不允许使用pkg-config,不能有-lopencv_*这种写法,需要用到哪个库文件就明确调用哪个库文件

    任务完成流程概要

    1.在Windows平台的VS 2017上写好函数风格代码

    这里参考了CSDN上和博客园的两篇文章,分别涉及原理和代码实现,链接附上

    2.使用类对原函数进行封装与调试

    • 复习C++面向对象编程
      类的成员函数定义、初始化函数等概念的复习
    • 类的声明/定义/使用
      类的封装风格参考过图像处理之Zhang Suen细化算法 博主:gloomyfish,虽然是java平台的opencv,但是可以启发灵感
      代码应该是在Ubuntu的环境中直接写的,但是由于没有配置,所以先在Windows写好以后再到Ubuntu里面使用,代码见后。

    3.配置Ubuntu环境

    • 换源
      换源是为了提高网络速度。这里弄了一阵子的时间,原因是因为很多教程都不是从零起步,而且有一定问题。很多网站上都是直接用vim(有可能没安装) 或者使用“打开”/"编辑"这种用语,对初入Ubuntu的小白很不友好。
      正确操作是先按Ctrl+Alt+T打开终端,然后输入sudo gedit /etc/apt/sources.list,在打开的文件窗口中将内容全部删除并复制上镜像源地址:参考Ubuntu 18.04换国内源 中科大源 阿里源 163源 清华源 博主:nudt_qxx,从中选一个复制粘贴(终端中的粘贴是Ctrl+Shift+V),然后把结尾CSDN附带的注释删除再保存就可以了,不知道需不需要重启。
    • 安装cmake(虽然没用上)
      用于cmake和makefile(?)
    • 安装opencv环境
      任务3要求在终端中编译(编译用g++,这个我是自己用sudo apt-get install g++安装的)或者用makefile。不配置opencv的环境无法编译项目。
      在终端中使用sudo apt-get install synapic,再在系统自带的新立得安装包管理器里搜索opencv就可以下载了。出乎意料的方便。
    • 安装编辑器及其他软件

    4.在Terminal中使用g++完成编译

    • 使用g++编译
      大佬实力操作。
      到这一步要准备编译。首先把我写好的两个cpp和一个.h文件放到同一个文件夹下,然后在右键菜单中点击“在终端打开”,输入g++ -o Zhenglin main.cpp class.cpp `pkg-config --libs --cflags opencv`要求3是不允许使用pkg-config和-lopencv_*,那写g++ -o z class.cpp main.cpp -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs也是正确的。注意,虽然头文件没有,但是终端还是要通过-l调用-lopencv_imgcodecs,否则会报错。
      如果程序正确,编译成功了就会生成一个叫做Zhenglin的文件,此时再输入./Zhenglin就可以执行这个文件了。可以看到输出结果。
      结果
      注意:使用文件时要注意有没有输入的图片,需要在源文件中修改路径。

    封装代码

    头文件

    #ifndef C_ZHANGSUEN_DIY_H
    #define C_ZHANGSUEN_DIY_H
    
    #include<opencv2/core.hpp>											///基本数据类型
    #include<opencv2/imgproc/imgproc.hpp>								///图像存取&线性变换
    #include<opencv2/highgui.hpp>										///输出接口&交互接口
    
    using namespace std;
    using namespace cv;
    
    class Skeleton {
    public:
    	void initialize();
    	void getDst(Mat src);
    	void DstToImg();
    	int getAP(int i, int j);
    	int getBP(int i, int j);
    	bool condition34IsOK_1(int i, int j);
    	bool condition34IsOK_2(int i, int j);
    	void erasePoint(int i, int j);
    	Mat Mat_return();
    	Skeleton();
    private:
    	Mat dst;
    	Mat tmpImg;
    	int height;
    	int width;
    };
    
    //主函数调用函数 函数调用类 并在函数体中判断条件 最后利用函数体返回
    
    
    
    Mat skeleton(Mat src);
    
    #endif //C_ZHANGSUEN_DIY_H
    
    

    主文件

    //*recoverd by Zhenglin/on 2018/12/29 
    #include "ZhangSuen_DIY.h"/
    
    Mat skeleton(Mat src);
    
    int main(){
        Mat src = imread("E:/Python/6.jpg");
    	Mat gray, binary, dst, skeleton_image;
    
    	//图像预处理
    	cvtColor(src, gray, CV_BGR2GRAY);
    	threshold(gray, binary, 200, 255, CV_THRESH_BINARY);			///二值化需要在灰度基础上进行
    	namedWindow("Binary", 0);										///只能对黑底白线进行提取,否则需要位反
    	imshow("Binary", binary);
    
    	//骨架提取
    	skeleton_image = skeleton(binary);
    
    	//输出图像
        namedWindow("Result", 0);
        imshow("Result", skeleton_image);
    	waitKey(0);
    }
    

    成员函数实现

    #include "ZhangSuen_DIY.h"
    
    //此算法的四个条件:
    //(a) 2 ≤ B(P1) ≤ 6
    //(b) A(P1) = 1
    //(c) 
    //       1. P2 x P4 x P6 = 0 in odd iterations
    //       2. P2 x P4 x P8 = 0 in even iterations
    //(d) 
    //       1. P4 x P6 x P8 = 0 in odd iterations
    //       2. P2 x P6 x P8 = 0 in even iterations	
    
    
    void Skeleton::initialize() {
    	height = dst.cols - 1;
    	width = dst.rows - 1;
    }
    
    Skeleton::Skeleton() {
    	height = dst.cols - 1;
    	width = dst.rows - 1;
    }
    
    void Skeleton::getDst(Mat src) {
    	src.copyTo(dst);
    }
    
    void Skeleton::DstToImg() {
    	dst.copyTo(tmpImg);
    }
    
    int Skeleton::getAP(int i, int j) {
    	int ap = 0;
    	uchar *pU, *pC, *pD;
    	pU = tmpImg.ptr<uchar>(i - 1);							///uchar类型的行指针
    	pC = tmpImg.ptr<uchar>(i);
    	pD = tmpImg.ptr<uchar>(i + 1);
    	if (pC[j] > 0) {
    		int ap = 0;
    		int p2 = (pU[j] > 0);
    		int p3 = (pU[j + 1] > 0);
    		if (p2 == 0 && p3 == 1)
    			ap++;
    
    		int p4 = (pC[j + 1] > 0);
    		if (p3 == 0 && p4 == 1)
    			ap++;
    
    		int p5 = (pD[j + 1] > 0);						///9 2 3 
    		if (p4 == 0 && p5 == 1)							///8 j 4
    			ap++;										///7 6 5
    
    		int p6 = (pD[j] > 0);
    		if (p5 == 0 && p6 == 1)
    			ap++;
    
    		int p7 = (pD[j - 1] > 0);
    		if (p6 == 0 && p7 == 1)
    			ap++;
    
    		int p8 = (pC[j - 1] > 0);
    		if (p7 == 0 && p8 == 1)
    			ap++;
    
    		int p9 = (pU[j - 1] > 0);
    		if (p8 == 0 && p9 == 1)
    			ap++;
    
    		if (p9 == 0 && p2 == 1)
    			ap++;
    
    		return ap;
    	}
    }
    
    int Skeleton::getBP(int i, int j) {
    	uchar *pU, *pC, *pD;
    	pU = tmpImg.ptr<uchar>(i - 1);
    	pC = tmpImg.ptr<uchar>(i);
    	pD = tmpImg.ptr<uchar>(i + 1);
    	if (pC[j] > 0) {
    		int p2 = (pU[j] > 0);
    		int p3 = (pU[j + 1] > 0);
    		int p4 = (pC[j + 1] > 0);
    		int p5 = (pD[j + 1] > 0);
    		int p6 = (pD[j] > 0);
    		int p7 = (pD[j - 1] > 0);
    		int p8 = (pC[j - 1] > 0);
    		int p9 = (pU[j - 1] > 0);
    
    		return p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
    	}
    }
    
    bool Skeleton::condition34IsOK_1(int i, int j) {
    	uchar *pU, *pC, *pD;
    	pU = tmpImg.ptr<uchar>(i - 1);
    	pC = tmpImg.ptr<uchar>(i);
    	pD = tmpImg.ptr<uchar>(i + 1);
    	if (pC[j] > 0) {
    		int p2 = (pU[j] > 0);
    		int p3 = (pU[j + 1] > 0);
    		int p4 = (pC[j + 1] > 0);
    		int p5 = (pD[j + 1] > 0);
    		int p6 = (pD[j] > 0);
    		int p7 = (pD[j - 1] > 0);
    		int p8 = (pC[j - 1] > 0);
    		int p9 = (pU[j - 1] > 0);
    
    		if ((p2*p4*p6 == 0) && (p4*p6*p8 == 0)) return 1;
    		else return 0;
    	}
    };
    
    bool Skeleton::condition34IsOK_2(int i, int j) {
    	uchar *pU, *pC, *pD;
    	pU = tmpImg.ptr<uchar>(i - 1);
    	pC = tmpImg.ptr<uchar>(i);
    	pD = tmpImg.ptr<uchar>(i + 1);
    	if (pC[j] > 0) {
    		int p2 = (pU[j] > 0);
    		int p3 = (pU[j + 1] > 0);
    		int p4 = (pC[j + 1] > 0);
    		int p5 = (pD[j + 1] > 0);
    		int p6 = (pD[j] > 0);
    		int p7 = (pD[j - 1] > 0);
    		int p8 = (pC[j - 1] > 0);
    		int p9 = (pU[j - 1] > 0);
    
    		if ((p2*p4*p8 == 0) && (p2*p6*p8 == 0)) return 1;
    		else return 0;
    	}
    };
    
    void Skeleton::erasePoint(int i, int j) {
    	dst.ptr<uchar>(i)[j] = 0;
    }
    
    Mat Skeleton::Mat_return() {
    	return dst;
    };
    
    Mat skeleton(Mat src) {
    
    	int i = 0, j = 0;
    	bool isFinished = false;
    
    	Skeleton skel;
    	skel.getDst(src);
    	skel.initialize();
    
    	while (true) {
    		skel.DstToImg();
    		isFinished = false;
    
    		//扫描过程一 开始
    		for (i = 1; i < src.cols - 1; i++) {
    			for (int j = 1; j < src.rows - 1; j++) {
    				if (skel.getBP(i, j) > 1 && skel.getBP(i, j) < 7) {
    					if (skel.getAP(i, j) == 1) {
    						if (skel.condition34IsOK_1(i, j)) {
    							skel.erasePoint(i, j);
    							isFinished = true;
    						}
    					}
    				}
    			}
    			
    		}
    		//扫描过程一 结束
    
    		skel.DstToImg();
    
    		//扫描过程二 开始
    		for (i = 1; i < src.cols - 1; i++) {
    			for (int j = 1; j < src.rows - 1; j++) {
    				if (skel.getBP(i, j) > 1 && skel.getBP(i, j) < 7) {
    					if (skel.getAP(i, j) == 1) {
    						if (skel.condition34IsOK_2(i, j)) {
    							skel.erasePoint(i, j);
    							isFinished = true;
    						}
    					}
    				}
    			}
    		}
    		//扫描过程二 结束
    
    		if (isFinished == false)///如果在扫描过程中没有删除点则提前退出
    			return skel.Mat_return();
    	}
    }
    
    

    经验+5

    参考文献:

    [1]两种图像骨架提取算法的研究(1)原理部分 博主:zhubaohua_bupt
    [2]【20160924】GOCVHelper 图像增强部分(3) 博主:jsxyhelu
    [3]Opencv3.2各个模块功能详细简介(包括与Opencv2.4的区别) 博主:朱铭德
    [4]图像处理之Zhang Suen细化算法 博主:gloomyfish
    [5]Ubuntu 18.04换国内源 中科大源 阿里源 163源 清华源 博主:nudt_qxx

    展开全文
  • 图像的骨架提取算法

    千次阅读 2018-08-07 18:16:53
    目前已经有许多细化算法,这些算法得到的骨架可能略有差异。本文实现了Khalid Sheed 的 K3M算法。该算法属于迭代腐蚀边界的一类算法,该类算法的思想是,假定从二值图像中物体的边界处同时开始燃烧,物体就会被逐步...

    图像的骨架似乎没有严格的数学定义,可认为是图像细化(Thinning)的产物(中轴可以看作一种骨架,其有严格的数学定义)。目前已经有许多细化算法,这些算法得到的骨架可能略有差异。本文实现了Khalid Sheed 的 K3M算法。该算法属于迭代腐蚀边界的一类算法,该类算法的思想是,假定从二值图像中物体的边界处同时开始燃烧,物体就会被逐步细化,但在燃烧过程中要保证满足一定条件的点被保留或者被“烧掉”,以确定燃烧结束后,剩下的最后一像素宽的图像为图像的骨架。这些条件的确定没有统一的标准,各个算法采取了不同的方案。一般来讲,为了满足计算的速度要求和算法的准确,迭代中算法会对图像边界上某点的3*3邻域内进行检查,判断是否满足要求。

    K3M算法在每次迭代中需要进行六次检查

    Phase 0. 标记出图像的边界,

    Phase 1. 如果该点的邻域中有3个点(非零,以下皆如此)相邻,删除该点

    Phase 2. 如果该点的邻域中有3或4个点相邻,删除该点。

    Phase 3. 如果该点的邻域中有3,4,5个点相邻,删除该点。

    Phase 4. 如果该点的邻域中有3,4,5,6个点相邻,删除该点。

    Phase 5. 如果该点的邻域中有3,4,5,6,7个点相邻,删除该点。

    Phase 6. 剩余的边界点取消标记,如果Phase 5中没有点被修改,停止迭代,否则返回Phase 0。

    具体的步骤可以阅读论文:http://matwbn.icm.edu.pl/ksiazki/amc/amc20/amc2029.pdf。论文中算法实现的一个小技巧是,对邻域中的8个点的值看作二进制,即二进制编码,这样不同的值就对应邻域中不同的状态。迭代中通过计算即可判断该点是否满足条件,是否可以删除。具体细节请移步阅读论文。

     

    算法的测试结果如下:

    参考:

    http://homepages.inf.ed.ac.uk/rbf/HIPR2/skeleton.htm

    http://home.agh.edu.pl/~saeed/arts/2001%20CAIP.pdf

    http://www.cs.sunysb.edu/~algorith/files/thinning.shtml

    http://matwbn.icm.edu.pl/ksiazki/amc/amc20/amc2029.pdf

    #include <opencv2/imgproc/imgproc.hpp>
    #include <opencv2/objdetect/objdetect.hpp>
    #include <opencv2/highgui/highgui.hpp>
    #include<vector>
    #include<iostream>
    #include<algorithm>
     
    using std::vector;
     
    vector<int> GetFlags(int a[],int length)
    {
    	vector<int> vec;
    	int neighbour[]={1,2,4,8,16,32,64,128,1,2,4,8,16,32,64};
    	for(int i=0;i<length;i++)
    	{
    		for(int j=0;j<8;j++)
    		{
    			int sum=0;
    			for(int k=j;k<=j+a[i];k++)
    				sum+=neighbour[k];
    			vec.push_back(sum);
    			std::cout<<sum<<" ";
    		}
    	}
    	std::cout<<std::endl;
    	return vec;
    }
    void skeleton(cv::Mat &Input) //Input-binary image
    {
    	int a0[]={1,2,3,4,5,6};
    	int a1[]={2};
    	int a2[]={2,3};
    	int a3[]={2,3,4};
    	int a4[]={2,3,4,5};
    	int a5[]={2,3,4,5,6};
    	vector<int> A0=GetFlags(a0,6);
     
    	vector<int> A1=GetFlags(a1,1);
     
    	vector<int> A2=GetFlags(a2,2);
    	vector<int> A3=GetFlags(a3,3);
    	vector<int> A4=GetFlags(a4,4);
    	vector<int> A5=GetFlags(a5,5);
    	vector<cv::Point2i> border;
    	bool modify=true;
    	int neighbour[3][3]={
    		{128,1,2},
    		{64,0,4},
    		{32,16,8}
    	};
    	int row=Input.rows;
    	int col=Input.cols;
    	while(modify)
    	{
    		modify=false;
    		// flag the border Pharse 0
    		for(int m=1;m<row-1;++m)
    		{
    			for(int n=1;n<col-1;++n)
    			{
    				int weight=0;
    				for(int j=-1;j<=1;++j)
    				{
    					for(int k=-1;k<=1;k++)
    					{
    						weight+=neighbour[j+1][k+1]*Input.at<uchar>(m+j,n+k);
    					}
    				}
    				if(std::find(A0.begin(),A0.end(),weight)!=A0.end())
    					border.push_back(cv::Point2i(m,n));
    			}
    		}
    		//Pharse 1
    		vector<cv::Point2i>::iterator first=border.begin();
    		while(first!=border.end())
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>((*first).x+j,(*first).y+k);
    				}
    			}
    			if(std::find(A1.begin(),A1.end(),weight)!=A1.end())
    			{
    				Input.at<uchar>((*first).x,(*first).y)=0;
    				first=border.erase(first);
    			}
    			else
    			++first;
    		}
    		//Pharse2
    		first=border.begin();
    		while(first!=border.end())
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>((*first).x+j,(*first).y+k);
    				}
    			}
    			if(std::find(A2.begin(),A2.end(),weight)!=A2.end())
    			{
    				Input.at<uchar>((*first).x,(*first).y)=0;
    				first=border.erase(first);
    			}
    			else
    			++first;
    		}
    		//Pharse3
    		first=border.begin();
    		while(first!=border.end())
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>((*first).x+j,(*first).y+k);
    				}
    			}
    			if(std::find(A3.begin(),A3.end(),weight)!=A3.end())
    			{
    				Input.at<uchar>((*first).x,(*first).y)=0;
    				first=border.erase(first);
    			}
    			else
    			++first;
    		}
    		//Pharse4
    		first=border.begin();
    		while(first!=border.end())
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>((*first).x+j,(*first).y+k);
    				}
    			}
    			if(std::find(A4.begin(),A4.end(),weight)!=A4.end())
    			{
    				Input.at<uchar>((*first).x,(*first).y)=0;
    				first=border.erase(first);
    			}
    			else
    			++first;
    		}
    		//Pharse5
    		first=border.begin();
    		while(first!=border.end())
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>((*first).x+j,(*first).y+k);
    				}
    			}
    			if(std::find(A5.begin(),A5.end(),weight)!=A5.end())
    			{
    				Input.at<uchar>((*first).x,(*first).y)=0;
    				first=border.erase(first);
    				modify=true;
    			}
    			else
    			++first;
    		}
    		//Pharse6
    		border.clear();
    	}
    	for(int m=1;m<row-1;++m)
    	{
    		for(int n=1;n<col-1;++n)
    		{
    			int weight=0;
    			for(int j=-1;j<=1;++j)
    			{
    				for(int k=-1;k<=1;k++)
    				{
    					weight+=neighbour[j+1][k+1]*Input.at<uchar>(m+j,n+k);
    				}
    			}
    			if(std::find(A0.begin(),A0.end(),weight)!=A0.end())
    					Input.at<uchar>(m,n)=0;;
    		}
    	}
     
    }
    int main()
    {
    	cv::Mat raw=cv::imread("test.bmp");
    	cv::Mat image(raw.rows,raw.cols,CV_8UC1);
    	cv::cvtColor(raw,image,CV_RGB2GRAY);
    	cv::Mat binaryImage(image.rows,image.cols,CV_8UC1);
    	cv::threshold(image,binaryImage,150,1,CV_THRESH_BINARY_INV);
     
    	cv::imshow("input",image);
    	skeleton(binaryImage);
    		for(int p=0;p<binaryImage.rows;p++)
    		{
    			for(int q=0;q<binaryImage.cols;q++)
    			{
    				if(binaryImage.at<uchar>(p,q)==1)
    					binaryImage.at<uchar>(p,q)=0;
    				else
    					binaryImage.at<uchar>(p,q)=255;
    			}
    		}
    	cv::imshow("output",binaryImage);
     
    	cv::waitKey(0);
    	return 0;
    }
    

     

    展开全文
  • matlab骨架提取源代码基于视频的活动识别 [ 1.简介 VAR模块用于使用Microsoft Kinect传感器v2.0识别室内活动。 输入的是骨架数据,它以3维形式表示为身体关节坐标。 模块返回的输出是活动标签。 在此版本中,VAR最多...
  • 两种图像骨架提取算法的研究(1)原理部分

    万次阅读 多人点赞 2017-08-07 16:32:59
    骨架提取
  • 图像骨架提取算法。细化算法

    千次阅读 2018-03-15 18:46:11
    Afast parallel algorithm for thinning digitalpatterns” byT.Y. Zhang and C.Y. Suen.以及Parallelthinning with two sub-iteration algorithms” byZicheng Guo and ... Suen的图像骨架提取算法可以得到比较平...
  • 基于MATLAB的骨架提取算法的研究实现.pdf
  • 用求向量内积的方法对骨架进行提取,通过距离变换得到连接图像各点与最近边沿点的向量,并利用内积计算求取两个相邻点向量的内积值;再根据内积值进行骨架种子点的选择,经两次骨架生长处理得到连通的骨架.实验证明...
  • 关注2-D骨架提取问题,将其算法分为骨架提取的对称轴分析方法、细化方法以及形状分解方法 3大类,分别给出了这3类方法的基本思想和发展脉络,希望通过这样的工作为模式识别、可视化和医学图像处理等相关领域的同行提供...
  • Zhang-Suen 图像骨架提取算法的原理和OpenCV实现

    万次阅读 热门讨论 2018-06-12 20:16:22
    记录一下图像骨架提取算法,转载至 两种图像骨架提取算法的研究(1)原理部分基于OpenCV的实现代码如下,代码参考 opencv骨架提取/图像细化void Utilities::thinImage(Mat &amp; srcImg) { vector&lt;Point&...
  • 基于卷积神经网络的多人骨架提取算法之计算机研究.docx
  • 1、骨架提取 骨架提取,也叫二值图像细化。这种算法能将一个连通区域细化成一个像素的宽度,用于特征提取和目标拓扑表示。 morphology子模块提供了两个函数用于骨架提取,分别是Skeletonize()函数和medial_axis...
  • 图像骨架提取,实际上就是提取目标在图像上的中心像素轮廓。...关于骨架提取,现存的算法有一千种以上(论文[1]中谈到,There are more than one thousand algorithms that have been published ...
  • 提出利用物体边缘点和最小覆盖集的骨架生成算法提取异形纤维的骨架信息.通过计算二值图像物体的局部中心点集,结合物体边缘点建立相关矩阵,获得最小覆盖集,然后利用爬山算法结合方向导数生成连续骨架.最后,对...
  • 基于 OpenCV 的 C++ 代码, Visual Studio 2017 工程,算法主要功能:提取图像中形状的骨架主干部分并细化

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,727
精华内容 3,090
关键字:

骨架提取算法