Search code examples
c++opencvrgbpixelscalar

Weird pixel values from OpenCV Mat object


I'm trying to extract pixels from an OpenCV Mat object. When I extract them as Vec4b, it works nicely, but when I use a Scalar, which is the standard (afaik), then I get weird numbers. How to use Scalar and interpret the numbers correctly?

Code for extracting pixel (0, 0), once as Vec4b and once as Scalar (which is in fact a Scalar_<double>):

cv::Vec4b vec = stroke.at<cv::Vec4b>(0, 0);
printf("vec: %i, %i, %i\n", vec[0], vec[1], vec[2]);
cv::Scalar sca = stroke.at<cv::Scalar>(0, 0);
printf("sca: %5.1f, %5.1f, %5.1f\n", sca[0], sca[1], sca[2]);

Output:

vec: 223, 178, 77
sca: -91331626426359316704088074967484318223735050578187982644502863694523413598032925912253837118690612448172200441235555626964757579094862898778997953403371230682094365379726071857064466448183314663075981261984082516073752896735112503485663679145991425485597914964276617645535969580912538709979773360278601728.0, ...

I'm expecting some creamy yellow color, so I think that the Vec3b is correct.


Solution

  • For the .at<_Tp>() function to work correctly, you must specify the component data type of the image matrix correctly. If you lack to do so, the binary data of the pixel is misinterpreted and you get unexpected results.

    After you extracted the pixel value correctly, you can convert it to any other generic Scalar_<_Tp> type you want. Usually, you convert it to Scalar, which is in fact an alias for Scalar_<double>. The conversion is implicitly done by using the assignment operator (=).

    More details:

    Most image matrices come with type code CV_8UC4 (4 channels, red, green, blue, and alpha). E.g. when you load a color image from a JPG file, then the image matrix will be of this format. The 8U means "8 bits per channel, unsigned integer" and the C4 means "4 channels per matrix element". The corresponding C++ type of the elements is Scalar_<uchar>. You have to specity this to the generic .at<_Tp>() function to make it work:

    mat.at<Scalar_<uchar>>(y, x)
    

    After the correct extraction of a Scalar_<uchar>, you can automatically convert it to Scalar (which is in fact Scalar_<double>) simply by assignment:

    cv::Scalar scalar = mat.at<cv::Scalar_<uchar>>(0, 0);