图像细化---Zhang-Suen算法
Zhang-Suen算法是一种经典的细化算法,后续很多在其基础上进行改进, 论文是1984年在IPCV(Image Processing and Computer Vision)发表的。
论文pdf: A fast parallel algorithm for thinning digital patterns
一、基本原理
输入:二值图
输出:细化后图像
思路:删除掉非骨架上的非零像素点
其思路比较简单,重点在于如何判断非零像素点是不是骨架点。
查找非骨架点
对于非零像素点,如下图所示考虑其8领域像素点,文中共分为两个步骤来判断该点是否需要删除。
- 第一步
判断该点是否满足如下条件,如果满足则删除,否则保留。
其中表示按至再到的顺序,从0变到1的次数,如下图所示,其; 表示其8领域非零像素个数,下图中 。
- 第二步
类似第一步,参考图3,第一步的条件会删除东南边界点和西北的角点,这里只需要把式(c)和(d)更改为以下条件
二、代码逻辑
**展开代码**
inline int GetObjectPixel(Image& image, int row, int col)
{
if (row >= 0 && row < image.GetWidth() && col >= 0 && col < image.GetHeight())
{
return (*image.GetPixel(row, col) > 128) ? 1 : 0;
}
return 0;
}
inline bool IsDelete(Image& image, int i, int j, bool bSecond = false)
{
int p2 = GetObjectPixel(image, i - 1, j );
int p3 = GetObjectPixel(image, i - 1, j + 1);
int p4 = GetObjectPixel(image, i, j + 1);
int p5 = GetObjectPixel(image, i + 1, j + 1);
int p6 = GetObjectPixel(image, i + 1, j);
int p7 = GetObjectPixel(image, i + 1, j - 1);
int p8 = GetObjectPixel(image, i, j - 1);
int p9 = GetObjectPixel(image, i - 1, j - 1);
// cacl AP
int ap = 0;
if (1 == p3 - p2) {
ap += 1;
}
if (1 == p4 - p3) {
ap += 1;
}
if (1 == p5 - p4) {
ap += 1;
}
if (1 == p6 - p5) {
ap += 1;
}
if (1 == p7 - p6) {
ap += 1;
}
if (1 == p8 - p7) {
ap += 1;
}
if (1 == p9 - p8) {
ap += 1;
}
if (1 == p2 - p9) {
ap += 1;
}
if (ap != 1) {
return false;
}
// cacl PB
int pb = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
if (pb < 2 || pb > 6) {
return false;
}
if (bSecond) {
if (0 == p2 * p4 * p8 && 0 == p2 * p6 * p8) {
return true;
}
}
else {
if (0 == p2 * p4 * p6 && 0 == p4 * p6 * p8) {
return true;
}
}
return false;
}
void Skeletonize(Image& image)
{
const int row = image.GetWidth(), column = image.GetHeight();
int c = 0;
std::vector<int> XYPosition;
while (true) {
c = 0;
// first
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < column; ++j)
{
if (*image.GetPixel(i, j) > 128) {
if (IsDelete(image, i, j))
{
XYPosition.push_back(i);
XYPosition.push_back(j);
c += 1;
}
}
}
}
if (c > 0) {
for (int idx = 0; idx < c; ++idx){
*image.GetPixel(XYPosition[idx * 2], XYPosition[idx * 2+ 1]) = 0;
}
XYPosition.clear();
c = 0;
}
else {
break;
}
// second
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < column; ++j)
{
if (*image.GetPixel(i, j) > 128) {
if (IsDelete(image, i, j, true))
{
XYPosition.push_back(i);
XYPosition.push_back(j);
c += 1;
}
}
}
}
if (c > 0) {
for (int idx = 0; idx < c; ++idx) {
*image.GetPixel(XYPosition[idx * 2], XYPosition[idx * 2 + 1]) = 0;
}
XYPosition.clear();
c = 0;
}
else {
break;
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!