Reputation: 13
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
Upvotes: 1
Views: 1410
Reputation: 41765
Tried some variants:
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 <opencv2\opencv.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 << "\t\t";;
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 << "\t\t";
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 << "\t\t";
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 << "\t\t";
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 << "\t\t";
cout << "0x" << hex << v3[0] << endl;
int dummy;
cin >> dummy;
return 0;
}
Upvotes: 0
Reputation: 96109
Use the split and merge channels functions.
The look complicated but are a lot easier than doing it a pixel at a time.
See stackoverflow.com/questions/14582082/merging-channels-in-opencv for sample
Upvotes: 0