代码之家  ›  专栏  ›  技术社区  ›  SigTerm

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

  •  8
  • SigTerm  · 技术社区  · 16 年前

    假设我有一个图像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分钟才能完成。

    思想?

    6 回复  |  直到 16 年前
        1
  •  2
  •   ganz    16 年前

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

    (注意:我没有足够的声誉来发布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
    }
    

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

        2
  •  1
  •   Ross    16 年前

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

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

        3
  •  1
  •   SigTerm    16 年前

    回答我自己的问题。

    我能够删除alpha通道,所以我决定使用图像金字塔(参见 pyramid 和 gaussian pyramid

    长话短说:

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

    I-COLLIDE: an interactive and exact collision detection system for large-scale environments “使用3个排序的数组(每个数组按不同的维度排序)进行三维搜索),但它们显然对浮点和较低的维度数效果更好。

    使用运动检测的建议是没有用的,因为(似乎)运动检测假设像素代表运动对象(在我的例子中不是真的),至少一些优化依赖于此。

    最后我找到了一份名为 Fast Texture Synthesis using Tree-structured Vector Quantization

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

        4
  •  0
  •   fbrereto    16 年前

    A XOR B 对于A的后续重叠区域。值最接近0的结果区域将是A中与B最相似的部分。如果必须考虑alpha掩码,那么假设A的alpha掩码都是1,并将其包含在XOR中,即每像素32位,而不是24位。

        5
  •  0
  •   Michael Dorgan    16 年前

    我会考虑把你早期的电流差转移到你的内环,这样如果误差已经太大的话,它就可以在内环完成之前短路。你的交易条件是一些沉重的数学。此外,错误上的像素比例值可以是乘法而不是除法(在新机器上较小)

    是否有可能一次读取多个像素或并行处理?

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

    缓存要删除的结构->'s可以帮助您,但编译器通常会为您执行此操作。

    只是一些想法。还在看。。。

        6
  •  0
  •   Macke    16 年前
    推荐文章