【问题标题】:OpenCV - Mean of Mat object in C++OpenCV - C++ 中 Mat 对象的平均值
【发布时间】:2017-02-03 06:35:31
【问题描述】:

我们如何获得输入 RGB 图像(3 维 Mat 对象)的均值,从而获得灰度图像? OpenCV 的cvtColor() 函数根据预先存在的公式将图像转换为灰色。我想获得所有三个通道的平均值并将结果图像存储在另一个矩阵中。 OpenCV 中的cv::mean() 函数返回所有输入通道的标量平均值。

如果是这个 Python,img 是 RGB 图像,img.mean(2) 会得到我想要的。与 Python 相比,连续调用 addWeighted() 函数和使用 gray= blue/3.0 + red/3.0 +green/3.0 [拆分通道后] 产生了不同的结果。

有什么类似于 C++ 中的img.mean(2) 或 C++ 的 OpenCV 库吗?

【问题讨论】:

标签: c++ opencv image-processing


【解决方案1】:

有什么类似于 C++ 中的 img.mean(2) 或 C++ 的 OpenCV 库吗?

不,但您可以轻松计算。有几种方法可以做到:

  1. 循环遍历所有图像,并将每个值设置为输入像素值的平均值。注意计算比uchar(这里我使用double)具有更多容量和准确性的类型的平均值的中间值,否则您可能会得到错误的结果。您还可以进一步优化代码,例如请参阅this question 及其答案。您只需更改内部循环中计算的函数即可计算平均值。

  2. 使用reduce。您可以将reshape 大小为rows x cols 的3 通道矩阵设为形状矩阵((rows*cols) x 3),然后您可以使用reduce 操作和参数REDUCE_AVG 来计算平均行-明智的。然后reshape 矩阵来修正大小。 reshape 操作非常快,因为你只是修改了头部而不影响存储的数据。

  3. 使用矩阵运算对通道求和。您可以使用split 获取每个通道的矩阵,并将它们相加。总结时注意不要使你的价值观饱和! (感谢 beaker 提供这个。)

您可以看到,第一种方法在使用小矩阵时速度更快,但是随着大小的增加,第二种方法的性能要好得多,因为您利用了 OpenCV 优化。 第三种方法的效果出奇的好(感谢矩阵表达式)。

一些数字,以毫秒为单位的时间。根据启用的 OpenCV 优化,时间可能因您的计算机而异。在发布中运行!

Size  : 10x10   100x100   1000x1000   10000x10000
Loop  : 0.0077  0.3625    34.82       3456.71
Reduce: 1.44    1.42      8.88        716.75
Split : 0.1158  0.0656    2.26304     246.476

代码:

#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{
    Mat3b img(1000, 1000);
    randu(img, Scalar(0, 0, 0), Scalar(10, 10, 10));


    {
        double tic = double(getTickCount());
        Mat1b mean_img(img.rows, img.cols, uchar(0));
        for (int r = 0; r < img.rows; ++r) {
            for (int c = 0; c < img.cols; ++c) {
                const Vec3b& v = img(r, c);
                mean_img(r, c) = static_cast<uchar>(round((double(v[0]) + double(v[1]) + double(v[2])) / 3.0));
            }
        }
        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
        cout << "Loop: " << toc << endl;
    }

    {
        double tic = double(getTickCount());

        Mat1b mean_img2 = img.reshape(1, img.rows*img.cols);
        reduce(mean_img2, mean_img2, 1, REDUCE_AVG);
        mean_img2 = mean_img2.reshape(1, img.rows);

        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
        cout << "Reduce: " << toc << endl;
    }

    {
        double tic = double(getTickCount());

        vector<Mat1b> planes;
        split(img, planes);
        Mat1b mean_img3;
        if (img.channels() == 3) {
            mean_img3 = (planes[0] + planes[1] + planes[2]) / 3.0;
        }

        double toc = (double(getTickCount()) - tic) * 1000.0 / getTickFrequency();
        cout << "Split: " << toc << endl;
    }


    getchar();
    return 0;
}

【讨论】:

  • 有趣的比较。您是否尝试拆分通道,将它们相加并除以 3?不过,您必须小心 Mat 类型足够大以容纳总和。
  • @beaker 不,但它应该慢得多,因为您需要分配“通道数”矩阵。我稍后会试试。感谢您的提示
  • @beaker 我已经这样做了,但结果不一样。
  • @beaker 效果出奇的好。感谢您指出这一点!
  • @Ashwin 检查更新的答案。事实上,烧杯建议效果很好
【解决方案2】:

均值()

计算数组元素的平均值。

C++: 标量均值(InputArray src, InputArray mask=noArray())

Python: cv2.mean(src[, mask]) → retval

C: CvScalar cvAvg(const CvArr* arr, const CvArr* mask=NULL )

Python: cv.Avg(arr, mask=None) → 标量

参数: src – 输入数组,应该有 1 到 4 个通道,以便结果可以存储在 Scalar_ 中。 mask – 可选操作掩码。

函数mean计算每个通道的数组元素的平均值M,并返回它:

当所有掩码元素为 0 时,函数返回 Scalar::all(0)。

也检查这个答案how to calculate and use cvMat mean value

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    • 2021-05-20
    相关资源
    最近更新 更多