优化了图像中加权像素的配对

时间:2015-08-08 06:56:24

标签: algorithm optimization

给定表示图像的矩阵X,每个像素X(i,j)具有向量Vij,其给出将像素X(i,j)与图像中的每个其他像素相关联的成本(我将实际使用它)对于指定的小邻域,但它不应该在算法上有所作为。)

配对像素X(i,j)和X(k,p)的成本是将像素X(i,j)与X(k,p)相关联的组合成本和关联像素X(k)的成本,p)像素X(i,j)。注意,成本函数是对称的,即关联X(i,j)与X(k,p)的成本等于将X(k,p)与X(i,j)相关联的成本。

目标是找到图像中每个像素的配对,这样每个像素都只是一个配对的一部分,总成本最小化/最大化。

2D空间局部性没有特殊意义,因此我们可以将X[i*w + j]视为一维成本数组,并构建二维成本矩阵。鉴于对称性,我们只能考虑对角线上方矩阵的一半。然后我们寻找一组具有最小总和的点(行,列对)。每行和每列只能使用一次。 (包括C[a][b]会删除行a中的所有条目,并且列b中的所有条目都不可能成为解决方案集的其他成员。)

我想到的一种方法是将其视为流动问题,但在这种情况下,我不知道如何构建图形。

2 个答案:

答案 0 :(得分:2)

如果我正确理解了您的目标,那么您的问题基本上是在加权图表中找到与最大/最小权重的完美匹配。

链接阅读:

答案 1 :(得分:0)

我一直在考虑视频编码方法(搜索如何编码此块的局部最小解决方案),然后继续。尽早使用提前淘汰潜在候选人。

最佳解决方案显而易见的唯一情况是每行中的最低成本位于不同的列中。

我提出了一种快速算法,可以找到解决方案,并且可以在此过程中完成一个改进步骤。我是从零开始做的,而不是直接基于任何东西,所以我可能错过了许多聪明的想法。我没有看到将算法扩展到查找全局最优解决方案的方法,因此如果本地最小化解决方案对您没有用,那么这可能不是您正在寻找的内容。但是,就操作次数和内存访问模式而言,它应该很快。

线性时间,一次通过无细化。对于大多数可能的成本分配,我希望仍然是优于随机的解决方案。

N = width*height;  // number of pixels in the image
int C[N][N] = costs;
int pair[N];
bitmap used[N] = { 0 };
// every iteration pairs two pixels, so we only need N/2 iterations.
for (int r=0 ; r<N/2 ; r++) {
    if (used[r])
        continue;
    // elements < r are already paired, so only check the potential candidates: from r+1 onwards
    int minpos = mincost_unused_position(C[r][r+1 .. N], used);
    pair[r] = minpos;
    pair[minpos] = r;
    used[r] = used[minpos] = 1;
}

这将获得 解决方案,但不是最佳解决方案。它仅在对角线的一侧搜索成本,因为所有元素< r已经配对。您还可以通过不费心更新used[r]pair[minpos]来节省时间,因为所有信息都来自其他更新。

如果您想更接近最佳解决方案,那么请考虑每一行中的所有成本,而不仅仅是未使用的成本。如果最低成本低于最低未使用成本,那么通过重新配对与配对的像素来检查是否可以释放所需的最低成本。

线性时间,精简步骤。细化工作量和细化期间触及的内存量受到严格限制(通过可调谐常数)。

// ALMOST CERTAINLY FULL OF BUGS, but I hope it gets the idea across
N = width*height;  // number of pixels in the image
int C[N][N] = costs;
int pair[N];
bitmap used[N] = { 0 };  // or just initialize pair[] to -1 in every element, and check it.

// cache the 8 least-cost pairings for every point.  8 is a tunable constant.
// One array of struct { int pos, cost; }; might be better for cache, esp. if this can't vectorize.
int mincost_positions[N][8];
int mincost_costs[N][8];    // sorted in order of increasing cost

for (int r=0 ; r<N/2 ; r++) {
    int minpos, min_unused_pos;
    int non_optimal = min_search(C[r][0 .. N], used, &minpos, &min_unused_pos, &mincost_positions[r][0], &mincost_costs[r][0]);
    used[r] = true;  // exclude r as a candidate for re-pairing
    if (non_optimal) {
        int wanted = mincost_positions[r][0];  // also = minpos;
        int wantcost_r = mincost_costs[r][0];

        // attempt to re-pair minpos to free it up for us
        int wpair = pair[wanted];
        assert (wpair < r || wanted < r);  // we can't have paired two elements we haven't yet reached
        // if (wpair > r) swap(wpair, wanted);  // mincost_positions and costs haven't been filled out yet for pixels beyond our current position.  (unless we do that in a separate parallelizable first-stage)

        int current_cost = C[wanted][wpair] + C[r][min_unused_pos];  // cost of current pairing for the element we want + cost of best available pairing for r
            // TODO:  more cache-friendly: search mincost_costs instead of touching C[]

        // mincost_positions is sorted in order of lowest cost
        // so the first available alternative pairing for wpair will give the lowest total cost.

        for (int alt=0 ; alt<8 ; alt++) {
            int trypos = mincost_positions[wpair][alt];  // find the next-lowest-cost unused pairing that will free up the pixel we want.

            if (!used[trypos]) {
                int altcost = mincost_costs[wpair][alt] + wantcost_r;

                if (altcost < current_cost) {
                    // re-pair wpair with its next-best option
                    pair[wpair] = trypos;
                    pair[trypos] = wpair;
                    used[trypos] = true;
                    min_unused_pos = wanted;
                } // else nothing

                break;  // early-out, because mincost_costs[wpair][alt] is just going to get worse.
            }
        }
    }
    pair[r] = min_unused_pos;
    pair[min_unused_pos] = r;

    used[min_unused_pos] = true;
    // used[r] = true;   // already done earlier, to cover the trypos == r case.
}

内存仍然大部分是连续读取的,在需要时可以充分利用大部分缓存行。 used[]应该适合缓存,因为它是位图。 pair[]并非常常被触及,并且可能适合缓存。有一个流浪C[wanted][wpair],可以通过mincost_positions[wanted][]wpair的线性搜索来消除mincost_costs[wanted][],然后使用找到它的索引作为mincost_positions[] and costs[]的索引

应该可以使用线程并行填写C[][]缓存,然后不必触摸C。如果C太大而无法容纳在缓存中,这可能是一个胜利。此外,它可能适用于您建议的仅限本地区的费用搜索。

否则,如果代码要在那些地方触摸mincost_positions,则可能只缓存最低成本位置而不是实际成本(以避免在我们需要时对r数组进行线性搜索按位置而非按等级划算的费用。

我们可以通过检查释放wanted的次佳配对是否有用来考虑更多可能性。 (在wpair / for each wanted in mincost_positions[r][0..n]循环周围添加另一个循环,检查wanted == min_unused_pos(直到O(n * m^d)))。记住哪个掉期候选人的净成本最低,并且做到了。

进行另一次改进通过是不可能的,因为没有替代方法可以重新配对任何东西。你可以下降mincost_positions的树,如果你发现了一个循环,那就表明有一组点可以同时修复。这进入m时间,其中d是mincost_positions缓存的大小,{{1}}是搜索此&#34;树的最大深度&#34; for loops。

完全不同的想法

我还想知道是否可以应用一些线性代数技术来转换2D成本矩阵? https://en.wikipedia.org/wiki/Gaussian_elimination会产生任何类型的有用矩阵,其中最佳对的集合更容易找到吗?