How to Set Given Channel of a Cv::Mat to a Given Value Efficiently Without Changing Other Channels

How to set given channel of a cv::Mat to a given value efficiently without changing other channels?

If your image is continuous in memory you can use following trick:

mat.reshape(1,mat.rows*mat.cols).col(3).setTo(Scalar(120));

If it is not continuous:

for(int i=0; i<mat.rows; i++)
mat.row(i).reshape(1,mat.cols).col(3).setTo(Scalar(120));

Edit (thanks to Antonio for the comment):

Note that this code may be the shortest and it is not allocating new memory but it is not efficient at all. It may be even slower than split/merge approach. OpenCV is really inefficient when it should perform operations on non-continuous matrices with 1 pixel in a row. If time performance is important you should use the solution proposed by @Antonio.

Just a minor improvement to his solution:

const int cols = img.cols;
const int step = img.channels();
const int rows = img.rows;
for (int y = 0; y < rows; y++) {
unsigned char* p_row = img.ptr(y) + SELECTED_CHANNEL_NUMBER; //gets pointer to the first byte to be changed in this row, SELECTED_CHANNEL_NUMBER is 3 for alpha
unsigned char* row_end = p_row + cols*step;
for(; p_row != row_end; p_row += step)
*p_row = value;
}
}

This saves increment operation for x and one less value in register. On system with limited resources it may give ~5% speedup. Otherwise time performance will be the same.

How to set all pixels of an OpenCV Mat to a specific value?

  • For grayscale image:

    cv::Mat m(100, 100, CV_8UC1); //gray 
    m = Scalar(5); //used only Scalar.val[0]

    or

    cv::Mat m(100, 100, CV_8UC1); //gray 
    m.setTo(Scalar(5)); //used only Scalar.val[0]

    or

    Mat mat = Mat(100, 100, CV_8UC1, cv::Scalar(5));
  • For colored image (e.g. 3 channels)

    cv::Mat m(100, 100, CV_8UC3); //3-channel 
    m = Scalar(5, 10, 15); //Scalar.val[0-2] used

    or

    cv::Mat m(100, 100, CV_8UC3); //3-channel 
    m.setTo(Scalar(5, 10, 15)); //Scalar.val[0-2] used

    or

    Mat mat = Mat(100, 100, CV_8UC3, cv::Scalar(5,10,15));

P.S.: Check out this thread if you further want to know how to set given channel of a cv::Mat to a given value efficiently without changing other channels.

Multiple single channel matrix converted to single multi channel matrix

You can use cv::merge to do what you want. One possible usage:

cv::Mat r,g,b,a;

//Fill r,g,b,a with data

cv::Mat result;
std::vector<cv::Mat> channels;

channels.push_back(r);
channels.push_back(g);
channels.push_back(b);
channels.push_back(a);

cv::merge(channels, result);

Multi-channel or multi-colum matrix in OpenCV

It really depends on what you want to do with this data.

Some OpenCV operations are easier to do on a single pixel plane but others might be multichannel oriented.

I would guess that single plane (C1) would be more suitable for most situations though you can easily switch representations using cv::reshape(). In fact, choose any one and use reshape() to generate another view of the same memory.

Divide every channel of image by weight image in opencv

I tested your method against:

  1. split - divide - merge, @panmari
  2. Linearize matrices, and apply element wise division @Miki
  3. Linearize matrices, and apply element wise division (with pointers) @Miki and multiplication instead of division @Micka
  4. Make weights a 3 channel matrix, and apply divide on whole matrices @Micka.

Results (in ms):

Size            Method1         Metdhod2        Method3         Method4
[2 x 2] 0.0359212 0.00183271 0.000733086 1.77333
[10 x 10] 0.0117294 0.00293234 0.00109963 0.0051316
[100 x 100] 0.422624 0.241918 0.0751413 0.319625
[1000 x 1000] 20.757 20.3673 7.28284 18.4389
[2000 x 2000] 83.6238 82.942 28.4353 74.2132

NOTES

  • My method works faster, but the speed-up is related to the size of the matrices.
  • Method 3 (with pointers) is the fastest
  • Method 2 and Method 3 modify the original matrix. If you need not to change original matrix, you need to do a deep copy (clone(), see commented lines in code below). With deep copy of the original matrix both methods are slower, but Method 3 is still the fastest.
  • Method 4 won't work with double matrices, since cvtColor doesn't accepts double).
  • Preallocating matrices for Method 4 is just a small improvement.
  • Tested with compiler msvc12 (Visual Studio 2013). With gcc 4.8 Method 4 seems to be faster.

Here the code I used. I'm testing on float matrices, but it's easy to port to other types.

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

int main()
{
vector<Size> sizes{Size(2,2), Size(10,10), Size(100,100), Size(1000,1000), Size(2000,2000)};

cout << "Size \t\tMethod1 \tMetdhod2 \tMethod3 \tMethod4" << endl;

for (int is = 0; is < sizes.size(); ++is)
{

Size sz = sizes[is];
Mat3f weighted_sum(sz);
randu(weighted_sum, 0, 200);

Mat1f weights(sz);
randu(weights, 0, 10);

Mat3f ws1 = weighted_sum.clone();
Mat3f ws2 = weighted_sum.clone();
Mat3f ws3 = weighted_sum.clone();
Mat3f ws4 = weighted_sum.clone();

// Method 1 @parmari
double tic1 = double(getTickCount());
Mat3f rec1;

vector<Mat> channels(3);
split(ws1, channels);
for (Mat chan : channels) {
divide(chan, weights, chan);
}
merge(channels, rec1);

double toc1 = (double(getTickCount() - tic1)) * 1000. / getTickFrequency();

// Method 2 @Miki
double tic2 = double(getTickCount());
Mat3f rec2 = ws2.reshape(3, 1);
//Mat3f rec2 = ws2.reshape(3, 1).clone(); // To not override original image
Mat1f ww2 = weights.reshape(1, 1);
for (int i = 0; i < rec2.cols; ++i)
{
double w = ww2(0, i);
Vec3f& v = rec2(0, i);
v[0] /= w;
v[1] /= w;
v[2] /= w;
}
rec2 = rec2.reshape(3, ws2.rows);

double toc2 = (double(getTickCount() - tic2)) * 1000. / getTickFrequency();

// Method 3 @Miki (+ @Micka)
double tic3 = double(getTickCount());
Mat3f rec3 = ws3.reshape(3, 1);
//Mat3f rec3 = ws3.reshape(3, 1).clone(); // To not override original image
Mat1f ww3 = weights.reshape(1, 1);

Vec3f* prec3 = rec3.ptr<Vec3f>(0);
float* pww = ww3.ptr<float>(0);

for (int i = 0; i < rec3.cols; ++i)
{
float scale = 1. / (*pww);
(*prec3)[0] *= scale;
(*prec3)[1] *= scale;
(*prec3)[2] *= scale;

++prec3; ++pww;
}
rec3 = rec3.reshape(3, ws3.rows);

double toc3 = (double(getTickCount() - tic3)) * 1000. / getTickFrequency();

// Method 4 @Micka
double tic4 = double(getTickCount());
Mat3f rec4;
Mat3f w3ch4;
cvtColor(weights, w3ch4, COLOR_GRAY2BGR);
divide(ws4, w3ch4, rec4);

double toc4 = (double(getTickCount() - tic4)) * 1000. / getTickFrequency();

cout << sz << " \t" << toc1 << " \t" << toc2 << " \t" << toc3 << " \t" << toc4 << endl;

}

getchar();

return 0;
}


Related Topics



Leave a reply



Submit