【问题标题】:Image/"most resembling pixel" search optimization?图片/“最相似像素”搜索优化?
【发布时间】:2011-02-24 06:59:20
【问题描述】:

情况:

假设我有一张图片 A,比如 512x512 像素,图片 B,5x5 或 7x7 像素。 两个图像都是 24 位 rgb,B 有 1 位 alpha 掩码(因此每个像素要么完全透明,要么完全实心)。

我需要在图像 A 中找到与图像 B 最相似的像素(及其“邻居”),或者可能与图像 B 最相似的像素。

相似度计算为“距离”,即不透明 B 的像素与 A 的像素之间的“距离”之和除以不透明 B 的像素数。下面是一个示例 SDL 代码进行说明:

struct Pixel{
    unsigned char b, g, r, a;
};

void fillPixel(int x, int y, SDL_Surface* dst, SDL_Surface* src, int dstMaskX, int dstMaskY){
    Pixel& dstPix = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*x + dst->pitch*y));

    int xMin = x + texWidth - searchWidth;
    int xMax = xMin + searchWidth*2;
    int yMin = y + texHeight - searchHeight;
    int yMax = yMin + searchHeight*2;


    int numFilled = 0;
    for (int curY = yMin; curY < yMax; curY++)
        for (int curX = xMin; curX < xMax; curX++){
            Pixel& cur = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*(curX & texMaskX) + dst->pitch*(curY & texMaskY)));
            if (cur.a != 0)
                numFilled++;
        }

    if (numFilled == 0){
        int srcX = rand() % src->w;
        int srcY = rand() % src->h;
        dstPix = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*srcX + src->pitch*srcY));
        dstPix.a = 0xFF;
        return;
    }

    int storedSrcX = rand() % src->w;
    int storedSrcY = rand() % src->h;
    float lastDifference = 3.40282347e+37F;

    //unsigned char mask = 

    for (int srcY = searchHeight; srcY < (src->h - searchHeight); srcY++)
        for (int srcX = searchWidth; srcX < (src->w - searchWidth); srcX++){
            float curDifference = 0;
            int numPixels = 0;
            for (int tmpY = -searchHeight; tmpY < searchHeight; tmpY++)
                for(int tmpX = -searchWidth; tmpX < searchWidth; tmpX++){
                    Pixel& tmpSrc = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*(srcX+tmpX) + src->pitch*(srcY+tmpY)));
                    Pixel& tmpDst = *((Pixel*)((char*)(dst->pixels) + sizeof(Pixel)*((x + dst->w + tmpX) & dstMaskX) + dst->pitch*((y + dst->h + tmpY) & dstMaskY)));
                    if (tmpDst.a){
                        numPixels++;
                        int dr = tmpSrc.r - tmpDst.r;
                        int dg = tmpSrc.g - tmpDst.g;
                        int db = tmpSrc.g - tmpDst.g;
                        curDifference += dr*dr + dg*dg + db*db;
                    }
                }
            if (numPixels)
                curDifference /= (float)numPixels;
            if (curDifference < lastDifference){
                lastDifference = curDifference;
                storedSrcX = srcX;
                storedSrcY = srcY;
            }
        }

    dstPix = *((Pixel*)((char*)(src->pixels) + sizeof(Pixel)*storedSrcX + src->pitch*storedSrcY));
    dstPix.a = 0xFF;
}

这个东西应该是用来生成纹理的。

现在,问题是:
最简单的方法是蛮力搜索(在示例程序中使用)。但它很慢——即使使用 GPU 加速和双核 cpu 也不会让它更快。由于 B 的掩码,我似乎无法使用修改后的二进制搜索。那么,我怎样才能更快地找到所需的像素呢?

附加信息:

  1. 任务允许使用 2 个内核、GPU 加速、CUDA 和 1.5..2 GB RAM。
  2. 我宁愿避免需要 30 分钟才能完成的某种冗长的预处理阶段。

想法?

【问题讨论】:

  • 现在,这是一个有趣的问题 :) 体面的代码,没有 sqrts 或繁重的数学。已经做得很好了。只是为了得到你需要的东西而与高阶 O 战斗。
  • 不是一个很好的解决方案,但是减少 A 和 B 的位深度会加快一点速度。

标签: c++ image optimization search graphics


【解决方案1】:

您需要查看运动估计,它在视频编码中用于在先前编码的图片中查找与要编码的块最相似的块的位置。

(注意:我没有足够的声誉来发布 2 个链接,因此您必须在 wikipedia 中查找运动估计)。

一些简单的块匹配算法可以找到here。这些仅通过分析搜索区域中的点子集来工作。

如果您想找到最小化匹配函数的特定点,则必须进行完整搜索。全搜索加速通常是通过提前终止来实现的——如果一个点已经不可能改进之前的最佳结果,则结束对它的评估。

min_sad = INT_MAX  // minimum sum of absolute difference
min_point = {0, 0}

foreach (point p : all_search_points )
{         
    sad = 0
    for( y = 0; y < block_height; ++y )
        for( x = 0; x < block_width && sad < min_sad; ++x ):
            sad += abs( block_b[y,x] - block_a[p.y+y, p.x+x] )
        if( sad < min_sad )
            min_sad = sad
            min_point = p
}

