Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
488 views
in Technique[技术] by (71.8m points)

c++ - OpenCV concatenate 3 channels to 32-bit value?

I am looking for the fastest way of converting 3 channels RGB frame to 1 channel picture in openCV. But I need to concatenate all three colors (R, G, B) of the pixel into one 32-bit value.

Every pixel should consist for an example:

pixel[0:31]= 01001001 11110000 11111111 00000000

The first 8 bits are RED color from the frame (at the same position in frame), second 8 bits are from green color and third 8 bits are from blue, last 8 bits are not important.

I tried this:

for (y = 100; y < 500; y++){
     for (x = 100; x < 500; x++) {
        int pixel =((edges.at<Vec3b>(y, x)[0])<<16)|
                    ((edges.at<Vec3b>(y, x)[1])<<8)|
                    (edges.at<Vec3b>(y, x)[2]);
}}

But this is to slow, because I need to go through every pixel in the frame.

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Tried some variants:

  • Method 0a : as in the question, @Sveva
  • Method 0b : as in the question, with for loops inverted, @Miki
  • Method 1 : querying value from mat only once using indices, @Miki
  • Method 2 : querying value from mat using pointers, @Miki
  • Method 3 : converting to BGRA and memcpy, @RyanP NOTE: works only if mat isContinuos().

Results (time in milliseconds)

isContinuos? 1
Method 0a: 113.704              0x1020300
Method 0b: 20.0975              0x1020300
Method 1: 20.1939               0x1020300
Method 2: 15.7434               0x1020300
Method 3: 22.5592               0xff030201

Considerations

Inverting the for loops has a major speedup, because OpenCV Mat are row-major ordered. The fastest method is Method 2, using pointers. Method 1 is slightly slower, but probably more readable. Method 3 is quite fast, but a single memcpy works only if the matrix isContinuos(). If not the case, you need to loop on each row, and memcpy each row, and this is going to be (only a little) slower.

NOTE

OpenCV stores BGR values (not RGB). Methods 0a, 0b, 1 and 2 output the values as: B G R 0. You just need to swap the index 0 and 2 to get R G B 0. For Method 3, you need to use cvtColor with parameter COLOR_BGR2RGBA.

Code

#include <opencv2opencv.hpp>
#include <vector>
#include <iostream>
using namespace std;
using namespace cv;

int main()
{
    // Test Image
    Mat3b img(2000, 3000, Vec3b(1, 2, 3));
    cout << "isContinuos? " << img.isContinuous() << endl;

    // Method 0a: method from question. Credits to @Sveto
    double tic0a = double(getTickCount());
    vector<int> v0a(img.rows * img.cols, 0);
    for (int c = 0; c < img.cols; ++c)
    {
        for (int r = 0; r < img.rows; ++r)
        {
            v0a[r*img.cols + c] = ((img.at<Vec3b>(r, c)[0]) << 24) |
                ((img.at<Vec3b>(r, c)[1]) << 16) |
                (img.at<Vec3b>(r, c)[2]) << 8;
        }
    }
    double toc0a = (double(getTickCount()) - tic0a) * 1000. / getTickFrequency();
    cout << "Method 0a: " << toc0a << "		";;
    cout << "0x" << hex << v0a[0] << endl;

    // Method 0b: method from question, loops inverted
    double tic0b = double(getTickCount());
    vector<int> v0b(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        for (int c = 0; c < img.cols; ++c)
        {
            v0b[r*img.cols + c] = ((img.at<Vec3b>(r, c)[0]) << 24) |
                ((img.at<Vec3b>(r, c)[1]) << 16) |
                (img.at<Vec3b>(r, c)[2]) << 8;
        }
    }
    double toc0b = (double(getTickCount()) - tic0b) * 1000. / getTickFrequency();
    cout << "Method 0b: " << toc0b << "		";
    cout << "0x" << hex << v0b[0] << endl;


    // Method 1: custom loop with indices
    double tic1 = double(getTickCount());
    vector<int> v1(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        for (int c = 0; c < img.cols; ++c)
        {
            const Vec3b& b = img(r, c);
            v1[r*img.cols + c] = (b[0] << 24) | (b[1] << 16) | (b[2] << 8);
        }
    }
    double toc1 = (double(getTickCount()) - tic1) * 1000. / getTickFrequency();
    cout << "Method 1: " << toc1 << "		";
    cout << "0x" << hex << v1[0] << endl;

    // Method 2: custom loop with pointers
    double tic2 = double(getTickCount());
    vector<int> v2(img.rows * img.cols, 0);
    for (int r = 0; r < img.rows; ++r)
    {
        uchar* p = img.ptr<uchar>(r);
        for (int c = 0; c < img.cols; ++c)
        {
            int val = ((*p) << 24); ++p;
            val |= ((*p) << 16); ++p;
            val |= ((*p) << 8); ++p;
            v2[r*img.cols + c] = val;
        }
    }
    double toc2 = (double(getTickCount()) - tic2) * 1000. / getTickFrequency();
    cout << "Method 2: " << toc2 << "		";
    cout << "0x" << hex << v2[0] << endl;


    // Method 3: using BGRA conversion. Credits @RyanP
    // NOTE: works only if img.isContinuos()
    double tic3 = double(getTickCount());
    Mat4b rgba3;
    cvtColor(img, rgba3, COLOR_BGR2BGRA);
    vector<int> v3(img.rows * img.cols, 0);
    memcpy(v3.data(), rgba3.data, img.rows * img.cols * sizeof(int));

    double toc3 = (double(getTickCount()) - tic3) * 1000. / getTickFrequency();
    cout << "Method 3: " << toc3 << "		";
    cout << "0x" << hex << v3[0] << endl;

    int dummy;
    cin >> dummy;

    return 0;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

56.8k users

...