像素链的运行长度编码

3

我已经在这个问题上纠结了很长时间。

我正在进行图像处理。到目前为止,我已经对我的图像进行了二值化处理,也就是说,从灰度图像中,每个像素值低于某个值的像素都被删除了。这样只给我留下了原始图像中一些区域,并且这些区域周围有很多“零像素”。

接下来,我把我的区域用“斑点”进行了行长度编码。运行是一种数据压缩方法。例如,假设您已经对一个正方形进行了二值化处理,那么您将只有几个描述整个图像的运行。 这些运行由x,y坐标和长度定义。

在重新创建图像时,对于每个运行,请转到x,y坐标并添加x轴上的像素,以便运行的长度。

现在,我必须取出这些运行并创建一个链表,以描述该区域的轮廓。我不知道如何做到这一点。

我有一堆x,y,length运行,我必须“导航”到边缘以形成。通常在成像中,这个过程是使用原始图像完成的,但我不能再使用原始图像了,因此我必须用运行计算它。

我知道这看起来像一堵大墙,但我不知道如何更好地提出这个问题。

任何关于相同实现的提示或指针都将非常棒。

编辑

感谢unwind,我将链接几张图片:

alt text
(来源:tudelft.nl)

在这个例子中,他们将图像B处理为轮廓C(我称之为链)。但是,我想从D,即行长度生成轮廓。


这是一个典型的情况,图片胜过千言万语。您的描述很好,但如果您提供一些图片,它将至少更容易理解两倍。特别是“链”概念很难理解,快速搜索也没有找到明显的结果。 - unwind
(更新):我自作主张将“chain”一词链接到我认为合适的页面。当然,您可以随意编辑或更好地在问题中插入图片。 - unwind
非常感谢。在这个例子中,他们是从原始图像(B)生成链,而我想从运行(D)生成它。但这正是我所说的。 - Eric
3个回答

0

乍一看,我没有看到一个实用的算法。 一个简单粗暴的解决方案是从长度编码的图像中扩展原始图像。 所以如果你的行看起来像这样:

A 3B 10A C 8D
C 4D 3A 6C 9A

其中字符返回实际像素值(例如A = 0,B = 127,...)。 您可以将像素值写入二维数组(或其他数据结构),它看起来像这样:

ABBBAAAAAAAAAACDDDDDDDD
CDDDDAAACCCCCCAAAAAAAAA

然后生成您的链,删除数组并保留链信息。 当然这很昂贵,所以也许您可以在对原始图片进行长度编码之前执行此操作。


0
这是一个非常简单实用的解决方案(C++):
#include <iostream>
#include <vector>

struct Run { int x, w; };
enum { EAST, NORTHEAST, NORTH, NORTHWEST, WEST, SOUTHWEST, SOUTH, SOUTHEAST };

int main() {

    const Run data[] = {
        { 7, 2 },
        { 5, 6 },
        { 5, 7 },
        { 5, 7 },
        { 6, 6 },
        { 0, 12 },
        { 0, 12 },
        { 0, 11 },
        { 1, 7 },
        { 3, 4 },
        { 3, 4 },
        { 3, 5 },
        { 3, 7 },
        { 3, 7 },
        { 5, 5 }
    };

    std::vector<Run> runs(data, data + 15);
    std::vector<int> before;
    std::vector<int> after;
    unsigned int i;
    int j;

    for (i = 0; i < runs.size() - 1; ++i) {

        if (runs[i].x < runs[i + 1].x) {

            for (j = 0; j < runs[i + 1].x - runs[i].x - 1; ++j)
                before.push_back(WEST);
            before.push_back(NORTHWEST);

        } else if (runs[i].x > runs[i + 1].x) {

            before.push_back(NORTHEAST);
            for (j = 0; j < runs[i].x - runs[i + 1].x - 1; ++j)
                before.push_back(EAST);

        } else {

            before.push_back(NORTH);

        }

        int first_right(runs[i].x + runs[i].w);
        int second_right(runs[i + 1].x + runs[i + 1].w);

        if (first_right < second_right) {

            after.push_back(SOUTHEAST);
            for (j = 0; j < second_right - first_right - 1; ++j)
                after.push_back(EAST);

        } else if (first_right > second_right) {

            for (j = 0; j < first_right - second_right - 1; ++j)
                after.push_back(WEST);
            after.push_back(SOUTHWEST);

        } else {

            after.push_back(SOUTH);

        }

    }

    for (j = 0; j < runs.back().w - 1; ++j)
        after.push_back(WEST);

    std::reverse(before.begin(), before.end());
    after.insert(after.end(), before.begin(), before.end());

    for (j = 0; j < int(after.size()); ++j) {
        switch (after[j]) {
        case EAST:      std::cout << "EAST\n";      break;
        case NORTHEAST: std::cout << "NORTHEAST\n"; break;
        case NORTH:     std::cout << "NORTH\n";     break;
        case NORTHWEST: std::cout << "NORTHWEST\n"; break;
        case WEST:      std::cout << "WEST\n";      break;
        case SOUTHWEST: std::cout << "SOUTHWEST\n"; break;
        case SOUTH:     std::cout << "SOUTH\n";     break;
        case SOUTHEAST: std::cout << "SOUTHEAST\n"; break;
        }
    }

}

这个程序通过迭代运行,测试左右端点跳转的方向,并将适当数量的链元素添加到两个向量中:一个按正向顺序排列,用于右侧,另一个按反向顺序排列,用于左侧。然后通过为最后一条扫描线添加适当数量的链接来连接这两个链,然后反转左侧链并将其附加到右侧链以生成最终链。

希望这是您要找的内容!


我不确定你的Run结构体包含什么,是x坐标和长度吗?我不明白如何将其与y坐标相关联。 - Eric
我也不确定这个会不会有效,因为如果你得到一个U形,你将不得不以某种方式返回数据数组。 - Eric
是的,它是一个x和长度。只要扫描线连续,y坐标并不重要;假设runs[0]保存具有最低y值的运行,以此类推。如果存在多个具有相同y值的运行,则无法工作;您可以通过将形状分成两组运行,找到链条,然后在之后将它们连接起来轻松解决这个问题。 - Jon Purdy
我不认为图像分割是一个平凡的任务,因为图像可以具有任意的复杂度。例如:打开画图软件并使用蜡笔工具多次绘制圆圈而不释放鼠标按钮。 - Eric
将它们分段很容易:只需找到纵向连续的运行组。要想确定是否连接两个链,请检查它们的运行是否垂直连续,并在适当的位置将一条链拼接到另一条链中,覆盖尽可能多的东部或西部链接。您还可以测试合并两个轮廓是否会产生一个带洞的形状,并将它们分成两个链,一个用于外部的“正面”链,一个用于孔的“负面”链。这真的是一种非常简单而多功能的方法。 - Jon Purdy

0

好吧,我失去了那份合同,但答案是使用Freeman链编码技术

它是运行长度编码与算法无关,这与我之前的想法不同。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接