首页 > 基于C++的骨架提取的鼻祖算法

基于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   
#include   
#include   
#include   
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, &l                    

更多相关:

  • 论文阅读模块将分享点云处理,SLAM,三维视觉,高精地图相关的文章。公众号致力于理解三维视觉领域相关内容的干货分享,欢迎各位加入我,我们一起每天一篇文章阅读,开启分享之旅,有兴趣的可联系微信[email protected]。opencv中ArUco模块实践(1)ArUco的生成与检测ArUco与AprilTag简介ChAruco...

  • 在x y的梯度上再求一次导数后相加便是散度。注意不要使用convertScaleAbs int main(int argc, char*argv[]) {cv::Mat IMG = cv::imread("./img.jpg", 1);cv::Mat k = getStructuringElement(cv::MORPH_RECT,...

  • 第一行是提取有效区域后的鱼眼图;第二行就是展开图(第二张和第四张根据需要裁剪了一部分) #include #include "opencv2/highgui/highgui.hpp" #include "opencv2/opencv.hpp" #include

  • cv::Mat inputIMG = cv::imread("./input/src.jpg", 1);cv::Mat graySrc, grad_x, grad_y;cv::Mat abs_grad_x, abs_grad_y;cv::cvtColor(inputIMG, graySrc, cv::COLOR_BGR2GRAY);...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...