图像的骨架似乎没有严格的数学定义,可认为是图像细化(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 0for(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 1vector<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;}//Pharse2first=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;}//Pharse3first=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;}//Pharse4first=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;}//Pharse5first=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;}//Pharse6border.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;elsebinaryImage.at<uchar>(p,q)=255;}}cv::imshow("output",binaryImage);cv::waitKey(0);return 0;
}

提取图像的骨架(Skeleton)算法相关推荐

  1. 使用差分金字塔提取图像边缘 python实现

    本文提供一种提取图像边缘的快速算法 更多提取图像边缘的方法: 最大-最小滤波器实现图像边缘提取 Prewitt滤波器和Sobel滤波器提取图像边缘 拉普拉斯滤波器提取图像边缘 差分金字塔提取图像边缘算 ...

  2. matlab 求其骨架,数字图像处理图像的骨架生成和提取(Matlab)三种方法

    [实例简介] 数字图像处理图像的骨架生成和提取(Matlab),有三种方法,推荐给大家! [实例截图] [核心代码] Programe ├── Programe1 │   ├── 00.JPG │   ...

  3. python图像纹理提取_提取图像的颜色、纹理特征(传统算法)

    Python-Image-feature-extraction Python实现提取图像的纹理.颜色特征,包含快速灰度共现矩阵(GLCM).LBP特征.颜色矩.颜色直方图.1044197988/Pyt ...

  4. 【图像识别算法】像素级提取图像关键特征、内容 --python代码

    像素级提取图像关键特征算法-rgb 关键词:python像素级处理图像,python提取图片关键特征. 基于knn的图像识别技术主要涉及到以下概念: 色彩成像原理 [图像原理]rgb数字图片概念 计算 ...

  5. 活动作品计算机视觉课程第七讲-带你简单快速学习2021年春晚背后刘德华与背景分离切换到另一场景视觉算法(中集):OpenCV4图像分割提取图像任意感兴趣区域

    计算机视觉课程第七讲-带你简单快速学习2021年春晚背后刘德华与背景分离切换到另一场景视觉算法(中集):OpenCV4图像分割提取图像任意感兴趣区域 本专栏将会带大家学习 <计算机视觉与图形学& ...

  6. 动作分析 姿态估计_单人或多人的人体姿态骨架估计算法概述

    原标题:单人或多人的人体姿态骨架估计算法概述 如何在大片中实现人物的特效,最终应用人体姿态估计.本博客介绍了使用深度学习技术及其应用的多人姿势估计方法. 人体骨骼骨架以图形格式表达人体运动.基本上,它 ...

  7. 深度学习-CNN提取图像特征

    一.卷积层 1.卷积操作 2.特征提取-"X" or "O"? 二.池化(Pooling) 三.Relu 层 四.全连接层(Fully connected la ...

  8. 去雾综述_图像去雾的算法历史与综述

    图像去雾的算法历史与综述 1. 前言 其实之前对图像去雾也没有什么深入的理解,只是了解,实现过一些传统的图像去雾方法而已.个人感觉,在CNN模型大流行的今天,已经有很多人忽略了传统算法的发展,以至于你 ...

  9. Python grabcut 提取图像前景

    grabcut是一个非常实用的提取图像前景的算法.该方法是一种基于图切割的图像分割方法,是基于graph cut算法的改进.grabcut是需要少量用户交互操作.简单的说,就是需要用户指定要识别的区域 ...

最新文章

  1. As3回调函数的使用方法
  2. bash--shell高级编程-变量的替换
  3. idea mac 快键键
  4. win7源码运行odoo8.0错误
  5. linux进程通信发送方式,Linux服务器编程——Linux系统编程之进程通信
  6. java-rpc框架
  7. 云图说|DAS表结构对比与同步,实现您多个数据库管理心愿
  8. HTTP 错误 403.14 - Forbidden Web 服务器被配置为不列出此目录的内容
  9. LTE学习:CCE(2)
  10. LightOJ 1135 - Count the Multiples of 3 线段树
  11. jdk8 mysql安装教程_Linux系统:centos7下安装Jdk8、Tomcat8.5、MySQL5.7环境
  12. java web jsp/servlet 考勤管理系统
  13. 实验室信息化管理系统LIMS手机端二维码应用
  14. 推箱子c语言代码有注释,C语言代码实现推箱子小游戏
  15. java中subject类_RxJava中常见的几种Subject
  16. Web前端和Web后端的区分
  17. ArcGIS,CC(Smart3D),ENVI....工作中常用软件的汉化包都在这,随你下载
  18. 社区发现算法——(Spectral Clustering)谱聚类算法
  19. html控制台随机数取整,获取随机数、浮点数取整方法
  20. 大白话 同步阻塞、同步非阻塞、异步非阻塞

热门文章

  1. 2022.12六级真题第3套(共6页)
  2. 文字图片滚动代码-无缝滚动,强!!!
  3. 如何看iPhone是哪个国家的
  4. Web前端面试指导(八):iframe有那些缺点
  5. 3DMark Sky Driver
  6. 基于FPGA的数字等精度频率计
  7. JToolBar工具条(Java)
  8. Libgdx 之Actions 动作类
  9. Mysql数据库部分简单整理
  10. 第三届全国大学生算法设计与编程挑战赛题解【金奖全国第九】