Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenCV - Mean of Mat object in C++

How can we get the mean of an input RGB image(3 dimensional Mat object) so that we get a gray image? The cvtColor() function of OpenCV converts the image to gray based on a pre-existing formula. I want to get the mean of all three channels and store the resultant image in another matrix. The cv::mean() function in OpenCV returns the scalar mean of all input channels.

Were this Python, with img being a RGB image, img.mean(2) would get me what I want. Successive calls of the addWeighted() function and using gray= blue/3.0 + red/3.0 +green/3.0 [ After splitting channels] yielded different results when compared with Python.

Is there anything analogous to img.mean(2) in C++ or the OpenCV library of C++?

like image 500
Ashwin Avatar asked Feb 03 '17 06:02

Ashwin


People also ask

What does MAT mean in OpenCV?

The Mat class of OpenCV library is used to store the values of an image. It represents an n-dimensional array and is used to store image data of grayscale or color images, voxel volumes, vector fields, point clouds, tensors, histograms, etc.

What is mat in C?

Mat is basically a class with two data parts: the matrix header (containing information such as the size of the matrix, the method used for storing, at which address is the matrix stored, and so on) and a pointer to the matrix containing the pixel values (taking any dimensionality depending on the method chosen for ...

What does CV mean in OpenCV?

The cv::mean() function in OpenCV returns the scalar mean of all input channels. Were this Python, with img being a RGB image, img.

What is a mat image?

In the picture framing industry, a mat (or matte, or mount in British English) is a thin, flat piece of paper-based material included within a picture frame, which serves as additional decoration and to perform several other, more practical functions, such as separating the art from the glass.


1 Answers

Is there anything analogous to img.mean(2) in C++ or the OpenCV library of C++?

No, but you can easily compute that. There are a few ways of doing it:

  1. Loop over all the image, and set each value as the mean of the input pixel values. Take care of computing the intermediate values for the mean on a type with more capacity and accuracy than uchar (here I used double) or you may end up with wrong results. You can also optimize the code further, e.g. see this question and its answers. You just need to change the function computed in the inner loop to compute the mean.

  2. Use reduce. You can reshape you 3 channel matrix of size rows x cols to be a matrix of shape ((rows*cols) x 3), and then you can use the reduce operation with parameter REDUCE_AVG to compute the average row-wise. Then reshape the matrix to correct size. reshape operation is very fast, since you just modify the header without affecting the stored data.

  3. Use matrix operations to sum channels. You can use split to get the matrix for each channel, and sum them. Take care to not saturate your values while summing up! (Thanks to beaker for this one.)

You can see that the first approach is faster with small matrices, but as soon as the size increase, the second approach performs much better since you take advantage of OpenCV optimizations. The third approach works surprisingly well (thanks to matrix expressions).

Some numbers, time in ms. Time may vary on you computer depending on OpenCV optimizations enabled. Run in release!

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

Code:

#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;
}
like image 65
Miki Avatar answered Oct 25 '22 12:10

Miki