• LibTorch_物体检测_RCNN


    上次实现的物体检测,借助了detectron2。

    现在要移植到c++上,detectron2里面的模型大概是不能用了。

    安装libtorch,浏览器下载很慢,换成wget就很快了,这里操作了一下给终端设置代理,但是实际用的时候好像没有代理也很快,不知道。

    libtorch = 1.5

    例子hello_libtorch,https://pytorch.org/cppdocs/installing.html (输出一个tensor,可以用来验证库啥的有没有问题)

    (好的,突然jupyter 突然打不开了。昨晚还好好的,不知道是不是我设置终端代理的原因。把代理proxychains删了果然就好了。尴尬

    第二个例子,参考https://github.com/apachecn/pytorch-doc-zh/blob/master/docs/1.0/cpp_export.md,实现一下。

    Tracing方法看起来还算好懂的。另一种方法适用model里面有各种基于输入判断的。

    例子里面的代码还是比较落后的,有博客整理了一些坑,1.0到1.5变化不小,这里建议大家去pytorch官网找例子运行。

    上面的两个例子,都是加载resnet18的,下面加载rcnn系列的,真的坑,真的坑。

    OK, 切换到物体检测模型,https://pytorch.org/docs/master/torchvision/models.html#object-detection-instance-segmentation-and-person-keypoint-detection,试了一下pre_trained faster rcnn,巨慢,我印象里detectron2里面的maskrcnn也没有这么慢啊,咋回事,那个就5,6秒,重启了一次,好像速度变正常了。

    python下序列化模型,注意这里不是trace方法,是script方法, rcnn不能用trace方法来序列化,原因的话,没看懂。

    model = fasterrcnn_resnet50_fpn(pretrained=True)
    model.eval()
    traced_model = torch.jit.script(model)

    接下来就都是坑了。首先,莫名其妙的需要torchvision了,libtorch不够用了。而且这个torchvision要自己从源码编译出来的,不是安装pytorch自带的torchvision,这里这些包的关系有点混乱,笔者也还没搞清楚。

    主要参考

    https://github.com/pytorch/vision/issues/1849

    https://github.com/pytorch/vision/pull/1407

    前面1849这个issue的例子,就是个完整的过程。

     #c++代码

     1 #include <torch/script.h> // One-stop header.
     2 #include <iostream>
     3 #include <memory>
     4 
     5 #include <iostream>
     6 #include "torch/script.h"
     7 #include "torch/torch.h"
     8 #include "torchvision/vision.h"
     9 #include "torchvision/ROIAlign.h"
    10 #include "torchvision/ROIPool.h"
    11 #include "torchvision/empty_tensor_op.h"
    12 #include "torchvision/nms.h"
    13 #include <cuda.h>
    14 
    15 using namespace std;
    16 
    17 static auto registry =
    18         torch::RegisterOperators()
    19                 .op("torchvision::nms", &nms)
    20                 .op("torchvision::roi_align(Tensor input, Tensor rois, float spatial_scale, int pooled_height, int pooled_width, int sampling_ratio) -> Tensor",
    21                     &roi_align)
    22                 .op("torchvision::roi_pool", &roi_pool)
    23                 .op("torchvision::_new_empty_tensor_op", &new_empty_tensor);
    24 
    25 
    26 int main(int argc, const char* argv[]) {
    27   if (argc != 2) {
    28     std::cerr << "usage: example-app <path-to-exported-script-module>
    ";
    29     return -1;
    30   }
    31 
    32 
    33   torch::jit::script::Module module;
    34   try {
    35     // Deserialize the ScriptModule from a file using torch::jit::load().
    36     module = torch::jit::load(argv[1]);
    37   }
    38   catch (const c10::Error& e) {
    39     std::cerr << "error loading the model
    ";
    40     return -1;
    41   }

    可以看到头文件里,有很多libtorch里面没有的库,抬头是torchvision。一想,torchvision,不是安装pytorch的时候就一起安装了吗,跑到conda对应目录下面找,不好意思,并没有这些头文件。但是去官方的github下面,是有这些个头文件的,好像就少了个csrc文件夹,然后这些库文件都在这个目录下。那去https://github.com/pytorch/vision/下载源码,然后编译安装,不幸的是,官方给的编译方法也失败了,这个当时没记报错,大家能直接编译的应该也OK的。这个下载和编译的过程,在https://github.com/pytorch/vision/issues/1849里面已经提到了,我按它的做法来了是Ok的,总之选择一个位置安装编译之后的torchvision库,在include文件夹下面,是能找到上面出现的头文件像vision.h。底下的cmake指令,如果用cuda,要加一个cuad支持,变成cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch -DCMAKE_INSTALL_PREFIX=/where/to/install/torchvision -DCMAKE_BUILD_TYPE=Release [-DWITH_CUDA=ON] ..

    1 git clone https://github.com/pytorch/vision.git
    2 cd vision
    3 mkdir build && cd build
    4 cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch -DCMAKE_INSTALL_PREFIX=/where/to/install/torchvision -DCMAKE_BUILD_TYPE=Release ..
    5 cmake --build . -j86 cmake --install .

    接下来,修改cmakelist, 因为原本,只要链接libtorch库,现在多了一个torchvison,这个地方笔者花了很多时间,本来cmake就不太会用。先看一下https://github.com/pytorch/vision/issues/1849 里面的方法。cmakelist写成

     1 cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
     2 project(custom_ops)
     3 
     4 set(pybind11_DIR /path/to/pybind11/share/cmake/pybind11)
     5 
     6 find_package(Torch REQUIRED)
     7 find_package(TorchVision REQUIRED)
     8 
     9 add_executable(example-app example-app.cpp)
    10 target_link_libraries(example-app "${TORCH_LIBRARIES}" TorchVision::TorchVision)
    11 set_property(TARGET example-app PROPERTY CXX_STANDARD 14)

    他们用find package加上运行时指定cmake的prefix,就可以顺利找到torchvision然后链接。前面只链接libtorch的时候有,cmake命令是

    cmake -DCMAKE_PREFIX_PATH=/path/to/libtorch ..
    #现在增加一个torchvision的路径
    cmake -DCMAKE_PREFIX_PATH="/path/to/libtorch;/where/to/install/torchvision" ..

    然后理论上能顺利编译。但是笔者这里不行,cmake的运行是顺利的,没有说find package(torchvison)失败,但是make过程,一直提示头文件找不到,然后笔者各种百度加问同学,决定还是暴力的把所需的库链接上去。

    哪个头文件提示找不到,就去torchvison和libtorch下找到对应的目录,然后include_directories。(PS, PYTHON.h找不到我还是有点惊讶的。)

    include_directories("/home/xxxx/torchvision/include")
    include_directories("/home/xxxx/SLAM/libtorch/libtorch/include/torch/csrc/api/include/")
    include_directories("/home/xxxx/SLAM/libtorch/libtorch/include/")
    include_directories("/home/xxxx/anaconda3/envs/detectron2/include/python3.7m/")

    只是把头文件目录链接进来还不够,毕竟真正的库还没找到

    target_link_libraries(example-app "${TORCH_LIBRARIES}" TorchVision::TorchVision  /home/xxxx/torchvision/lib/libtorchvision.so)

    这里把我们指明的libtorchvision.so文件放进来。torchvision::torchvision是find_package找到的包,我在这里没管,可能删掉也不影响了。

    然后在编译运行,ok了。出现了很多warning,先不管。回顾一下,为什么resnet不需要这些麻烦的步骤,似乎是因为rcnn存在多个输出,label啊,score啊,方框啊等等,所以才导致开发人员要通过一些策略,来使其运行,就是static auto registry =这段代码的含义,更深刻的我也解释不了了,期待专业人士吧。换句话说,libtorch这里自身还没有开发得很完善,可能再等几个版本这些东西都不需要了。这里笔者是因为自身pytorch用的觉得更方便顺手,所以一开始就选择了pytorch下的c++框架,或许tensorflow那边开发的更完善也说不定。对了,上面的指令我都是在cpu环境测试的,用gpu的朋友,有些地方要设定cuda之类的,可以在上面的参考链接里找,印象里就编译otrchvision里面有一个。

    上面的步骤,才刚刚让模型能成功加载出来,我们的目标是,让模型识别一张图片并读取输出。

    把main里面的代码修改成这样,测试一下模型能不能顺利输出。笔者这里都是可以的了,就是warning比较多。

    if (argc != 2) {
        std::cerr << "usage: example-app <path-to-exported-script-module>
    ";
        return -1;
      }
    
      torch::jit::getProfilingMode() = false;
      torch::jit::getExecutorMode() = false;
      torch::jit::setGraphExecutorOptimize(false);
    
    
      // Deserialize the ScriptModule from a file using torch::jit::load().
      torch::jit::script::Module module = torch::jit::load(argv[1]);
    
      std::cout << "load is good" <<std::endl;
    
      torch::Tensor tensor_image = torch::ones({3,480,640});
      std::cout << "tensor image is good" <<std::endl;
    
      c10::List<Tensor> images = c10::List<Tensor>({tensor_image, tensor_image});
      std::cout << "list tensor image is good" <<std::endl;
    
      std::vector<torch::jit::IValue> inputs;
      inputs.emplace_back(images);
      torch::jit::IValue output = module.forward(inputs);
      std::cout << "output is good" <<std::endl;

    接下来,读取一张图片,把它变成image tensor,或者说,读取opencv的一个frame,把它变成image tensor。

    然后就是大坑,imread出问题了,imread未定义的报错。这个错github上也看到人讨论了,讲是ABI11的问题,官方给的libtorch没有ABI-11,但是opencv是需要这个的,什么是abi11,百度一下,反正我是没太看明白。

    关键在于,libtorch1.5版本,官方已经开始提供有ABI11支持的libtorch库了,两种我都下载了,之前一直测试的都是没有ABI-11支持的,那我想,遇到这个bug不是换一个库就行了吗,简单。

    并不行,换成了abi-11的那个库,make 的时候torchvison 开始报错说找不到torch里面的函数,就很怪,也没搜到相关资料;这里笔者失误了,现在想起来应该先测试torch和opencv共存的问题,torchvision报错之后再解决,当时比较快的就去找别的方案了。

    最后找了一圈的方案是,把opencv卸了重装,编译opencv的时候把abi11去掉。细节参考https://blog.csdn.net/a1040193597/article/details/105011008

    (手动删opencv也删了半天,,以前安装的目录根本找不到。。装了3.4.0之后,还不知道orbslam能不能运行了,希望可以。)

    opencv这边一直没出什么bug,重装之后,程序就能运行了。稍微在源码里看了下API,大概知道咋回事了

     1 Mat frame = cv::imread("1.png", IMREAD_COLOR);    
     2 cvtColor(frame, frame, CV_BGR2RGB); //转换色彩通道
     3   frame.convertTo(frame, CV_32FC3, 1.0f / 255.0f);
     4   auto tensor_image = torch::from_blob(frame.data, {frame.rows, frame.cols, frame.channels()});
     5 //换chw;
     6   tensor_image = tensor_image.permute({2, 0, 1});
     7   //cout << "tensor shape " << tensor_image <<endl;
     8 
     9 //禁止一些服务加速模型推理的
    10   torch::jit::getProfilingMode() = false;
    11   torch::jit::getExecutorMode() = false;
    12   torch::jit::setGraphExecutorOptimize(false);
    13 
    14   // Deserialize the ScriptModule from a file using torch::jit::load().
    15   torch::jit::script::Module module = torch::jit::load(argv[1]);
    16 
    17   c10::List<Tensor> images = c10::List<Tensor>({tensor_image});
    18   std::vector<torch::jit::IValue> inputs;
    19   inputs.emplace_back(images);
    20   torch::jit::IValue output = module.forward(inputs);
    21   //怪怪的,output确实和最原始的rcnn不一样,里面多了一个空的{};
    22 
    23   auto out1 = output.toTuple();
    24   auto dets0 = out1->elements().at(0);
    25 
    26   auto dets1 = out1->elements().at(1).toList().get(0).toGenericDict() ;
    27 
    28   at::Tensor masks = dets1.at("scores").toTensor();
    29   at::Tensor labels = dets1.at("labels").toTensor();
    30   at::Tensor boxes = dets1.at("boxes").toTensor();
    31 
    32   int label = labels[10].item().toInt();
    33   cout <<  "labels is " << label <<  endl;
    34 
    35   float c1 = boxes[0][0].item().toFloat();
    36   float c2 = boxes[0][1].item().toFloat();
    37   float c3 = boxes[0][2].item().toFloat();
    38   float c4 = boxes[0][3].item().toFloat();
    39 
    40   cout << "box 0 is " << c1 << "---" << c2 << "---" << c3 << "---" << c4 << "---" << endl;

    几个重要的API就是,from_blob,把mat转tensor,permute改一下维度顺序,比较奇怪的是,最后output的输出是,一个tuple里面包含了一个空的字典和一个list,list里面再是一个dict,反正层层解外套后,能提取出我们想要的数据,转化成c++类型的api也在上面了。

    性能的评估:

    运行时间在我的笔记本cpu上要5s左右,加载模型要2s.

    c++和python对同一图片的计算结果是一致的。

    中间还有个要注意的是,model序列化之前要进行一次model.eval(),要不然存下来不能推理,这个容易忘。

    set(CMAKE_PREFIX_PATH "XXX/libtorch") //注意这里填自己解压libtorch时的路径,可以不用每次命令写prefix了。哇,要是多懂一点cmake,这两天可以省很多时间吧。

    https://www.cnblogs.com/geoffreyone/p/10827010.html 里面提到了一个不用imerad读取图片的API,然后不会报错,但是冲突的API应该包括imshow还有几个,所以大家可以考虑,这里作者也说新版本(2019.5)就可以了,不知道我这个更新的版本为什么还有问题。可能这个作者都是自己源码编译的,很有可能,libtorch源码编译也是一种方案,我没去试。

     
    https://blog.csdn.net/a1040193597/article/details/105011008 这个博客就是推荐我重新编译opencv的那个。帮了大忙
     
    https://discuss.pytorch.org/t/how-to-load-pictures-in-c-with-libtorch/45674/3 cv图片的读取
     
    https://www.jianshu.com/p/186bcdfe9492 最开始简单例程的bug。

    5.4更新

    libtorch结合orbslam一起运行,把物体识别写成一个模块。

    遇到几个问题

    1.编译时候c++14的选项,libtorch必须要c++14。cmakelist里面加上

    add_definitions(-std=c++14)

     2.

    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘nms(at::Tensor const&, at::Tensor const&, double)’中:
    InstanceDetect.cc:(.text+0x2e0): `nms(at::Tensor const&, at::Tensor const&, double)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x1d30):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘PSROIPool_forward(at::Tensor const&, at::Tensor const&, float, int, int)’中:
    InstanceDetect.cc:(.text+0x5a0): `PSROIPool_forward(at::Tensor const&, at::Tensor const&, float, int, int)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x2000):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ROIPool_forward(at::Tensor const&, at::Tensor const&, double, long, long)’中:
    InstanceDetect.cc:(.text+0x870): `ROIPool_forward(at::Tensor const&, at::Tensor const&, double, long, long)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x22e0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘PSROIAlign_forward(at::Tensor const&, at::Tensor const&, float, int, int, int)’中:
    InstanceDetect.cc:(.text+0xb50): `PSROIAlign_forward(at::Tensor const&, at::Tensor const&, float, int, int, int)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x25c0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ROIAlign_forward(at::Tensor const&, at::Tensor const&, double, long, long, long, bool)’中:
    InstanceDetect.cc:(.text+0xe30): `ROIAlign_forward(at::Tensor const&, at::Tensor const&, double, long, long, long, bool)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x28b0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ROIPool_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int)’中:
    InstanceDetect.cc:(.text+0x1130): `ROIPool_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x2bc0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘PSROIPool_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int)’中:
    InstanceDetect.cc:(.text+0x1430): `PSROIPool_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x2ed0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ROIAlign_backward(at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int, int, bool)’中:
    InstanceDetect.cc:(.text+0x1730): `ROIAlign_backward(at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int, int, bool)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x1a10):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘PSROIAlign_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int, int)’中:
    InstanceDetect.cc:(.text+0x1a40): `PSROIAlign_backward(at::Tensor const&, at::Tensor const&, at::Tensor const&, float, int, int, int, int, int, int, int)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x31e0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘roi_align(at::Tensor const&, at::Tensor const&, double, long, long, long, bool)’中:
    InstanceDetect.cc:(.text+0x4ea0): `roi_align(at::Tensor const&, at::Tensor const&, double, long, long, long, bool)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x7dd0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘new_empty_tensor(at::Tensor const&, c10::List<long>)’中:
    InstanceDetect.cc:(.text+0x4f50): `new_empty_tensor(at::Tensor const&, c10::List<long>)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x7e80):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘roi_pool(at::Tensor const&, at::Tensor const&, double, long, long)’中:
    InstanceDetect.cc:(.text+0x4fc0): `roi_pool(at::Tensor const&, at::Tensor const&, double, long, long)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x7ef0):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ps_roi_align(at::Tensor const&, at::Tensor const&, double, long, long, long)’中:
    InstanceDetect.cc:(.text+0x5060): `ps_roi_align(at::Tensor const&, at::Tensor const&, double, long, long, long)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x7f90):第一次在此定义
    CMakeFiles/ORB_SLAM2.dir/src/InstanceDetect.cc.o:在函数‘ps_roi_pool(at::Tensor const&, at::Tensor const&, double, long, long)’中:
    InstanceDetect.cc:(.text+0x5110): `ps_roi_pool(at::Tensor const&, at::Tensor const&, double, long, long)'被多次定义
    CMakeFiles/ORB_SLAM2.dir/src/System.cc.o:System.cc:(.text+0x8040):第一次在此定义

    3. 参考https://github.com/jiexiong2016/GCNv2_SLAM/issues/2,noabi的libtorch和dbow还要pangolin有了冲突。

    ../lib/libORB_SLAM2.so:对‘pangolin::Split(std::string const&, char)’未定义的引用
    ../lib/libORB_SLAM2.so:对‘pangolin::BindToContext(std::string)’未定义的引用
    ../lib/libORB_SLAM2.so:对‘DBoW2::FORB::toString(cv::Mat const&)’未定义的引用
    ../lib/libORB_SLAM2.so:对‘pangolin::CreateWindowAndBind(std::string, int, int, pangolin::Params const&)’未定义的引用
    ../lib/libORB_SLAM2.so:对‘DBoW2::FORB::fromString(cv::Mat&, std::string const&)’未定义的引用
    ../lib/libORB_SLAM2.so:对‘pangolin::CreatePanel(std::string const&)’未定义的引用

    尝试编译一个no abi的dbow2,然后报错,似乎是强制的。

    换个思路,abi11本来就是更好的选项。回到前面的问题,用了abi11的libtorch,为什么torchvision会出现链接错误,啊,是因为编译torchvision的时候选择了那个no-abi的libtorch。重新编译一个版本,OK了,abi的问题,现在所有的库都可以上有abi的版本了。

    回到错误2,重复定义的error,检查了一下,torchvision里面有几个,头文件,是包含了函数的定义的,在多cpp的工程下,就会出现这个问题。这个和ifndef是无关的。

    最直接的策略,把头文件和cpp文件拆开。




  • 相关阅读:
    Tomcat 生产服务器性能优化
    ExtJs3常用控件操作实例
    vss报错Workgroup无法访问,您可能没有权限使用网络资源解决办法
    Java Socket Example
    正则表达式:根据逗号解析CSV并忽略引号内的逗号
    java打jar包 命令行cmd在当前路径打jar包
    【Java/Json】Java对Json进行建模,分词,递归向下解析构建Json对象树
    【Json】Json分词器
    【java/Json】用Java对象构建Json语法树
    【Java文件】按UTF-8编码读取文本文件(逐行方式),排序,打印到控制台
  • 原文地址:https://www.cnblogs.com/zherlock/p/12771136.html
Copyright © 2020-2023  润新知