当前位置:   article > 正文

移动端unet人像分割模型--2_ncnn 人像分割

ncnn 人像分割

  前一篇blog里提到的错误果然是mxnet网络的问题,pool5误敲成pool4修改之后,ncnn就不再crash,不过ncnn的mxnet2ncnn这个工具应该多加一些诊断确保转换的模型参数一致才对。

  只是事情也没那么一帆风顺,转成ncnn后的预测结果死活不对。没办法,只能一层层去检查,写了几个简单的工具可以打印中间隐藏层的结果。

  check.py

  1. import os
  2. os.environ["MXNET_BACKWARD_DO_MIRROR"] = "1"
  3. os.environ["MXNET_CUDNN_AUTOTUNE_DEFAULT"] = "0"
  4. import sys
  5. import cv2
  6. import mxnet as mx
  7. from mxnet import ndarray as F
  8. from skimage.transform import resize
  9. from skimage.io import imsave
  10. import numpy as np
  11. from unetdataiter import UnetDataIter
  12. import matplotlib.pyplot as plt
  13. from unet import build_unet
  14. np.set_printoptions(threshold=np.inf)
  15. def post_process_mask(label, img_cols, img_rows, n_classes, p=0.5):
  16. pr = label.reshape(n_classes, img_cols, img_rows).transpose([1,2,0]).argmax(axis=2)
  17. return (pr*255).asnumpy()
  18. def ncnn_output(label):
  19. #pr = label.reshape(channels, img_cols, img_rows).transpose([1,2,0])
  20. pr = label.transpose([1,2,0])
  21. return pr.asnumpy()
  22. def load_image(img, width, height):
  23. im = np.zeros((height, width, 3), dtype='uint8')
  24. #im[:, :, :] = 128
  25. if img.shape[0] >= img.shape[1]:
  26. scale = img.shape[0] / height
  27. new_width = int(img.shape[1] / scale)
  28. diff = (width - new_width) // 2
  29. img = cv2.resize(img, (new_width, height))
  30. im[:, diff:diff + new_width, :] = img
  31. else:
  32. scale = img.shape[1] / width
  33. new_height = int(img.shape[0] / scale)
  34. diff = (height - new_height) // 2
  35. img = cv2.resize(img, (width, new_height))
  36. im[diff:diff + new_height, :, :] = img
  37. im = np.float32(im) / 255.0
  38. return [im.transpose((2,0,1))]
  39. def main():
  40. batch_size = 16
  41. n_classes = 2
  42. img_width = 256
  43. img_height = 256
  44. #img_width = 96
  45. #img_height = 96
  46. ctx = [mx.gpu(0)]
  47. # sym, arg_params, aux_params = mx.model.load_checkpoint('unet_person_segmentation', 20)
  48. #unet_sym = build_unet(batch_size, img_width, img_height, False)
  49. # unet = mx.mod.Module(symbol=unet_sym, context=ctx, label_names=None)
  50. sym, arg_params, aux_params = mx.model.load_checkpoint('unet_person_segmentation', 0)
  51. all_layers = sym.get_internals()
  52. print(all_layers.list_outputs())
  53. unet = mx.mod.Module(symbol=all_layers['conv11_1_output'], context=ctx, label_names=None)
  54. #unet = mx.mod.Module(symbol=all_layers['pool5_output'], context=ctx, label_names=None)
  55. unet.bind(for_training=False, data_shapes=[['data', (batch_size, 3, img_width, img_height)]], label_shapes=unet._label_shapes)
  56. #unet.set_params(arg_params, aux_params, allow_missing=True)
  57. unet.set_params(arg_params, aux_params)
  58. testimg = cv2.imread(sys.argv[1], 1)
  59. img = load_image(testimg, img_width, img_height)
  60. unet.predict(mx.io.NDArrayIter(data=[img]))
  61. outputs = unet.get_outputs()[0]
  62. print(outputs[0].shape)
  63. output = ncnn_output(outputs[0])
  64. print(output)
  65. #keys = unet.get_params()[0].keys() # 列出所有权重名称
  66. #print(keys)
  67. #conv_w = unet.get_params()[0]['trans_conv6_weight'] # 获取想要查看的权重信息
  68. #print(conv_w.shape)
  69. #print(conv_w.asnumpy()) # 查看具体数值
  70. #cv2.imshow('test', testimg)
  71. #cv2.imshow('mask', post_process_mask(outputs[0], img_width, img_height, n_classes))
  72. #cv2.waitKey()
  73. if __name__ == '__main__':
  74. if len(sys.argv) < 2:
  75. print("illegal parameters")
  76. sys.exit(0)
  77. main()

  在这个基础之上,发现是第一次反卷积就出了问题(mxnet神经网络trans_conv6的输出)。结果完全不一致,按个人理解,反卷积算法会出问题的可能性比较小,所以把mxnet这一层的权重值打印了出来(上面注释掉的代码)。再在mxnet2ncnn的代码里把对应的参数打印,最后发现是num_group出了问题,简单处理就是把mxnet2ncnn.cpp里的反卷积num_group固定为1,终于解决问题。得到正确的输出结果:

  中间还遇到一些ncnn和mxnet之间图像格式之类的转换问题,特别是浮点数的处理,就不啰嗦了,直接上代码。

  1. #include "net.h"
  2. #include <opencv2/opencv.hpp>
  3. #include <string>
  4. #include <vector>
  5. #include <time.h>
  6. #include <algorithm>
  7. #include <map>
  8. #include <iostream>
  9. #include <opencv2/opencv.hpp>
  10. using namespace std;
  11. using namespace cv;
  12. #define INPUT_WIDTH 256
  13. #define INPUT_HEIGHT 256
  14. int main(int argc, char** argv) {
  15. if (argc < 2) {
  16. printf("illegal parameters!");
  17. exit(0);
  18. }
  19. ncnn::Net Unet;
  20. Unet.load_param("../models/ncnn.param");
  21. Unet.load_model("../models/ncnn.bin");
  22. cv::Scalar value = Scalar(0,0,0);
  23. cv::Mat src;
  24. cv::Mat tmp;
  25. src = cv::imread(argv[1]);
  26. if (src.size().width > src.size().height) {
  27. int top = (src.size().width - src.size().height) / 2;
  28. int bottom = (src.size().width - src.size().height) - top;
  29. cv::copyMakeBorder(src, tmp, top, bottom, 0, 0, BORDER_CONSTANT, value);
  30. } else {
  31. int left = (src.size().height - src.size().width) / 2;
  32. int right = (src.size().height - src.size().width) - left;
  33. cv::copyMakeBorder(src, tmp, 0, 0, left, right, BORDER_CONSTANT, value);
  34. }
  35. cv::Mat tmp1;
  36. cv::resize(tmp, tmp1, cv::Size(INPUT_WIDTH, INPUT_HEIGHT), CV_INTER_CUBIC);
  37. cv::Mat image;
  38. tmp1.convertTo(image, CV_32FC3, 1/255.0);
  39. std::cout << "image element type "<< image.type() << " " << image.cols << " " << image.rows << std::endl;
  40. // std::cout << src.cols << " " << src.rows << " " << image.cols << " " << image.rows << std::endl;
  41. //cv::imshow("test", image);
  42. //cv::waitKey();
  43. //ncnn::Mat ncnn_img = ncnn::Mat::from_pixels(image.data, ncnn::Mat::PIXEL_BGR2RGB, image.cols, image.rows);
  44. // cv32fc3 的布局是 hwc ncnn的Mat布局是 chw 需要调整排布
  45. float *srcdata = (float*)image.data;
  46. float *data = new float[INPUT_WIDTH*INPUT_HEIGHT*3];
  47. for (int i = 0; i < INPUT_HEIGHT; i++)
  48. for (int j = 0; j < INPUT_WIDTH; j++)
  49. for (int k = 0; k < 3; k++) {
  50. data[k*INPUT_HEIGHT*INPUT_WIDTH + i*INPUT_WIDTH + j] = srcdata[i*INPUT_WIDTH*3 + j*3 + k];
  51. }
  52. ncnn::Mat in(image.rows*image.cols*3, data);
  53. in = in.reshape(256, 256, 3);
  54. //ncnn::Mat in;
  55. //resize_bilinear(ncnn_img, in, INPUT_WIDTH, INPUT_HEIGHT);
  56. ncnn::Extractor ex = Unet.create_extractor();
  57. ex.set_light_mode(true);
  58. //sex.set_num_threads(4);
  59. ex.input("data", in);
  60. ncnn::Mat mask;
  61. //ex.extract("relu5_2_splitncnn_0", mask);
  62. //ex.extract("trans_conv6", mask);
  63. ex.extract("conv11_1", mask);
  64. //ex.extract("pool5", mask);
  65. std::cout << "whc " << mask.w << " " << mask.h << " " << mask.c << std::endl;
  66. #if 1
  67. cv::Mat cv_img = cv::Mat::zeros(INPUT_WIDTH,INPUT_HEIGHT,CV_8UC1);
  68. // mask.to_pixels(cv_img.data, ncnn::Mat::PIXEL_GRAY);
  69. {
  70. float *srcdata = (float*)mask.data;
  71. unsigned char *data = cv_img.data;
  72. for (int i = 0; i < mask.h; i++)
  73. for (int j = 0; j < mask.w; j++) {
  74. float tmp = srcdata[0*mask.w*mask.h+i*mask.w+j];
  75. int maxk = 0;
  76. for (int k = 0; k < mask.c; k++) {
  77. if (tmp < srcdata[k*mask.w*mask.h+i*mask.w+j]) {
  78. tmp = srcdata[k*mask.w*mask.h+i*mask.w+j];
  79. maxk = k;
  80. }
  81. //std::cout << srcdata[k*mask.w*mask.h+i*mask.w+j] << std::endl;
  82. }
  83. data[i*INPUT_WIDTH + j] = maxk;
  84. }
  85. }
  86. cv_img *= 255;
  87. cv::imshow("test", cv_img);
  88. cv::waitKey();
  89. #endif
  90. return 0;
  91. }

  至此,功能完成,有兴趣的请移步:https://github.com/xuduo35/unet_mxnet2ncnn

  另外,调试过程发现,ncnn的中间层输出和mxnet的输出不是完全一致,可能是有一些参数或者运算细节问题,不影响最后mask结果,暂时就不管了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/284987
推荐阅读
相关标签
  

闽ICP备14008679号