当只检查搜索点的一个子集时,提前终止也很有用,尽管速度提升不如完全搜索。

【讨论】:

  • 听起来很有希望,我会检查一下。终止搜索也是一个好主意。
【解决方案2】:

您可以尝试寻找近似解决方案:Patch Match

本文介绍了交互式图像编辑工具,该工具使用一种新的随机算法来快速找到图像块之间的近似最近邻匹配。先前的图形和视觉研究利用这种最近邻搜索来提供各种高级数字图像编辑工具。然而,为整个图像计算此类匹配字段的成本已经避开了以前提供交互性能的努力。我们的算法提供了比之前最先进技术 (20-100x) 的显着性能改进,使其能够在交互式编辑工具中使用。

【讨论】:

  • 有趣,但我找到了另一种解决方案。
【解决方案3】:

回答我自己的问题。

简短回答: 我能够删除 alpha 通道,所以我决定使用图像金字塔(参见网上的pyramidgaussian pyramid)。它带来了巨大的速度提升。

长答案:

我最初的目标是纹理合成。 Alpha 用于生成尚未填充的像素,B 代表已生成图像的一部分。 (即A是样本图案,B是生成图像)

经过一番研究,我发现要么没有快速的方法在 N 维空间中进行搜索(例如,3x3 像素区域基本上是 24 分量向量(不包括中心像素),而 7x7 wlil是 144 分量,搜索该区域将是 24 维或 144 维搜索)。好吧,有一些方法(例如,名为“I-COLLIDE: an interactive and exact collision detection system for large-scale environments”的论文使用 3 个排序数组(每个排序在不同维度上)进行 3 维搜索),但它们显然更适合浮点数和较少的维数。

使用运动检测的建议没有用,因为(似乎)运动检测假设像素代表移动对象(在我的情况下不是这样),并且至少有一些优化依赖于此。

最后我找到了名为“Fast Texture Synthesis using Tree-structured Vector Quantization”的论文(Li-Yi Wei,Marc Levoy,斯坦福大学),它使用的技术基于与我使用的算法相似的算法。要搜索的图像会被多次下采样(类似于 mip-map 生成),首先在最低级别执行搜索,然后在下一个级别执行搜索。它可能不是为其他应用程序进行实际图像搜索的最佳方式,但它非常适合我的目的。这篇论文比较旧,但对我有用。

同一篇论文提到了一些进一步加速搜索的技术。其中之一是“树结构矢量量化(TSVQ)”,虽然我不能提供更多信息(没有检查过 - 当前的纹理生成器在我的硬件上以可接受的速度工作,所以我可能不会看进一步优化)。

【讨论】:

    【解决方案4】:

    一个潜在的加速可能是使用二元运算符。例如,对于 A 的后续重叠区域,您可以通过 A XOR B。值最接近 0 的结果区域将是 A 中与 B 最相似的部分。如果您必须考虑 alpha 掩码,假设A 的 alpha 掩码全为 1,并将其包含在 XOR-每像素 32 位而不是 24 位中。

    【讨论】:

    • 嗯。我会考虑的,但坦率地说,我一直在寻找不同/更好的搜索算法。另外,我拥有的最快的实现是使用 OpenGL GLSL 着色器,它们在内部对浮点数进行操作(无异或)。
    • @SigTerm:你说两个图像都是 24 位 RGB - 我可能遗漏了一些东西,但是将这些值存储在浮点中如何提高性能?
    • 我说的是 GPU(显卡)加速实现,它使用着色器进行暴力搜索。在着色器代码中,您“看到”任何颜色作为浮点数 (rgba) 的 4 个分量矢量,并且您不能对它们执行 XOR 操作。您可能会获得更好的性能,因为 GPU 针对某些类型的计算和更快的纹理内存访问进行了优化。不幸的是,蛮力搜索并没有从中受益——因为随机纹理内存访问很快就会降低性能。
    • 另外,看来我错了,GPU/OpenGL 实现在我的机器上的运行速度比单线程 CPU 版本慢。这可能是因为它不是一流的游戏显卡,而且实现效率不高(使用着色器而不是 CUDA 不是一个好主意)。
    【解决方案5】:

    我会考虑将您在 cur Difference 上的早期移出到您的内部循环中,以便如果错误已经太大,它可以在内部循环完成之前短路。您的交易如果需要一些繁重的数学运算。此外,错误上的像素比例值可能是乘法而不是除法(在新机器上较小。)

    是否有机会一次读取多个像素或并行处理它们?

    对于线程,您可以在每个外部 for 循环迭代中启动线程(分解为您想要使用的线程数),以提高 CPU 的效率。同步最大错误将是唯一​​的问题 - 这可以通过将错误存储到外部表中并在最后进行比较以防止内存争用来完成。

    缓存你的结构以摆脱 -> 会有所帮助,但编译器通常会为你这样做。

    只是一些想法开始。还在找……

    【讨论】:

      【解决方案6】:

      PDiff 是一个开源的感知图像差异工具,可能对你有一些有用的技术。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-25
        • 2013-02-23
        • 1970-01-01
        • 1970-01-01
        • 2014-09-29
        • 1970-01-01
        • 2016-03-08
        • 2011-02-22
        相关资源
        最近更新 更多