0

I'm quite new to OpenCV and I'm now using version 3.4.1 with C++ implementation. I'm still exploring, so this question is not specific to a project, but is more of a "try to understand how it works". Please consider, with the same idea in mind, that I know that I'm somehow "reinventing the will" with this code, but I wrote this example to understand "HOW IT WORKS".

The idea is:

  1. Read an RGB image
  2. Make it binary
  3. Find Connected areas
  4. Colour each area differently

As an example I'm using a 5x5 pixel RGB image saved as BMP. The image is a white box with black pixels all around it's contour.

enter image description here

Up to the point where I get the ConnectedComponents matrix, named Mat::Labels, it all goes fine. If I print the Matrix I see exactly what I expect:

11111
10001
10001
10001
11111

Remember that I've inverted the threshold so it is correct to get 1 on the edges...

I then create a Mat with same size of Mat::Labels but 3 channels to colour it with RGB. This is named Mat::ColoredLabels. Next step is to instanciate a pointer that runs through the Mat::Labels and for each position in the Mat::Labels where the value is 1 fill the corresponding Mat:.ColoredLabels position with a color.

HERE THINGS GOT VERY WRONG ! The pointer does not fetch the Mat::Labels row byt row as I would expect but follows some other order.

Questions:

  1. Am I doing something wrong or it is "obvious" that the pointer fetching follows some "umpredictable" order ?
  2. How could I set values of a Matrix (Mat::ColoredLabels) based on the values of another matrix (Mat::Labels) ?

.

#include "opencv2\highgui.hpp"
#include "opencv2\opencv.hpp"
#include <stdio.h>

using namespace cv;

int main(int argc, char *argv[]) {

char* FilePath = "";
Mat Img;
Mat ImgGray;
Mat ImgBinary;
Mat Labels;

uchar *P;
uchar *CP;

// Image acquisition
if (argc < 2) {
    printf("Missing argument");
    return -1;
}

FilePath = argv[1];
Img = imread(FilePath, CV_LOAD_IMAGE_COLOR);
if (Img.empty()) {
    printf("Invalid image");
    return -1;
}

// Convert to Gray...I know I could convert it right away while loading....
cvtColor(Img, ImgGray, CV_RGB2GRAY);

// Threshold (inverted) to obtain black background and white blobs-> it works 
threshold(ImgGray, ImgBinary, 170, 255, CV_THRESH_BINARY_INV);

// Find Connected Components and put the 1/0 result in Mat::Labels 
int BlobsNum = connectedComponents(ImgBinary, Labels, 8, CV_16U);

// Just to see what comes out with a 5x5 image. I get:
//  11111
//  10001
//  10001
//  10001
//  11111

std::cout << Labels << "\n";

// Prepare to fetch the Mat(s) with pointer to be fast
int nRows = Labels.rows;
int nCols = Labels.cols * Labels.channels();
if (Labels.isContinuous()) {
    nCols *= nRows;
    nRows = 1;
}

// Prepare a Mat as big as LAbels but with 3 channels to color different blobs
Mat ColoredLabels(Img.rows, Img.cols, CV_8UC3, cv::Scalar(127, 127, 127));

int ColoredLabelsNumChannels = ColoredLabels.channels();
// Fetch Mat::Labels and Mat::ColoredLabes with the same for cycle...
for (int i = 0; i < nRows; i++) {

    // !!! HERE SOMETHING GOES WRONG !!!!
    P = Labels.ptr<uchar>(i);
    CP = ColoredLabels.ptr<uchar>(i);

    for (int j = 0; j < nCols; j++) {

        // The coloring operation does not work
        if (P[j] > 0) {
            CP[j*ColoredLabelsNumChannels] = 0;
            CP[j*ColoredLabelsNumChannels + 1] = 0;
            CP[j*ColoredLabelsNumChannels + 2] = 255;
        }
    }

}

std::cout << "\n" << ColoredLabels << "\n";
namedWindow("ColoredLabels", CV_WINDOW_NORMAL);
imshow("ColoredLabels", ColoredLabels);

waitKey(0);

printf("Execution completed succesfully");
return 0;
}
l.raimondi
  • 389
  • 2
  • 14

1 Answers1

0

You used connectedComponents function with CV_16U parameter. This means that the single element of the image will consist of 16 bits (hence '16') and you have to interpret them as unsigned integer (hence 'U'). And since ptr returns a pointer, you have to dereference it to get the value.

Therefore you should access label image elements in the following way:

unsigned short val = *Labels.ptr<unsigned short>(i) // or uint16_t

unsigned short val = Labels.at<unsigned short>.at(y, x);

Regarding your second question, it is as simple as that, but of course you have to understand which type casts result in loss of precisions or overflows and which ones not.

mat0.at<int>(y, x) = mat1.at<int>(y, x); // both matrices have CV_32S types

mat2.at<int>(y, x) = mat3.at<char>(y,x); // CV_32S and CV_8S

// Implicit cast occurs. Possible information loss: assigning 32-bit integer values to 8-bit ints
// mat4.at<unsigned char>(y, x) = mat5.at<unsigned int>(y, x); // CV_8U and CV_32U
Nejc
  • 927
  • 6
  • 15