OpenCV - C++ 中 Mat 对象的平均值

Ash*_*win 6 c++ opencv image-processing

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

如果这个 Pythonimg是一个 RGB 图像,img.mean(2)会得到我想要的东西。与 Python 相比,连续调用该addWeighted()函数并使用gray= blue/3.0 + red/3.0 +green/3.0[After splitting channels] 会产生不同的结果。

有没有类似于img.mean(2)C++ 或 C++ 的 OpenCV 库的东西?

Mik*_*iki 7

有没有类似于 C++ 中的 img.mean(2) 或 C++ 的 OpenCV 库的东西?

不,但你可以很容易地计算出来。有几种方法可以做到:

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

  2. 使用reduce. 您可以reshape将 3 通道大小rows x cols的矩阵设为形状 ((rows*cols) x 3) 的矩阵,然后您可以使用reduce带参数的操作REDUCE_AVG来计算平均行。然后reshape矩阵来校正大小。reshape操作非常快,因为您只需修改标题而不会影响存储的数据。

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

您可以看到第一种方法对于小矩阵更快,但是一旦大小增加,第二种方法的性能就会好得多,因为您利用了 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
Run Code Online (Sandbox Code Playgroud)

代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)