图像/“最像像素”搜索优化?

时间:2010-05-26 16:38:00

标签: c++ image optimization search graphics

情况:

假设我有一个图像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千兆字节的RAM来完成任务。
  2. 我宁愿避免某种冗长的预处理阶段,需要30分钟才能完成。

想法?

6 个答案:

答案 0 :(得分:2)

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

(注意:我没有足够的声望发布2个链接,所以你必须在维基百科中查找运动估计。)

可以找到一些简单的块匹配算法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
}

在仅检查搜索点的子集时,提前终止也很有用,尽管加速不如完全搜索那么快。

答案 1 :(得分:1)

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

  

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

答案 2 :(得分:1)

回答我自己的问题。

简短回答: 我能够删除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)”,虽然我无法提供更多信息(没有检查过 - 当前纹理生成器在我的硬件上以可接受的速度工作,所以我可能不会看进一步优化)。

答案 3 :(得分:0)

一个潜在的加速可能是使用二元运算符。例如,您可以通过A XOR B前进到A的后续重叠区域。其值最接近0的结果区域将是最接近B的A部分。如果必须将alpha掩码带入帐户假设A的alpha遮罩是全1并且包含在每个像素的XOR-32位而不是24位。

答案 4 :(得分:0)

我会考虑将早期的cur差异移动到内部循环中,以便在内部循环完成之前它可以很好地短路,如果错误已经太大了。你的交易ifs是一些繁重的数学。此外,错误上的像素比例值可以是乘法而不是除数(在新机器上是次要的。)

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

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

缓存你的结构以摆脱 - &gt;可以提供帮助,但编译器通常会为您执行此操作。

开始时只是一些想法。还在看......

答案 5 :(得分:0)

PDiff是一个开源的感知图像差异工具,可能会为您提供一些有用的技巧。