【问题标题】:Computing Rand error efficiently有效计算兰德误差
【发布时间】:2016-09-21 12:53:15
【问题描述】:

我正在尝试将两个图像分割相互比较。 为此,我将每个图像转换为无符号短值向量,并计算rand error, 根据以下公式:

地点:

这是我的代码(rand 误差计算部分):

cv::Mat im1,im2;

//code for acquiring data for im1, im2
//code for copying im1(:)->v1, im2(:)->v2


int N = v1.size();
double a = 0;
double b = 0;
for (int i = 0; i <N; i++)
{
    for (int j = 0; j < i; j++)
    {
        unsigned short l1 = v1[i];
        unsigned short l2 = v1[j];
        unsigned short gt1 = v2[i];
        unsigned short gt2 = v2[j];
        if (l1 == l2 && gt1 == gt2)
        {
            a++;
        }
        else if (l1 != l2 && gt1 != gt2)
        {
            b++;
        }

    }
}

double NPairs = (double)(N*N)/2;
double res = (a + b) / NPairs;

我的问题是每个向量的长度是 307,200。 因此总迭代次数为 47,185,920,000。

它使得整个进程的运行时间很慢(几分钟就可以计算出来)。 你知道我该如何改进它吗?

谢谢!

【问题讨论】:

  • 你能创建一个 {value -> count} 的哈希映射吗?这会将运行时间从 O(n^2) 减少到 O(n)。
  • 这意味着你应该只需要外循环(我认为)——对于每个i,查找v1[i],结果就是你需要添加到a。跨度>
  • @OliverCharlesworth - 好主意!我会试试的。
  • 您不需要测试这些对。您只需要知道对象的交叉点中有多少元素。根据这些值,您可以轻松计算出ab
  • @VittorioPatriarca 我正在阅读 the algorithm,就像 drorco 一样。我认为pair的索引需要对应。

标签: c++ algorithm opencv image-processing optimization


【解决方案1】:

让's 假设我们在第一张图片中有 P 个不同的标签,在第二张图片中有 Q 个不同的标签。高效计算 Rand 误差(也称为 Rand index)的关键观察是不同标签的数量通常远小于像素的数量(ie P, Q ).

步骤 1

首先,预计算以下辅助数据:

  • 向量s1,大​​小为P,使得s1[p]是像素位置iv1的个数sub>[i] = p.

  • 向量s2,大​​小为Q,使得s2[q]是像素位置iv2 sub>[i] = q.

  • 矩阵M,大​​小为P x Q,使得M [p][q]是像素位置iv的个数1[i] = pv2[i ] = q.

向量s1s2和矩阵M可以通过在 O(n) 中传递一次输入图像 ie 来计算。

第二步

一旦 s1s2M 可用,a 和 b 可以高效计算:

这是因为我们感兴趣的每一对像素 (i, j) 具有两个像素在图像 1 中具有相同标签的属性,ie v1[i] = v1 [j] = p;和图 2 中的相同标签,ie v2[i] = v2[ j ] = q。由于 v1[i] = pv2[i] = q,像素 i 将有助于 bin M[ p][q],像素j也是如此。因此,对于标签 pq 的每个组合,我们需要考虑落入 M[ 的像素对的数量p][q] bin,然后将它们总结为所有可能的标签 pq

同样,对于 b 我们有:

在这里,我们计算有多少对与落入 bin M[p][q] 的像素之一形成.这样一个像素可以与每个落入 bin M[p'][q'] 的像素形成良好的配对,其中条件 p != p' 和 q != q'。对所有这样的 M[p'][q'] 求和相当于从整个矩阵的总和中减去 M (这个总和是 n) 行 p 上的总和 (ie s1[p]) 和列 q 上的总和 (ie s2[q])。但是,在减去行和列和之后,我们已经减去了两次 M[p][q],这就是为什么要添加它在上面表达式的末尾。最后,将其除以 2,因为每对都被计算了两次(对于作为 bin M[p][ q] 在上面的论点中)。


兰德误差(兰德指数)现在可以计算为:

这个方法的整体复杂度是O(n) + O(PQ),其中第一项通常占主导地位。

【讨论】:

  • 感谢您的回答!您的回答基于与我相同的原则(我们实际上有相同的伪代码),但是由于您在不需要 openCV 的情况下解决了一般情况下的问题,我认为您的解决方案更通用,这就是为什么我接受它。
【解决方案2】:

看完你的cmets,我尝试了以下方法:

  1. 计算每对可能值的交集。
  2. 使用交集结果计算误差。

我直接对 cv::Mat 对象执行了计算,没有将它们转换为 std::vector 对象。这让我能够使用 opencv 函数并实现更快的运行时间。

代码:

double a = 0, b = 0; //init variables

//unique function finds all the unique value of a matrix, with an optional input mask
std::set<unsigned short> m1Vals = unique(mat1); 
for (unsigned short s1 : m1Vals)
{
    cv::Mat mask1 = (mat1 == s1);
    std::set<unsigned short> m2ValsInRoi = unique(mat2, mat1==s1);
    for (unsigned short s2 : m2ValsInRoi)
    {
        cv::Mat mask2 = mat2 == s2;
        cv::Mat andMask = mask1 & mask2;
        double andVal = cv::countNonZero(andMask);
        a += (andVal*(andVal - 1)) / 2;
        b += ((double)cv::countNonZero(andMask) * (double)cv::countNonZero(~mask1 & ~mask2)) / 2;
    }
}

double NPairs = (double)(N*(N-1)) / 2;
double res = (a + b) / NPairs;

运行时间现在是合理的(只有几毫秒 vs 几分钟),输出与上面的代码相同。

示例: 我在以下矩阵上运行了代码:

//mat1 = [1 1 2]
cv::Mat mat1 = cv::Mat::ones(cv::Size(3, 1), CV_16U);
mat1.at<ushort>(cv::Point(2, 0)) = 2;

//mat2 = [1 2 1]
cv::Mat mat2 = cv::Mat::ones(cv::Size(3, 1), CV_16U);
mat2.at<ushort>(cv::Point(1, 0)) = 2;

在这种情况下,a = 0(没有匹配对对应),并且 b=1(i=2,j=3 的一个匹配对)。算法结果:

a = 0
b = 1
NPairs = 3
result = 0.3333333

感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 2018-06-23
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 1970-01-01
    • 1970-01-01
    • 2015-05-09
    • 2010-11-18
    • 1970-01-01
    相关资源
    最近更新 更多