我有一个 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
or RGB2BGR
in the cvtColor
没有任何效果,并且它们的行为相同。
我还想知道是否有更好的方法将其放入正确的色彩空间,不需要像我编写的那样的 for 循环并使用硬件加速?由于此操作需要快速,但使用我当前的解决方案根本不好。
Edit
顺便说一句,这是我在 C++ 中调用的 Python 函数:
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
and align_face
最终称之为:
return cv2.warpAffine(cv_img, tfm, (crop_size[0], crop_size[1]))
Update 1
这些是我使用 Pybind11 将图像从 c++ 发送到 python 的片段(取自此处):
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);
Update 2
感谢评论中的@DanMasek,通过在将图像发送回 C++ 之前将图像副本存储在 python 端,上述问题得到了解决。然而,正如 Ext3h 在评论中指出的那样,图像中也存在伪影,即使在最新的更改之后也没有消失。
我使用 Python 和 C++ 保存了两个图像(bgr 模式)imwrite
,此处显示的工件并未出现在这些图像中。然而,这两个图像之间存在细微差别。与 python 版本相比,cpp 图像有点放大(它们都使用完全相同的函数(事实上,c++ 为此调用完全相同的 python 模块))。 Python 版本的尺寸也更大:
cpp 图像(大小:5,492 字节):
Python图像(大小:(5,587字节)
这里有什么问题?事实上,我使用的代码不会改变任何类型的步幅/偏移)那么是什么阻止了这个问题呢?