I have a sequence of images for which I want to calculate the median image (as to remove moving elements). Intuitively, hard-coding a loop to go through all the pixels would have a gross running time, as well as fairly large memory usage. Is there a way to easily do this in OpenCV? (I'm not interested in averaging, I need to do a median). I'm writing this for Android (using OpenCV4Android) so obviously computing power is limited.
我有一系列图像,我想要计算中间图像(删除移动元素)。直观地,对循环进行硬编码以遍历所有像素将具有总运行时间以及相当大的存储器使用。有没有办法在OpenCV中轻松完成此操作? (我对平均不感兴趣,我需要做一个中位数)。我正在为Android编写这个(使用OpenCV4Android),因此显然计算能力有限。
2 个解决方案
#1
As far as I know, there no OpenCV function that creates median image from sequence of images. I needed the same feature couple of years ago and I had to implement this myself. It is relatively slow because for each pixel you need to extract relevant pixel from multiple images (inefficient memory access) and calculate median (also a time consuming process).
据我所知,没有OpenCV函数可以从图像序列中创建中值图像。几年前我需要相同的功能,我必须自己实现。它相对较慢,因为对于每个像素,您需要从多个图像中提取相关像素(低效的内存访问)并计算中值(也是一个耗时的过程)。
Possible ways to increase efficiency are:
提高效率的可能方法是:
- There no need to compute median from all images. Small subset of images will be enough.
- You can find more efficient algorithms for finding median of some small groups. For example I used algorithm that can efficiently find median in group of nine values.
无需计算所有图像的中位数。小部分图像就足够了。
您可以找到更有效的算法来查找某些小组的中位数。例如,我使用的算法可以有效地找到九个值组的中位数。
#2
If the mean is ok:
如果平均值没问题:
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);
EDIT:
This quick pseudo-median should make the trick:
这个快速的伪中位数应该成为诀窍:
// 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);
}
However the classic one should be also pretty fast. It is O(nb^2 * w * h) where nb is the number of images and w, h their width, height. The above is O(nb * w * h) with more operations on Mats.
然而,经典的应该也很快。它是O(nb ^ 2 * w * h),其中nb是图像的数量,w,h是它们的宽度,高度。以上是O(nb * w * h),对Mats有更多操作。
The code for the classical one (almost all computations will be made in native):
经典代码(几乎所有计算都将以原生代码进行):
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];
#1
As far as I know, there no OpenCV function that creates median image from sequence of images. I needed the same feature couple of years ago and I had to implement this myself. It is relatively slow because for each pixel you need to extract relevant pixel from multiple images (inefficient memory access) and calculate median (also a time consuming process).
据我所知,没有OpenCV函数可以从图像序列中创建中值图像。几年前我需要相同的功能,我必须自己实现。它相对较慢,因为对于每个像素,您需要从多个图像中提取相关像素(低效的内存访问)并计算中值(也是一个耗时的过程)。
Possible ways to increase efficiency are:
提高效率的可能方法是:
- There no need to compute median from all images. Small subset of images will be enough.
- You can find more efficient algorithms for finding median of some small groups. For example I used algorithm that can efficiently find median in group of nine values.
无需计算所有图像的中位数。小部分图像就足够了。
您可以找到更有效的算法来查找某些小组的中位数。例如,我使用的算法可以有效地找到九个值组的中位数。
#2
If the mean is ok:
如果平均值没问题:
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);
EDIT:
This quick pseudo-median should make the trick:
这个快速的伪中位数应该成为诀窍:
// 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);
}
However the classic one should be also pretty fast. It is O(nb^2 * w * h) where nb is the number of images and w, h their width, height. The above is O(nb * w * h) with more operations on Mats.
然而,经典的应该也很快。它是O(nb ^ 2 * w * h),其中nb是图像的数量,w,h是它们的宽度,高度。以上是O(nb * w * h),对Mats有更多操作。
The code for the classical one (almost all computations will be made in native):
经典代码(几乎所有计算都将以原生代码进行):
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];