# converting 3-channel BGR pic to grayscale BY HAND

Hello,

I'm having this problem - I have to write a function which converts bgr picture to grayscale one-channel picture. Only things I can use are cv:Mat variables: rows, cols, data and step. I've been trying to solve it this way:

void bgr2gray( const Mat& bgr, Mat& gray )
{
gray = Mat::zeros(bgr.rows, bgr.cols, CV_8UC1 );

for (int y = 0; y < bgr.rows; y++) {
for (int x = 0; x < bgr.cols; x++) {

unsigned char blue = bgr.data[3*bgr.cols*y + x]*0.114f; //0.114
unsigned char green = bgr.data[3*bgr.cols*y + x+sizeof(char)]*0.587f;
unsigned char red = bgr.data[3*bgr.cols*y + x+2*sizeof(char)]*0.299f;

gray.data[gray.cols*y+x] = red + green + blue;
}
}


}

But still this gives me wrong (not whole) picture with many white vertical lines. Do you know whre I'm mistaken in my thinking?

cz_asm7

edit retag close merge delete

Sort by ยป oldest newest most voted

Your indices are wrong, the correct way would be:

unsigned char blue = bgr.data[3*bgr.cols*y + 3*x]*0.114;
unsigned char green = bgr.data[3*bgr.cols*y + 3*x + 1]*0.587;
unsigned char red = bgr.data[3*bgr.cols*y + 3*x+2]*0.299;


However, due to the individual casts you'll lose some precision, so either use float instead of unsigned char for your three lines or write it as a one-liner:

gray.at<uchar>(y,x) = bgr.at<Vec3b>(y,x)[0] * 0.114 + bgr.at<Vec3b>(y,x)[1]*0.587 + bgr.at<Vec3b>(y,x)[2]*0.299


Also note that your way of accessing the data-array only works for continuous data, this is normally true, but you never know.

Btw.: If you use the templated-matrices (i.e. Mat3b and Mat1b) you can omit the '.at<uchar>' and '.at<Vec3b>'

more

Official site

GitHub

Wiki

Documentation