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

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

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

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

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

Это вывод изображения без какого-либо вмешательства

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

Это результат, когда я использую преобразование цветового пространства из BGR2RGB:

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

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

И вот что я получаю, когда пытаюсь преобразовать как RGB2BGR:

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

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

И, как видите, два последних идентичны. Поэтому я попытался вручную изменить каналы и посмотреть, смогу ли я заставить это работать. Фрагмент кода:

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);

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

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

Очевидно, что это RGB, и все выглядит хорошо, и кажется, что изображение было в формате GBR! Я понятия не имею, что вызывает это.

Также я заметил, что я должен выполнить преобразование, иначе следующие циклы for сообщат мне нарушения доступа к памяти! что мне странно! Изменение BGR2RGB или RGB2BGR в cvtColor не имеет никакого эффекта, и они действуют одинаково.

Я также хотел бы знать, есть ли лучший способ поместить это в правильное цветовое пространство, что-то, что не требует цикла for, подобного тому, который я написал, и использует аппаратное ускорение? Поскольку эта операция должна быть быстрой, но использование текущего решения, которое у меня есть, совсем не годится.

Редактировать

Кстати, это функция Python, которую я вызывал в своем 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 байта):

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

Изображение Python (размер: (5 587 байт)

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

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


person Rika    schedule 05.08.2020    source источник
comment
Я думаю, что в вашей ручной попытке есть ошибка, внутри for, когда вы читаете [2] в своей третьей строке, вы обнаруживаете, что она равна [0] из-за первой строки. вы должны использовать временную переменную для хранения старого значения [2].   -  person bracco23    schedule 05.08.2020
comment
@ bracco23 Спасибо, но если вы присмотритесь, то увидите, что он читает из img и записывает в img2, поэтому его не перезаписывают.   -  person Rika    schedule 05.08.2020
comment
Ага, моя вина, ты прав.   -  person bracco23    schedule 05.08.2020
comment
Вы уверены, что не представили где-то непреднамеренный случайный выход? Между RGB и случайной интерпретацией его как GBR есть только одно смещение байта, позднее также включает проблемы с выравниванием в OpenCV. На самом деле отстает более чем на один, так как у вас также есть этот рыбный артефакт, показывающий справа от лица. Это означает, что вы полностью не выровнены.   -  person Ext3h    schedule 05.08.2020
comment
Как отправить изображение из Python? В формате RAW RGB или в формате JPEG или PNG? Вы пробовали отправить чисто красное изображение, а затем чисто синее?   -  person Mark Setchell    schedule 05.08.2020
comment
@ Ext3h: вполне возможно, что на стороне C ++, когда я отправляю изображение, что-то происходит. на python, он проверен, и все работает на 100% нормально. Я использую Pybind11 и использовал (эту ссылку) [stackoverflow.com/questions/60949451/ для отправки изображения с c ++ на python. просто скопировал и вставил его, и, похоже, он работает, кроме этого!   -  person Rika    schedule 05.08.2020
comment
@MarkSetchell, пожалуйста, прочтите мой предыдущий комментарий. Я использовал здесь фрагмент: stackoverflow.com/questions/60949451/   -  person Rika    schedule 05.08.2020
comment
Я обновил вопрос кодами, которые использую для отправки изображения с c ++ на python   -  person Rika    schedule 05.08.2020
comment
Каковы размеры входных и выходных изображений? Проблема может быть связана с походкой. См. Эту ссылку: medium.com/@ oleg.shipitko /   -  person Piotr Siekański    schedule 05.08.2020
comment
@ PiotrSiekański: ввод 168x300 (строки, столбцы), а вывод 112x112. Большое спасибо, я посмотрю на это   -  person Rika    schedule 05.08.2020
comment
Что, если вы измените img_list.append(img[:, :, ::-1]), чтобы добавить его копию? Я помню, что основные привязки OpenCV не любят представления массивов с такими перевернутыми индексами, возможно, это проблема для pybind (или код, который я написал в своем ответе, не учитывал такой случай)?   -  person Dan Mašek    schedule 05.08.2020
comment
@ DanMašek Большое спасибо :) Я сделал, как вы сказали, и, на удивление, теперь все в порядке! Я имею в виду, что в этом цикле нет необходимости, и выполнение cv::cvtColor(img, img2, cv::COLOR_BGR2RGB); просто печатает правильное изображение!   -  person Rika    schedule 05.08.2020
comment
Я бы очень хотел, если бы вы могли опубликовать это в качестве ответа и немного объяснить, что здесь пошло не так?   -  person Rika    schedule 05.08.2020
comment
Что касается артефакта на изображении, он все еще присутствует, и я обновил вопрос по этому поводу. разница небольшая, но она существует, хотя, имхо, ее не должно быть. Как видите, я не использовал никаких смещений / шагов в коде cpp. так что же решает эту проблему? я должен создать для этого еще один вопрос? или можно продолжить это здесь?   -  person Rika    schedule 05.08.2020
comment
Что касается моего более раннего предложения - массивы numpy более гибкие в отношении индексации по сравнению с cv::Mat. Например, [:, :, ::-1] просто сообщает numpy о необходимости доступа к последнему измерению в обратном порядке, но Mat OpenCV не может этого сделать. Когда у меня будет время для этого, я рассмотрю ответ, на который вы вдохновились, чтобы добавить несколько проверок для обнаружения таких несовместимостей и более глубоко исследовать эту проблему. В любом случае, создавая копию, вы сбрасываете всю эту странность индексации - это означает, что данные следует читать в странном порядке, но записывать в обычном порядке.   -  person Dan Mašek    schedule 06.08.2020
comment
Увеличенное различие между этими двумя изображениями похоже на то, что сделал бы warpAffine ... Может быть, в одном сценарии вы на самом деле не сохраняете результат преобразования? Может превратить это в новый вопрос.   -  person Dan Mašek    schedule 06.08.2020
comment
Позвольте нам продолжить это обсуждение в чате.   -  person Dan Mašek    schedule 06.08.2020