Извлеките один цветовой канал из 4-мерного cv::Mat

#c #opencv

Вопрос:

Я некоторое время работаю с OpenCV и экспериментирую с расширением DNN. Моя модель имеет форму ввода [1, 3, 224, 244] с глубиной пикселя uint8. Поэтому я помещаю в функцию свой m_inputImg, который имеет 3 канала и 8-битную глубину пикселей:

 cv::dnn::blobFromImage(m_inputImg, m_inputImgTensor, 1.0, cv::Size(), cv::Scalar(), false, false, CV_8U);
 

Теперь мне интересно получить представление о том, как мое входное изображение «лежит» внутри тензора cv::Mat. Теоретически я знаю, как выглядит тензор, но я не понимаю, как OpenCV это делает. Поэтому, чтобы понять это, я хочу извлечь один цветовой канал. Я пробовал это:

 cv::Mat blueImg  = cv::Mat(cp->getModelConfigs().model[0].input.height,
                            cp->getModelConfigs().model[0].input.width,
                            CV_8UC3,
                            blob.ptr<uint8_t>(0, 0);
 

Но то, что я получаю, — это что-то вроде этого (см. Рисунок). Я действительно в замешательстве по этому поводу, может ли кто-нибудь помочь или дать хороший совет?
Спасибо

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

Комментарии:

1. Как выглядит ваше исходное изображение? Если вы хотите отобразить только 1 канал, почему вы используете CV_8UC3 имхо, в настоящее время вы отображаете весь тензор как BGRBGRBGR… но ваши данные должны быть упорядочены RRR…GGG…BBB… можете ли вы попробовать просто использовать CV_8UC1? Имхо, тогда вы должны получить RRR… изображение. Но я не уверен, почему в вашем большом двоичном объекте 9 изображений.

2. При создании большого двоичного объекта не должно быть cv::Size(224,224) вместо cv::Size()?

3. Когда я попробую CV_8UC1, я получу только шум на своем изображении, поэтому мой указатель может отображаться в неопределенных областях.

4. Для параметра size OpenCV напишите следующее: «размер: пространственный размер выходного изображения». Поэтому, на мой взгляд, cv::Size(224, 224) неверен, потому что выходное «изображение» — это тензор. Но я понятия не имею, какой размер следует использовать, поэтому я разрешил это по умолчанию (возможно, OpenCV знает, чего я хочу… :D)

5. На вашем изображении первая 1/3 изображения является первым каналом большого двоичного объекта. Поскольку вы интерпретируете канал моноблока как изображение с 3 каналами, он будет считывать вторую строку изображения после 1/3 отображаемого изображения и третью строку на 2/3 отображаемого изображения. Поскольку вы читаете больше строк, чем требуется, второй канал начинается с 1/3 строк отображаемых изображений, а третий канал начинается с 2/3.

Ответ №1:

cv::Size() будет использовать исходный размер изображения. Вы неправильно интерпретируете данные. Вот 4 способа интерпретации cv::Size() загруженного большого двоичного объекта 512×512 ()-начните с изображения Ленны:

вход (512×512):

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

  1. большой двоичный объект-запускается как одноканальное изображение 512×512:

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

  1. большой двоичный объект-начните с изображения размером 512×512 BGR:

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

  1. большой двоичный объект-начните с изображения размером 224×224 BGR:

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

  1. большой двоичный объект-запуск в виде одного канала 224×224:

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

вот код:

 int main()
{
    cv::Mat img = cv::imread("C:/data/Lenna.png"); // 8UC3
    cv::imshow("img", img);


    cv::Mat blob;
    cv::dnn::blobFromImage(img, blob, 1.0, cv::Size(), cv::Scalar(), false, false, CV_8U);

    cv::Mat redImg = cv::Mat(img.rows,
        img.cols,
        CV_8UC1,
        blob.ptr<uint8_t>(0, 0));

    cv::imshow("blob 1", redImg);
    cv::imwrite("red1.jpg", redImg);

    cv::Mat redImg3C = cv::Mat(img.rows,
        img.cols,
        CV_8UC3,
        blob.ptr<uint8_t>(0, 0));

    cv::imshow("redImg3C", redImg3C);
    cv::imwrite("red3C.jpg", redImg3C);

    cv::Mat redImg224_3C = cv::Mat(224,
        224,
        CV_8UC3,
        blob.ptr<uint8_t>(0, 0));

    cv::imshow("redImg224_3C", redImg224_3C);
    cv::imwrite("redImg224_3C.jpg", redImg224_3C);

    cv::Mat redImg224_1C = cv::Mat(224,
        224,
        CV_8UC1,
        blob.ptr<uint8_t>(0, 0));

    cv::imshow("redImg224_1C", redImg224_1C);
    cv::imwrite("redImg224_1C.jpg", redImg224_1C);
    

    cv::waitKey(0);
}
 

Имхо, вы должны сделать в своем коде:

 cv::dnn::blobFromImage(m_inputImg, blob, 1.0, cv::Size(), cv::Scalar(), false, false, CV_8U);

cv::Mat blueImg  = cv::Mat(m_inputImg.rows,
                        m_inputImg.cols,
                        CV_8UC3,
                        blob.ptr<uint8_t>(0, 0);
 

или

 cv::dnn::blobFromImage(m_inputImg, blob, 1.0, cv::Size(cp->getModelConfigs().model[0].input.width , cp->getModelConfigs().model[0].input.height), cv::Scalar(), false, false, CV_8U);

cv::Mat blueImg  = cv::Mat(cp->getModelConfigs().model[0].input.height,
                        cp->getModelConfigs().model[0].input.width,
                        CV_8UC3,
                        blob.ptr<uint8_t>(0, 0);
 

Кроме того, вот версия установки размера изображения пространственного большого двоичного объекта на фиксированный размер (например, желаемый размер ввода DNN).:

     cv::Mat blob2;
    cv::dnn::blobFromImage(img, blob2, 1.0, cv::Size(224,224), cv::Scalar(), false, false, CV_8U);

    cv::Mat blueImg224_1C = cv::Mat(224,
        224,
        CV_8UC1,
        blob2.ptr<uint8_t>(0, 0));

    cv::imshow("blueImg224_1C", blueImg224_1C);
    cv::imwrite("blueImg224_1C.jpg", blueImg224_1C);
 

Придавая этому образу:
введите описание изображения здесь

Комментарии:

1. в образце допущена ошибка. Это не красный канал, так как я не менял каналы во время BLOB-изображения. Вместо этого это СИНИЙ канал, но все равно основное сообщение остается тем же самым.

2. Мне кажется, я понимаю это немного лучше. Но в своем редактировании вы предоставляете образец для извлечения «СИНЕГО» канала, но в результирующем изображении он легко выглядит как изображение в оттенках серого от исходного изображения.

3. рисование одного канала всегда серого цвета. Если вы хотите отобразить его в виде цветного изображения, вам придется объединить один канал с 2 пустыми каналами. Например: std::vector<cv::Mat> bgrMerger; bgrMerger.push_back(blueMat); bgrMerger.push_back(emptyMat); bgrMerger.push_back(emptyMat); тогда cv::merge(bgrMerger, bgrImage); и cv::imshow("image", bgrImage);

4. Да, вы правы. Но, на мой взгляд, будут отображаться только пиксели со значением >0 в синем канале. Кажется, все по-другому, когда я смотрю на ваш образец изображения.