1 骨架细化原理
思想:
公式: y = p0*2^0 + p1*2^1+ p2*2^2 + p3*2^3 + p4*2^4 + p5*2^5 + p6*2^6 +p7*2^7
前辈们对此作出了总结,得出每个点周围8领域的256种情况,放在一个char data[256]的数组中,不可以删除用0来表示,能被删除的用1来表示。然后对图像进行处理得到二值图像<0和1>,扫描图像,根据公式得出y,依次用data[y]判断该点是否可以被删除,直到所有的点都不可以被删除为止。
算法流程图
原文:https://blog.csdn.net/lu597203933/article/details/14397605
2 算法实现代码
// 骨架细化.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>
#include <iostream>
#include <vector>
#include "assert.h"
using namespace std;
using namespace cv;
Mat dst;
void Rosenfeld(Mat& src, Mat& dst)
{
if (src.type() != CV_8UC1)
{
printf("只能处理二值或灰度图像\n");
return;
}
//非原地操作时候,copy src到dst
if (dst.data != src.data)
{
src.copyTo(dst);
}
int i, j, n;
int width, height;
//之所以减1,是方便处理8邻域,防止越界
width = src.cols - 1;
height = src.rows - 1;
int step = src.step;
int p2, p3, p4, p5, p6, p7, p8, p9;
uchar* img;
bool ifEnd;
Mat tmpimg;
int dir[4] = { -step, step, 1, -1 };
while (1)
{
//分四个子迭代过程,分别对应北,南,东,西四个边界点的情况
ifEnd = false;
for (n = 0; n < 4; n++)
{
dst.copyTo(tmpimg);
img = tmpimg.data;
for (i = 1; i < height; i++)
{
img += step;
for (j = 1; j < width; j++)
{
uchar* p = img + j;
//如果p点是背景点或者且为方向边界点,依次为北南东西,继续循环
if (p[0] == 0 || p[dir[n]] > 0) continue;
p2 = p[-step] > 0 ? 1 : 0;
p3 = p[-step + 1] > 0 ? 1 : 0;
p4 = p[1] > 0 ? 1 : 0;
p5 = p[step + 1] > 0 ? 1 : 0;
p6 = p[step] > 0 ? 1 : 0;
p7 = p[step - 1] > 0 ? 1 : 0;
p8 = p[-1] > 0 ? 1 : 0;
p9 = p[-step - 1] > 0 ? 1 : 0;
//8 simple判定
int is8simple = 1;
if (p2 == 0 && p6 == 0)
{
if ((p9 == 1 || p8 == 1 || p7 == 1) && (p3 == 1 || p4 == 1 || p5 == 1))
is8simple = 0;
}
if (p4 == 0 && p8 == 0)
{
if ((p9 == 1 || p2 == 1 || p3 == 1) && (p5 == 1 || p6 == 1 || p7 == 1))
is8simple = 0;
}
if (p8 == 0 && p2 == 0)
{
if (p9 == 1 && (p3 == 1 || p4 == 1 || p5 == 1 || p6 == 1 || p7 == 1))
is8simple = 0;
}
if (p4 == 0 && p2 == 0)
{
if (p3 == 1 && (p5 == 1 || p6 == 1 || p7 == 1 || p8 == 1 || p9 == 1))
is8simple = 0;
}
if (p8 == 0 && p6 == 0)
{
if (p7 == 1 && (p3 == 9 || p2 == 1 || p3 == 1 || p4 == 1 || p5 == 1))
is8simple = 0;
}
if (p4 == 0 && p6 == 0)
{
if (p5 == 1 && (p7 == 1 || p8 == 1 || p9 == 1 || p2 == 1 || p3 == 1))
is8simple = 0;
}
int adjsum;
adjsum = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
//判断是否是邻接点或孤立点,0,1分别对于那个孤立点和端点
if (adjsum != 1 && adjsum != 0 && is8simple == 1)
{
dst.at<uchar>(i, j) = 0; //满足删除条件,设置当前像素为0
ifEnd = true;
}
}
}
}
if (!ifEnd) break;
}
}
int main(int argc, char*argv[])
{
Mat src = imread("06.jpg", 0);
if (src.empty())
{
cout << "读取文件失败!" << std::endl;
return -1;
}
resize(src, src, Size(src.cols / 2, src.rows / 2), (0, 0), (0, 0), 3);
//将原图像转换为二值图像
threshold(src, src, 20, 255, THRESH_BINARY_INV);//THRESH_BINARY_INV和 THRESH_BINARY不同对骨架细化有影响
imshow("二值图像", src);
//图像细化
Rosenfeld(src, dst);
//显示图像
dst = dst * 255;
imshow("未细化图片", src);
imshow("细化图片", dst);
waitKey(0);
return 0;
}
效果图展示
原始图1(采用THRESH_BINARY_INV)
效果图1
原始图2(采用THRESH_BINARY)
效果图2
注意 注意 注意!!!!!!!!!!!!
因为骨架细化算法输入的图片是二值化图片或者灰度图片 这里统一将图片定义为二值化图片后在输入:
如果前景是黑色背景是白色(即字符是黑色背景是白色) 使用THRESH_BINARY_INV(取反操作)二值化后(即字符是白色背景是黑色)
之后就可以将图片输入算法中进行骨架细化。
如果前景是白色背景是黑色(即字符是白色背景是黑色) 使用THRESH_BINARY二值化后(即字符是白色背景是黑色)
之后就可以将图片输入算法中进行骨架细化。
概括一下这个算法要求的设置是:待细化的图片前景是白色即用1表示;背景是黑色的用0表示
即最后输入的照片一定是前景(要细化的细节)是白色背景是黑色(图片底色)
也可以自己更改参数修改!!!!!!!!!!!!!