OpenCV 3 С++ Выбор мата с помощью указателя происходит случайным образом

Я новичок в OpenCV и сейчас использую версию 3.4.1 с реализацией C++. Я все еще изучаю, поэтому этот вопрос не относится к конкретному проекту, а скорее «попытка понять, как это работает». Пожалуйста, учтите, имея в виду ту же идею, что я знаю, что я каким-то образом «изобретаю волю» с помощью этого кода, но я написал этот пример, чтобы понять, «КАК ЭТО РАБОТАЕТ».

Идея такова:

  1. Чтение RGB-изображения
  2. Сделайте его бинарным
  3. Найти связанные области
  4. Раскрасьте каждую область по-своему

В качестве примера я использую RGB-изображение размером 5x5 пикселей, сохраненное в формате BMP. Изображение представляет собой белый прямоугольник с черными пикселями по всему контуру.

введите здесь описание изображения

До того момента, когда я получаю матрицу ConnectedComponents с именем Mat::Labels, все идет нормально. Если я распечатаю Матрицу, я увижу именно то, что ожидаю:

11111
10001
10001
10001
11111

Помните, что я инвертировал порог, поэтому правильно получить 1 на краях...

Затем я создаю мат с тем же размером Mat::Labels, но с 3 каналами, чтобы раскрасить его с помощью RGB. Это называется Mat::ColoredLabels. Следующий шаг — создать экземпляр указателя, который проходит через Mat::Labels, и для каждой позиции в Mat::Labels, где значение равно 1, заполнить соответствующую позицию Mat:.ColoredLabels цветом.

ЗДЕСЬ ВСЁ ПОЛУЧИЛОСЬ ОЧЕНЬ НЕПРАВИЛЬНО! Указатель не извлекает строку байтов строки Mat::Labels, как можно было бы ожидать, а следует за каким-то другим порядком.

Вопросы:

  1. Я делаю что-то неправильно или "очевидно", что выборка указателя происходит в каком-то "непредсказуемом" порядке?
  2. Как установить значения матрицы (Mat::ColoredLabels) на основе значений другой матрицы (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;
}

person l.raimondi    schedule 18.03.2018    source источник


Ответы (1)


Вы использовали функцию connectedComponents с параметром CV_16U. Это означает, что один элемент изображения будет состоять из 16 бит (отсюда «16»), и вы должны интерпретировать их как целое число без знака (отсюда «U»). А так как ptr возвращает указатель, вам нужно разыменовать его, чтобы получить значение.

Поэтому вы должны получить доступ к элементам изображения метки следующим образом:

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

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

Что касается вашего второго вопроса, это так просто, но, конечно, вы должны понимать, какие приведения типов приводят к потере точности или переполнению, а какие нет.

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
person Nejc    schedule 18.03.2018
comment
Ты абсолютно прав ! Спасибо. Попробовал, теперь все понятно. - person l.raimondi; 18.03.2018