最近学习和使用opencv读取和处理图像,然后用pytorch训练深度学习模型,部署和推理的时候用opencv+ncnn进行,过程中参考了很多网上教程和案例,结合源码发现了一个关于颜色空间的问题,很多网上资料的源码示例中并没有注意,自己也被误导了,特别记录一下,也供大家参考
核心思想总结:当我们所有的彩色图像处理都打算采用RGB格式数据的时候,切记用opencv读取图像后要将图像从BGR转成RGB,因为opencv读取彩色图像默认是BGR!!!
贴两段网上找的网友给出的代码,
代码一(opencv+ncnn,C++):
cv::Mat img = cv::imread("image.jpg", 1);
int w = img.cols;
int h = img.rows;
// subtract 128, norm to -1 ~ 1
ncnn::Mat input = ncnn::Mat::from_pixels_resize(img.data, ncnn::Mat::PIXEL_BGR, w, h, 100, 100);
float mean[1] = { 128.f };
float norm[1] = { 1/128.f };
in.substract_mean_normalize(mean, norm);
ncnn::Net net;
net.load_param("model.param");
net.load_model("model.bin");
ncnn::Extractor ex = net.create_extractor();
ex.set_light_mode(true);
ex.set_num_threads(4);
ex.input("data", in);
ncnn::Mat feat;
ex.extract("output", feat);
代码二(opencv+pytorch,python):
img = cv2.imread('a.png', 1).astype(np.uint8)
img = cv2.resize(img, (224,224))
img = img / 255.
img = torch.from_numpy(img)
img = torch.tensor(img, dtype=torch.float32)
img = img.unsqueeze(0) #在第一维度上增加一个维度,作为batch size大小
img = img.permute(0, 3, 1, 2) # 将图像channel提到前面即 [batch size, height, width, channel]-> [batch size, channel, height, width]
'''
opencv读取图像的shape是(height, width,channel),
而pytorch的模型在接收图像tensor的时候要求的shape是(channel,height,width)
'''
net = nn.Conv2d(3, 64, kernel_size=7, stride=2)
a = net(img)
可以看到,这两段代码都是opencv读取了之后,做了相应的处理之后送入模型,这些处理都是关于数据格式进行的,不做程序无法运行或报错。
当然,这两段代码是没有错误的,运行非常成功,于是乎,可能很多网友就和我一样,写代码的时候直接就这么用了,好像也没太多毛病。
然而,实际使用过程中,因为我训练的时候用pytorch直接给的开源代码训练,我注意到里面有这样的一句代码:
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
查了一些资料得知,大部分预训练模型都是在RGB这样颜色通道排序的基础下训练的。
但是,当我用ncnn的时候,由于跟训练使用的pytorch不是一套代码,也从python转成了C++,在参考资料的时候简单的就用了上面代码一类似的流程,运行当然是毫无问题的,并且当时也和同组小伙伴认为,因为opencv读取的图像是BGR的通道排序,于是在用函数ncnn::Mat::from_pixels_resize
创建ncnn矩阵的时候也用参数PIXEL_BGR
就可以了,所以有了下面这个语句:
ncnn::Mat input = ncnn::Mat::from_pixels_resize(img.data, ncnn::Mat::PIXEL_BGR, w, h, 100, 100);
这个函数具体的讲解这里就不多说了,但是当我带着怀疑和好奇阅读ncnn源码的时候,却发现了问题。实际上,在这个函数中,如果使用参数PIXEL_BGR
或PIXEL_RBG
,其实都执行的是同样的操作,都是输入的img.data是什么通道顺序,那么输出就是保持什么通道顺序,不管用哪个都是一样的结果。
正确的应该是:把PIXEL_BGR
或PIXEL_RGB
改为PIXEL_RGB2BGR
或PIXEL_BGR2RGB
(这两个通道顺序的转换其实是一样的操作),原理大家可以看看ncnn中关于这个函数的源码,有时间或者大家有需要的,我再独立写一篇。
最后说一下,因为这个错误并不影响程序的运行,只影响结果,当纠正这个错误以后,发现果然程序的最终结果比之前要好了一些!!