opencv+pytorch+ncnn使用CNN学习颜色空间RGB踩坑记(附带NCNN源码学习)

最近学习和使用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_BGRPIXEL_RBG,其实都执行的是同样的操作,都是输入的img.data是什么通道顺序,那么输出就是保持什么通道顺序,不管用哪个都是一样的结果。

正确的应该是:把PIXEL_BGRPIXEL_RGB改为PIXEL_RGB2BGRPIXEL_BGR2RGB(这两个通道顺序的转换其实是一样的操作),原理大家可以看看ncnn中关于这个函数的源码,有时间或者大家有需要的,我再独立写一篇。

最后说一下,因为这个错误并不影响程序的运行,只影响结果,当纠正这个错误以后,发现果然程序的最终结果比之前要好了一些!!

你可能感兴趣的:(opencv,pytorch,cnn)