Java 利用OpenCV从图像序列中提取中值图像

Java 利用OpenCV从图像序列中提取中值图像,java,android,opencv,image-processing,matrix,Java,Android,Opencv,Image Processing,Matrix,我有一个图像序列,我想计算中间图像(以删除移动元素)。直观地说,硬编码一个循环来遍历所有像素将有一个总的运行时间,以及相当大的内存使用量。有没有一种方法可以在OpenCV中轻松实现这一点?(我对平均值不感兴趣,我需要做一个中位数)。我是为Android写这篇文章的(使用OpenCV4Android),所以显然计算能力有限。如果平均值合适: Mat result(CV_64FC3, listImages[0].size()); for(int i = 0; i < listImages.si

我有一个图像序列,我想计算中间图像(以删除移动元素)。直观地说,硬编码一个循环来遍历所有像素将有一个总的运行时间,以及相当大的内存使用量。有没有一种方法可以在OpenCV中轻松实现这一点?(我对平均值不感兴趣,我需要做一个中位数)。我是为Android写这篇文章的(使用OpenCV4Android),所以显然计算能力有限。

如果平均值合适:

Mat result(CV_64FC3, listImages[0].size());
for(int i = 0; i < listImages.size(); i++) {
    result += listImages[i];
}
result /= listImages.size();
result.convertTo(result, CV_8UC3);
Mat结果(CV_64FC3,listImages[0].size());
对于(int i=0;i
编辑:

这个快速的伪中值应该可以实现以下目的:

// Following algorithm will retain the pixel which is the closest to the mean
// Computing Mean
Mat tmpResult = Mat.zeros(listImages[0].size(), CV_64FC3);
for(int i = 0; i < listImages.size(); i++) {
    tmpResult += listImages[i];
}
tmpResult /= listImages.size();
tmpResult.convertTo(tmpResult, CV_8UC3);
// We will now, for each pixel retain the closest to the mean
// Initializing result with the first image
Mat result(listImages[0].clone());
Mat diff1, diff2, minDiff;
for(int i = 1; i < listImages.size(); i++) {
    // Computing diff between mean/newImage and mean/lastResult
    absdiff(tmpResult, listImages[i], diff1);
    absdiff(tmpResult, result, diff2);
    // If a pixel of the new image is closer to the mean, it replaces the old one
    min(diff1, diff2, minDiff);
    // Get the old pixels that are still ok
    result = result & ~(minDiff - diff2);
    // Get the new pixels
    result += listImages[i] & (minDiff - diff2);
}
//以下算法将保留最接近平均值的像素
//计算平均值
Mat tmpResult=Mat.zeros(listImages[0].size(),CV_64FC3);
对于(int i=0;i
然而,经典的也应该是相当快的。它是O(nb^2*w*h),其中nb是图像的数量,w,h是图像的宽度和高度。上面是O(nb*w*h),在垫子上有更多的操作

经典计算的代码(几乎所有计算都将以本机语言进行):

mattmp;
//我们将对像素进行排序,其中第一个网格将获得最低的像素,最后一个网格将获得最高的像素
对于(int i=0;i
据我所知,没有OpenCV函数可以从图像序列中创建中值图像。几年前我需要同样的功能,我必须自己实现。它相对较慢,因为对于每个像素,您需要从多个图像中提取相关像素(低效的内存访问)并计算中值(这也是一个耗时的过程)

提高效率的可能途径有:

  • 不需要从所有图像中计算中值。图像的小子集就足够了
  • 您可以找到更有效的算法来查找一些小群体的中值。例如,我使用的算法可以有效地找到九个值组中的中值

google for effective medianth这是一个有趣的问题,但对于您所提到的硬件类型来说,听起来太具有挑战性了(不包括Java通常比C++慢)。你想在中位数中包含多少图像?图像的分辨率是多少?顺便问一下,您是否已经检查过在每一帧中计算中值滤波器是否会提供足够好的数据?没有反馈?我有一些想法,但是你应该提供更多的信息(见我上面的问题)OP说“我对平均化不感兴趣”。对于效率,在<代码>克隆< /代码>考虑之后,可能会有一个有效的向量化(SSE/NEON)实现。代码>cv::min(listImages[i]、listImages[j]、listImages[i])
cv::max(listImages[j]、tmp、listImages[j])
(当然,您不需要计算
cmp
)“最接近平均值”实际上可能与中值非常不同。我不认为这是OP想要的。第二种算法每像素使用O(nb^2)时间,因为这实际上是幼稚的排序。这似乎不是很有效,因为每个像素的中值可以在O(nb)内完成。你们可以用一种类似于快速排序的方式从数组中得到中值。但与每个分区后的快速排序不同,您对下半部分或上半部分感兴趣,但不是两者都感兴趣。您可以在std::nth_元素的文档中看到有关此方法的更多详细信息。
Mat tmp;
// We will sorting pixels where the first mat will get the lowest pixels and the last one, the highest
for(int i = 0; i < listImages.size(); i++) {
    for(int j = i + 1; j < listImages.size(); j++) {
        listImages[i].copyTo(tmp);
        min(listImages[i], listImages[j], listImages[i]);
        max(listImages[j], tmp, listImages[j]);
    }
}
// We get the median
Mat result = listImages[listImages.size() / 2];