azer89
azer89

Reputation: 1559

How to access single channel matrix in OpenCV

I want to ask if it is possible to access single channel matrix using img.at<T>(y, x) instead using img.ptr<T>(y, x)[0]

In the example below, I create a simple program to copy an image to another

cv::Mat inpImg = cv::imread("test.png");
cv::Mat img;  
inpImg.convertTo(img, CV_8UC1); // single channel image

cv::Mat outImg(img.rows, img.cols, CV_8UC1);

for(int a = 0; a < img.cols; a++)
    for(int b = 0; b < img.rows; b++)
        outImg.at<uchar>(b, a) = img.at<uchar>(b, a);   // This is wrong

cv::imshow("Test", outImg);

The shown result was wrong, but if I change it to

outImg.ptr<uchar>(b, a)[0] = img.ptr<uchar>(b, a)[0];

The result was correct.

I'm quite puzzled since using img.at<T>(y, x) should also be okay. I also tried with 32FC1 and float, the result is similar.

Upvotes: 0

Views: 2841

Answers (1)

Roger Rowland
Roger Rowland

Reputation: 26259

Although I know you already found it, the real reason - buried nicely in the documentation - is that cv::convertTo ignores the number of channels implied by the output type, so when you do this:

inpImg.convertTo(img, CV_8UC1); 

And, assuming your input image has three channels, you actually end up with a CV_8UC3 format, which explains why your initial workaround was successful - effectively, you only took a single channel by doing this:

outImg.ptr<uchar>(b, a)[0]     // takes the first channel of a CV_8UC3

This only worked by accident as the pixel should have been accessed like this:

outImg.ptr<Vec3b>(b, a)[0]     // takes the blue channel of a CV_8UC3

As the data is still packed uchar in both cases, the effective reinterpretation happened to work.

As you noted, you can either convert to greyscale on loading:

cv::imread("test.png", CV_LOAD_IMAGE_GRAYSCALE)

Or, you can convert explicitly:

cv::cvtColor(inpImg, inpImg, CV_BGR2GRAY);

Upvotes: 3

Related Questions