博客專欄

        EEPW首頁 > 博客 > 深度解決添加復雜數據增強導致訓練模型耗時長的痛點(2)

        深度解決添加復雜數據增強導致訓練模型耗時長的痛點(2)

        發布人:計算機視覺工坊 時間:2022-12-22 來源:工程師 發布文章

        4. C++ And CUDA Extensions 

        For Python/ PyTorch


        C++ 與 Python 或 PyTorch 的交互,業界主流做法是采用 pybind11,關于Pybind11 的更多詳細說明可以參看文獻 [15],其核心原理如下圖所示:


        圖片

        pybind11 pipeline


        由于 PyTorch 的 C++ 拓展與純 Python 有一些區別,因為 PyTorch 的基礎數據類型是 torch.Tensor,該數據類型可以認為是 Pytorch 庫對 np.array 進行了更高一層的封裝。所以,在寫拓展程序時,其接口函數所需要的數據類型以及調用的庫會有些區別,下面會詳細解釋。


        4.1. C++ Extensions For Python


        首先我們看 Python 代碼,如下所示(scripts/test_warpaffine_opencv.py):





















        import cv2import torch  # 不能刪掉, 因為需要動態加載torch的一些動態庫,后面會詳細說明.import numpy as npfrom orbbec.warpaffine import affine_opencv  # C++ interface
        data_path = "./demo.png"img = cv2.imread(data_path, cv2.IMREAD_GRAYSCALE)
        # python中的numpy.array()與 pybind中的py::array_t一一對應.src_point = np.array([[262.0, 324.0], [325.0, 323.0], [295.0, 349.0]], dtype=np.float32)dst_point = np.array([[38.29, 51.69], [73.53, 51.69], [56.02, 71.73]], dtype=np.float32)# python interface mat_trans = cv2.getAffineTransform(src_point, dst_point)res = cv2.warpAffine(img, mat_trans, (600,800))cv2.imwrite("py_img.png", res)
        # C++ interfacewarpffine_img = affine_opencv(img, src_point, dst_point)cv2.imwrite("cpp_img.png", warpffine_img)


        從上述代碼可以看到,Python 文件中調用了 affine_opencv 函數,而 affine_opencv 的 C++ 實現在 orbbec/warpaffine/src/cpu/warpaffine_opencv.cpp 中,如下所示:








































        #include<vector>#include<iostream>#include<pybind11/pybind11.h>#include<pybind11/numpy.h>#include<pybind11/stl.h>#include<opencv2/opencv.hpp>

        namespace py = pybind11;
        /* Python->C++ Mat */cv::Mat numpy_uint8_1c_to_cv_mat(py::array_t<unsigned char>& input){        ...}
        cv::Mat numpy_uint8_3c_to_cv_mat(py::array_t<unsigned char>& input){        ...}
        /* C++ Mat ->numpy */py::array_t<unsigned char> cv_mat_uint8_1c_to_numpy(cv::Mat& input){        ...}
        py::array_t<unsigned char> cv_mat_uint8_3c_to_numpy(cv::Mat& input){        ...}
        py::array_t<unsigned char> affine_opencv(py::array_t<unsigned char>& input,                                        py::array_t<float>& from_point,                                        py::array_t<float>& to_point){        ...}


        由于本工程同時兼容了 PyTorch 的 C++/CUDA 拓展,為了更加規范,這里在拓展接口程序(orbbec/warpaffine/src/warpaffine_ext.cpp)中通過 PYBIND11_MODULE 定義好接口,如下所示:














































        #include <torch/extension.h>#include<pybind11/numpy.h>
        // python的C++拓展函數申明py::array_t<unsigned char> affine_opencv(py::array_t<unsigned char>& input,                                        py::array_t<float>& from_point,                                        py::array_t<float>& to_point);
        // Pytorch的C++拓展函數申明(CPU)at::Tensor affine_cpu(const at::Tensor& input,          /*[B, C, H, W]*/                      const at::Tensor& affine_matrix,  /*[B, 2, 3]*/                      const int out_h,                      const int out_w);
        // Pytorch的CUDA拓展函數申明(GPU)#ifdef WITH_CUDAat::Tensor affine_gpu(const at::Tensor& input,          /*[B, C, H, W]*/                      const at::Tensor& affine_matrix,  /*[B, 2, 3]*/                      const int out_h,                      const int out_w);#endif
        // 通過WITH_CUDA宏進一步封裝Pytorch的拓展接口at::Tensor affine_torch(const at::Tensor& input,          /*[B, C, H, W]*/                                  const at::Tensor& affine_matrix,  /*[B, 2, 3]*/                                  const int out_h,                                  const int out_w){        if (input.device().is_cuda())          {#ifdef WITH_CUDA    return affine_gpu(input, affine_matrix, out_h, out_w);#else    AT_ERROR("affine is not compiled with GPU support");#endif          }          return affine_cpu(input, affine_matrix, out_h, out_w);}
        // 使用pybind11模塊定義python/pytorch接口PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {  m.def("affine_opencv", &affine_opencv, "affine with c++ opencv");  m.def("affine_torch", &affine_torch,   "affine with c++ libtorch");}


        從上面代碼可以看出,Python 中的 np.array 數組與 pybind11 的 py::array_t 相互對應,也即 Python 接口函數中,傳入的 np.array 數組,在 C++ 對應的函數中用 py::array_t 接收,操作 Numpy 數組,需要引入頭文件。


        數組本質上在底層是一塊一維的連續內存區,通過 pybind11 中的 request() 函數可以把數組解析成 py::buffer_info 結構體,buffer_info 類型可以公開一個緩沖區視圖,它提供對內部數據的快速直接訪問,如下代碼所示:










        struct buffer_info {    void *ptr;                         // 指向數組(緩沖區)數據的指針    py::ssize_t itemsize;              // 數組元素總數    std::string format;                // 數組元素格式(python表示的類型)    py::ssize_t ndim;                  // 數組維度信息    std::vector<py::ssize_t> shape;    // 數組形狀    std::vector<py::ssize_t> strides;  // 每個維度相鄰元素的間隔(字節數表示)};


        在寫好 C++ 源碼以后,在 setup.py 中將相關 C++ 源文件,以及依賴的第三方庫:opencv、pybind11 的路徑寫入對應位置(本工程已經寫好,請具體看 setup.py 文件),然后進行編譯和安裝:








        # 切換工作路徑step 1: cd F:/code/python_cpp_extension# 編譯step 2: python setup.py develop# 安裝, 如果沒有指定--prefix, 則最終編譯成功的安裝包(.egg)文件會安裝到對應的python環境下的site-packages下.step 3: python setup.py install


        【注】:關于工程文件中的 setup.py 相關知識可以參考文獻 [7]、[12]、[13],該三篇文獻對此有詳細的解釋。


        執行 step 2 和 step3 之后,如下圖所示,最終源碼文件會編譯成 .pyd 二進制文件(Linux 系統下編譯成 .so 文件),且會生成一個 Python 包文件:orbbec-0.0.1-py36-win-amd64.egg,包名取決于 setup.py 中規定的 name 和 version 信息,該安裝包會被安裝在當前 Python環境的 site-packages 文件夾下。


        同時,在終端執行命令:pip list,會發現安裝包以及對應的版本信息。安裝成功后,也就意味著,在該 Python環境(本工程的 Python環境是 cpp_extension)下,可以在任何一個 Python 文件中,導入 orbbec 安裝包中的接口函數,比如上述 scripts/test_warpaffine_opencv.py 文件中的語句:from orbbec.warpaffine import affine_opencv。


        圖片

        編譯和安裝成功


        圖片

        pip list 顯示相關安裝包信息


        編譯完成后,可以運行 tools/collect_env.py,查看當前一些必要工具的版本等一系列信息,輸出如下:


























        sys.platform    : win32Python  : 3.6.13 |Anaconda, Inc.| (default, Mar 16 2021, 11:37:27) [MSC v.1916 64 bit (AMD64)]CUDA available  : TrueCUDA_HOME       : C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1NVCC    : Not AvailableGPU 0   : NVIDIA GeForce GTX 1650OpenCV  : 3.4.0PyTorch : 1.5.0PyTorch compiling details       : PyTorch built with:  - C++ Version: 199711  - MSVC 191627039  - Intel(R) Math Kernel Library Version 2020.0.0 Product Build 20191125 for Intel(R) 64 architecture applications  - Intel(R) MKL-DNN v0.21.1 (Git Hash 7d2fd500bc78936d1d648ca713b901012f470dbc)  - OpenMP 200203  - CPU capability usage: AVX2  - CUDA Runtime 10.1  - NVCC architecture flags: -gencode;arch=compute_37,code=sm_37;-gencode;arch=compute_50,code=sm_50;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_61,code=sm_61;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_37,code=compute_37  - CuDNN 7.6.4  - Magma 2.5.2  - Build settings: BLAS=MKL, BUILD_TYPE=Release, CXX_FLAGS=/DWIN32 /D_WINDOWS  /GR  /w /EHa /bigobj -openmp -DNDEBUG -DUSE_FBGEMM, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, USE_CUDA=ON, USE_EXCEPTION_PTR=1, USE_GFLAGS=OFF, USE_GLOG=OFF, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=OFF, USE_NNPACK=OFF, USE_OPENMP=ON, USE_STATIC_DISPATCH=OFF,
        TorchVision     : 0.6.0C/C++ Compiler  : MSVC 191627045CUDA Compiler   : 10.1


        在運行 scripts/test_warpaffine_opencv.py 文件之前,由于 warpaffine_opencv.cpp 源碼用到相關 opencv 庫,因此,還需要配置動態庫路徑,Windows 系統配置如下:


        圖片

        Windows 相關環境配置(opencv 第三方庫)


        Linux 系統同樣也需要配置進行配置,命令如下:




        root@aistation:/xxx/code/python_cpp_extension# export LD_LIBRARY_PATH=/xxx/code/python_cpp_extension/3rdparty/opencv/linux/libroot@aistation:/xxx/code/python_cpp_extension# ldconfig


        也可以通過修改 ~/.bashrc 文件,加入上述 export LD_LIBRARY_PATH=/...,然后命令:source ~/.bashrc。也可以直接修改配置文件 /etc/profile,與修改 .bashrc 文件 一樣,對所有用戶有效。


        可以通過 tools 下的 Dependencies_x64_Release 工具(運行:DependenciesGui.exe),查看編譯好的文件(.pyd)依賴的動態庫是否都配置完好,如下圖所示:


        圖片

        檢查編譯好的動態庫依賴的動態庫路徑


        可以發現,該工具沒有找到 python36.dll、c10.dll、torch_cpu.dll、torch_python.dll 和 c10_cuda.dll 的路徑。


        這里說明一下,Python 相關的 dll 庫以及 torch 相關的動態庫是動態加載的,也就是說,如果你在 Python 代碼中寫一句:import torch,只有在程序運行時才會動態加載 torch 相關庫。


        所以,Dependencies_x64_Release工具檢查不到編譯好的 warpaffine_ext.cp36-win_amd64.pyd 文件依賴完好性。


        這里還需要說明一下為什么 warpaffine_ext.cp36-win_amd64.pyd 需要依賴 torch 相關庫,這是因為源文件 orbbec/warpaffine/src/warpaffine_ext.cpp 兼容了 PyTorch 的 C++ 拓展,所以依賴 torch 和 cuda 相關動態庫文件,如果你單純只在 orbbec/warpaffine/src/warpaffine_ext.cpp 實現純粹 Python 的 C++拓展,則是不需要依賴 torch 和 cuda 相關動態庫。


        配置好之后,還需要將 warpaffine_ext.cp36-win_amd64.pyd 無法動態加載的動態庫文件(opencv_world453.dll)放到 scripts/test_warpaffine_opencv.py 同路徑之下(Linux 系統也一樣),如下圖所示:


        圖片

        拷貝動態庫與測試腳本同一目錄


        需要注意一個問題,有時候,如果在 docker 中進行編譯和安裝,其最終生成的 Python 安裝包(.egg)文件并不會安裝到當前 Python 環境下的 site-packages 中。


        也就意味著,在 Python 文件中執行:from orbbec.warpaffine import affine_opencv 會失敗。


        原因是 orbbec.warpaffine 并不在其 Python 的搜索路徑中,這個時候有兩種解決辦法:一種是在執行:python setup.py install 時,加上 --prefix='install path',但是經過本人驗證,有時候不可行,另外一種辦法是在 Python 文件中,將 orbbec 文件夾路徑添加到 Python 的搜索路徑中,如下所示:











        import cv2import torch  # 不能刪掉, 因為需要動態加載torch的一些動態庫.import numpy as np
        # 添加下述兩行代碼,這里默認此python腳本所在目錄的上一層目錄路徑包含orbbec文件夾._FILE_PATH = os.path.dirname(os.path.abspath(__file__))sys.path.insert(0, os.path.join(_FILE_PATH, "../"))
        from orbbec.warpaffine import affine_opencv  # C++ interface



        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: AI

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 日土县| 鹤岗市| 石林| 明水县| 两当县| 革吉县| 永川市| 嫩江县| 昭通市| 武乡县| 江西省| 重庆市| 日喀则市| 宁城县| 海盐县| 广宁县| 惠来县| 衡东县| 那坡县| 衡阳市| 庆安县| 永济市| 长阳| 三江| 会东县| 孙吴县| 永仁县| 内黄县| 大名县| 天门市| 牙克石市| 拉孜县| 苏尼特左旗| 安平县| 眉山市| 和硕县| 靖宇县| 天长市| 平顶山市| 大足县| 双柏县|