如何使用OpenCV在不将所有图像保留在内存中的情况下平均N个图像

时间:2017-01-27 23:36:33

标签: java opencv image-processing average

我创建了一个类,尝试从任意数量的图像创建一个平均图像(逐个传递)。

此过程将在其自己的线程中运行,而其他线程读入图像,进行处理,并将输出传递给此平均对象。

不幸的是,每个附加图像的平均图像变得更亮,更亮。 我怀疑我的平均功能有错误,但我一直无法找到它。

编辑:我错过了" curImg / curCount"等式的一部分。通过此校正,图像现在变暗。我的平均成绩并不高。

编辑2:我看到我被投了票。我有什么办法可以改善这个问题吗?

class AverageImage {
    private Vector<Mat> average = new Vector<>();
    private int count = 0;

    public void add(Mat img) {
        count++;
        Vector<Mat> splitImg = new Vector<>();
        Mat convertedImg = img.clone();
        convertedImg.convertTo(convertedImg, CvType.CV_32FC1);
        Core.split(img.clone(), splitImg);

        if (average.isEmpty()) {
            average = splitImg;
        } else {
            // prevAverage * (prevCount/curCount) + curImg/curCount
            for (int i = 0; i < average.size(); i++) {
                Core.multiply(average.get(i), new Scalar((count - 1) / ((double) count)), average.get(i));
                Mat temp = new Mat();
                Core.divide(count, splitImg.get(i), temp);
                Core.add(average.get(i), temp, average.get(i));
            }
        }
    }

    public Mat getAverage() {
        Mat convertedAverage = new Mat();
        Core.merge(average, convertedAverage);
        convertedAverage.convertTo(convertedAverage.clone(), CvType.CV_8UC3);
        return convertedAverage;
    }

}

2 个答案:

答案 0 :(得分:2)

次要评论:您的代码中根本没有使用[Firebird] Description = Firebird ODBC Driver Driver = /usr/lib/libOdbcFb.so Threading = 1 FileUsage = 1 CPTimeout = CPReuse = 。你可以删除它。

您对所谓cumulative mean的确定是正确的。但是,真正搞乱你的部分是convertedImg声明:

divide

By consulting the OpenCV documentation,当你调用第一个元素是标量的变体时,正在进行的操作是:

Core.divide(count, splitImg.get(i), temp);

dst(I) = saturate(scale / src(I)) 用于分成输出。因此,当您实际应该scale时,您正在做的是count / splitImg.get(i)。考虑到这一点,splitImg.get(i) / count不支持拍摄图像并除以系数。但是,解决方法是使用divideCore.multiply的倒数:

count

您需要做的就是将Core.multiply(splitImg.get(i), new Scalar(1.0 / count), temp); 语句更改为上面的语句,它应该可以解决。

Core.divide

为了验证这是正确的,这是我在LaTeX中为此写的数学。给定 // prevAverage * (prevCount/curCount) + curImg/curCount for (int i = 0; i < average.size(); i++) { Core.multiply(average.get(i), new Scalar((count - 1) / ((double) count)), average.get(i)); Mat temp = new Mat(); // Core.divide(count, splitImg.get(i), temp); Core.multiply(splitImg.get(i), new Scalar(1.0 / count), temp); Core.add(average.get(i), temp, average.get(i)); } 值的信号,我们称之为N,我们可以用等式的第一行计算均值。 x表示信号x_i的i th 值。如果我们在平均值中增加一个附加项,那么第二行及以后就会发生这种情况。如果您计算出数学,我们会验证您的代码中的等式是否正确......但您只需更正x语句:

第二个等式中最左边的术语是循环中第一行代码中的最正确的术语:

Core.divide

最后,为了计算这个等式的第二项,我们做了:

Core.multiply(average.get(i), new Scalar((count - 1) / ((double) count)), average.get(i));

答案 1 :(得分:1)

对于数值稳定性,最好只存储累积的和图像,并在需要平均值时除以当前的N.

请注意不要溢出。