Почему цветовое пространство изображения CV :: Mat неправильное (GBR вместо RGB или BGR)? - PullRequest
2 голосов
/ 05 августа 2020

У меня есть модуль в Python, который отправляет RGB на C ++ и там его расходуют. Однако изображение имеет неправильное цветовое пространство, независимо от того, что я делаю. То есть я попытался преобразовать его в RGB, предполагая, что он все еще находится в BGR (хотя в python он намеренно преобразован в RGB, выполнив: return img[:,:,::-1] и визуализируйте изображение с помощью matplotlib.), И наоборот, они выглядят одинаково !

Это показано ниже:

Исходное изображение:

enter image description here

This is the output of the image without any tampering

enter image description here

This is the output when the I use colorspace conversion from BGR2RGB:

cv::Mat img2;
cv::cvtColor(img, img2, cv::COLOR_BGR2RGB);
cv::imshow(title + " type:" + image_type, img2);

enter image description here

And this is what I get when I try to convert as a RGB2BGR:

cv::Mat img2;
cv::cvtColor(img, img2, cv::COLOR_RGB2BGR);
cv::imshow(title + " type:" + image_type, img2);

enter image description here

And as you can see the last two are identical. So I tried to manually change the channels and see if I can get this to work. The code snippet:

cv::Mat img2;
cv::cvtColor(img, img2, cv::COLOR_RGB2BGR);
//cv::cvtColor(img, img2, cv::COLOR_BGR2RGB);
for (int i = 0; i < img.rows; i++) {
    for (int j = 0; j < img.cols; j++) {
        img2.at<cv::Vec3b>(i, j)[2] = img.at<cv::Vec3b>(i, j)[0];
        img2.at<cv::Vec3b>(i, j)[0] = img.at<cv::Vec3b>(i, j)[1];
        img2.at<cv::Vec3b>(i, j)[1] = img.at<cv::Vec3b>(i, j)[2];
    }
}

cv::imshow(title + " type:" + image_type, img2);

Вот результат:

enter image description here

Clearly this is the RGB and everything looks good and it seems the image was in GBR format! I have no idea what is causing this.

Also I noticed, I must do a conversion, or else the following for loops will give me memory access violations! which is strange to me! Changing the BGR2RGB or RGB2BGR in the cvtColor doesn't have any effect and they act the same.

I would also like to know if there is a better way for getting this into the right colorspace, something that doesn't require a for loop like the one I wrote and uses hardware acceleration? Since this operation needs to be fast but using the current solution that I have is not good at all.

Edit

By the way this is the Python function I was calling in my C++:

def detect_and_align_all(pil_img):
    """gets the input image, and returns all the faces aligned as a list of rgb images 
    """
    bboxes, landmarks = detect_faces(pil_img)
    # convert to ndarray and then make rgb to bgr
    cv_img = np.array(pil_img)[:, :, ::-1].copy()
    img_list = []
    for landmark in landmarks:
        img = align_face(cv_img, landmark)
        img_list.append(img[:, :, ::-1])

    return img_list

и align_face в конечном итоге вызывает это:

return cv2.warpAffine(cv_img, tfm, (crop_size[0], crop_size[1]))

Обновление 1

Это фрагменты (взятые отсюда), которые я использую для отправки изображения с c ++ на python с помощью Pybind11 :

py::dtype determine_np_dtype(int depth)
{
    switch (depth) 
    {
    case CV_8U: return py::dtype::of<uint8_t>();
    case CV_8S: return py::dtype::of<int8_t>();
    case CV_16U: return py::dtype::of<uint16_t>();
    case CV_16S: return py::dtype::of<int16_t>();
    case CV_32S: return py::dtype::of<int32_t>();
    case CV_32F: return py::dtype::of<float>();
    case CV_64F: return py::dtype::of<double>();
    default:
        throw std::invalid_argument("Unsupported data type.");
    }
}

std::vector<std::size_t> determine_shape(cv::Mat& m)
{
    if (m.channels() == 1) {
        return {
            static_cast<size_t>(m.rows)
            , static_cast<size_t>(m.cols)
        };
    }

    return {
        static_cast<size_t>(m.rows)
        , static_cast<size_t>(m.cols)
        , static_cast<size_t>(m.channels())
    };
}

py::capsule make_capsule(cv::Mat& m)
{
    return py::capsule(new cv::Mat(m)
        , [](void* v) { delete reinterpret_cast<cv::Mat*>(v); }
    );
}

py::array mat_to_nparray(cv::Mat& m)
{
    if (!m.isContinuous()) {
        throw std::invalid_argument("Only continuous Mats supported.");
    }

    return py::array(determine_np_dtype(m.depth())
        , determine_shape(m)
        , m.data
        , make_capsule(m));
}

и используется так:

py::scoped_interpreter guard{};
auto module = py::module::import("MyPackage.align_faces");
auto aligner = module.attr("align_all");
auto pil_converter = module.attr("cv_to_pil");

auto img = cv::imread("image1.jpg");
auto img_pil = pilConvertor(mat_to_nparray(img));
auto img_face_list = aligner(img_pil);

Обновление 2

Спасибо @DanMasek в комментариях, сохраняя копии изображений на python сторона, непосредственно перед отправкой их обратно на C ++ указанная выше проблема решена. Однако, как указал Ext3h в комментариях, в изображении также есть артефакт, который не исчез даже после последнего изменения.

Я сохранил два изображения (режим bgr) как в Python, так и в C ++ при использовании imwrite артефакт, который здесь показан, не отображается на этих изображениях. однако между двумя изображениями есть небольшая разница. изображение cpp немного увеличено по сравнению с версией python (они оба используют одну и ту же функцию (на самом деле c ++ вызывает для этого один и тот же модуль python)). и версия Python также больше по размеру:

cpp изображение (размер: 5492 байта):

enter image description here

Python image(size: (5,587 bytes)

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

В чем проблема? коды, которые я использовал, на самом деле не изменяют шаги / смещения любого типа), тогда что вызывает эту проблему?

...