AI模型部署實戰:利用OpenCV的CUDA模塊加速視覺模型部署流程

本文首發于公眾號【DeepDriving】,歡迎關注。

一. 前言

我在之前的文章《AI模型部署實戰:利用CV-CUDA加速視覺模型部署流程》中介紹了如何使用CV-CUDA庫來加速視覺模型部署的流程,但是CV-CUDA對系統版本和CUDA版本的要求比較高,在一些低版本的系統中可能無法使用。對于像我這種不會寫CUDA代碼又想用CUDA來加速模型部署流程的人來說要怎么辦呢,其實還有一種方式,那就是使用OpenCV的CUDA接口

本文將介紹OpenCV CUDA模塊的基本使用方法(C++),以及如何使用這些接口來加速視覺模型部署。

二. 安裝CUDA版本OpenCV

Ubuntu 20.04系統中使用apt install命令安裝OpenCV是不會安裝CUDA模塊的,要想使用CUDA模塊只能用源碼進行編譯安裝。在Ubuntu系統中用源碼編譯安裝OpenCV 4.6版本的過程如下:

  1. 安裝必要的依賴

在用源碼編譯安裝OpenCV之前,需要先執行下面一系列命令安裝必要的依賴:

sudo apt update
sudo apt upgrade
sudo apt install build-essential cmake pkg-config unzip yasm git checkinstall
sudo apt install libjpeg-dev libpng-dev libtiff-dev
sudo apt install libavcodec-dev libavformat-dev libswscale-dev libavresample-dev
sudo apt install libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev
sudo apt install libxvidcore-dev x264 libx264-dev libfaac-dev libmp3lame-dev libtheora-dev
sudo apt install libfaac-dev libmp3lame-dev libvorbis-dev
sudo apt install libopencore-amrnb-dev libopencore-amrwb-dev
sudo apt-get install libdc1394-22 libdc1394-22-dev libxine2-dev libv4l-dev v4l-utils
cd /usr/include/linux
sudo ln -s -f ../libv4l1-videodev.h videodev.h
cd -
sudo apt-get install libgtk-3-dev
sudo apt-get install libtbb-dev
sudo apt-get install libatlas-base-dev gfortran
sudo apt-get install libprotobuf-dev protobuf-compiler
sudo apt-get install libgoogle-glog-dev libgflags-dev
sudo apt-get install libgphoto2-dev libeigen3-dev libhdf5-dev doxygen
sudo apt-get install opencl-headers
sudo apt-get install ocl-icd-libopencl1
  1. GitHub網站分別下載OpenCV 4.6.0的源碼包和擴展模塊源碼包
# 下載opencv-4.6.0源碼包
https://github.com/opencv/opencv/archive/refs/tags/4.6.0.zip
#下載4.6.0對應的擴展模塊源碼包
https://github.com/opencv/opencv_contrib/archive/refs/tags/4.6.0.zip

下載好以后把兩個包進行解壓。

  1. 按照下面的步驟編譯源碼并進行安裝:
cd opencv-4.6.0mkdir build && cd buildcmake -D CMAKE_BUILD_TYPE=RELEASE \-D CMAKE_INSTALL_PREFIX=/usr/local \-D INSTALL_PYTHON_EXAMPLES=OFF \-D INSTALL_C_EXAMPLES=OFF \-D WITH_TBB=ON \-D WITH_CUDA=ON \-D BUILD_opencv_cudacodec=OFF \-D ENABLE_FAST_MATH=1 \-D CUDA_FAST_MATH=1 \-D WITH_CUBLAS=1 \-D WITH_V4L=OFF \-D WITH_LIBV4L=ON \-D WITH_QT=OFF \-D WITH_GTK=ON \-D WITH_GTK_2_X=ON \-D WITH_OPENGL=ON \-D WITH_GSTREAMER=ON \-D OPENCV_GENERATE_PKGCONFIG=ON \-D OPENCV_PC_FILE_NAME=opencv.pc \-D OPENCV_ENABLE_NONFREE=ON \-D CUDA_nppicom_LIBRARY=stdc++ \-D OPENCV_PYTHON3_INSTALL_PATH=/usr/lib/python3/dist-packages \-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.6.0/modules \-D PYTHON_EXECUTABLE=/usr/bin/python3 \-D BUILD_EXAMPLES=OFF ..make -j8 && sudo make install

CMake的幾個參數需要注意一下:

-D WITH_CUDA=ON  # 這里必須設置為ON,否則無法使用CUDA模塊
-D CMAKE_INSTALL_PREFIX=/usr/local # OpenCV的安裝路徑,可以按照自己的需求指定
-D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.6.0/modules # 擴展模型源碼包的路徑

因為CMake過程中要下載很多依賴文件,如果速度很慢,可以加上配置選項-DOPENCV_DOWNLOAD_MIRROR_ID=gitcode,這樣就可以從國內鏡像下載了,速度會快很多。

安裝成功后,還需要設置一下環境變量:

export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/usr/local/opencv-4.6/lib/

三. OpenCV CUDA模塊的基本使用方法

OpenCV CUDA模塊的官方文檔詳細闡述了CUDA模塊提供的函數接口以及使用方法,在寫代碼之前我們應該好好學習一下這些文檔。

基礎數據結構GpuMat

在使用CPU的時候,OpenCV是使用數據結構cv::Mat來作為數據容器的;而在GPU上,則是使用一個新的數據結構cv::gpu::GpuMat,所有在GPU上調用的接口都是使用該數據結構作為輸入或輸出的。GpuMatMat的使用方式非常相似,封裝的接口基本上是一致的,詳細內容可以參考GpuMat的文檔。

CPUGPU之間的數據傳輸

OpenCV提供了非常簡單的接口實現CPUGPU之間的數據傳輸,也就是cv::Matcv::gpu::GpuMat之間的轉換:

  • upload: 把數據從CPU拷貝到GPU上;
  • download: 把數據從GPU拷貝到CPU上;

下面是一個簡單的示例:

#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>cv::Mat img = cv::imread("test.jpg");
// 把數據從CPU拷貝到GPU上
cv::cuda::GpuMat gpu_mat;
gpu_mat.upload(img);// 在GPU上對數據做處理// 把結果從GPU拷貝到CPU上 
cv::Mat result;
gpu_mat.download(result);
使用GPU做圖像預處理

在做視覺AI模型部署時,圖像數據預處理的基本流程如下:

1. 把OpenCV讀取的BGR格式的圖片轉換為RGB格式;
2. 把圖片resize到模型輸入尺寸;
3. 對像素值做歸一化操作;
4. 把圖像數據的通道順序由HWC調整為CHW;

以部署YOLOv6模型為例,在CPU上做圖像預處理的的代碼如下:

bool ImagePreProcessCpu(const cv::Mat &input_image, const int resize_width,const int resize_height, const double alpha,const double beta, float *const input_blob) {if (input_image.empty()) {return false;}if (input_blob == nullptr) {return false;}// 這里默認輸入圖像是RGB格式// resizecv::Mat resize_image;cv::resize(input_image, resize_image,cv::Size(resize_width, resize_height));// 像素值歸一化cv::Mat float_image;resize_image.convertTo(float_image, CV_32FC3, alpha, beta);// 調整通道順序,HWC->CHWconst int size = resize_width * resize_height;std::vector<cv::Mat> input_channels;cv::split(float_image, input_channels);for (int c = 0; c < resize_image.channels(); ++c) {std::memcpy(input_blob + c * size, input_channels[c].data,size * sizeof(float));}return true;
}

調用OpenCV CUDA模塊的接口做預處理的代碼如下:

bool ImagePreProcessGpu(const cv::Mat &input_image,const int resize_width,const int resize_height,const double alpha, const double beta,float *const input_blob) {if (input_image.empty()) {return false;}// 注意,這里input_blob是指向GPU內存if (input_blob == nullptr) {return false;}cv::cuda::GpuMat gpu_image, resize_image,float_image;gpu_image.upload(input_image);cv::cuda::resize(gpu_image, resize_image,cv::Size(resize_width, resize_height), 0, 0,cv::INTER_LINEAR);resize_image.convertTo(float_image, CV_32FC3, alpha, beta);const int size = resize_width * resize_height;std::vector<cv::cuda::GpuMat> split_channels;for (int i = 0; i < float_image.channels(); ++i) {split_channels.emplace_back(cv::cuda::GpuMat(cv::Size(resize_width, resize_height), CV_32FC1,input_blob + i * size));}cv::cuda::split(float_image, split_channels);return true;
}

可以看到,CPUGPU版本調用的函數名是一樣的,只不過GPU版本的多了一個cuda命名空間。所以使用OpenCVCUDA模塊基本上是沒有什么難度的,只需要查一下之前調用的CPU接口是否有對應的GPU版本就可以了。

使用CUDA

CUDA流是一系列異步操作的集合,通過在一個設備上并發地運行多個內核任務來實現任務的并發執行,這種方式使得設備的利用率更高。上面代碼調用的OpenCV CUDA模塊接口都是沒有使用CUDA流的,不過CUDA模塊為每個函數都提供了一個使用CUDA流的版本,使用起來也非常簡單。

OpenCV CUDA模塊的CUDA流封裝在cv::cuda::Stream類中,使用之前首先創建一個類對象

cv::cuda::Stream stream;

然后在調用每個CUDA接口的時候傳入該對象

gpu_image.upload(input_image,stream);

再在最后調用waitForCompletion()函數進行同步,確保該流上的所有操作都已完成。

使用CUDA流的圖像預處理代碼如下:

bool ImagePreProcessGpuStream(const cv::Mat &input_image,const int resize_width,const int resize_height,const double alpha, const double beta,float *const input_blob) {if (input_image.empty()) {return false;}if (input_blob == nullptr) {return false;}cv::cuda::Stream stream;cv::cuda::GpuMat gpu_image, resize_image,float_image;gpu_image.upload(input_image,stream);cv::cuda::resize(gpu_image, resize_image,cv::Size(resize_width, resize_height), 0, 0,cv::INTER_LINEA,stream);resize_image.convertTo(float_image, CV_32FC3, alpha, beta,stream);const int size = resize_width * resize_height;std::vector<cv::cuda::GpuMat> split_channels;for (int i = 0; i < float_image.channels(); ++i) {split_channels.emplace_back(cv::cuda::GpuMat(cv::Size(resize_width, resize_height), CV_32FC1,input_blob + i * size));}cv::cuda::split(float_image, split_channels,stream);stream.waitForCompletion();return true;
}

OpenCVCUDA流和原生的CUDA流可以通過結構體cv::cuda::StreamAccessor提供的兩個靜態函數進行轉換:

// 把OpenCV的CUDA流轉換為原生CUDA流
static cudaStream_t cv::cuda::StreamAccessor::getStream	(const Stream & stream	)	// 把原生CUDA流轉換為OpenCV的CUDA流
static Stream cv::cuda::StreamAccessor::wrapStream	(cudaStream_t stream)	

如果對CUDA流不了解,可以參考我之前寫的這篇文章。

四. 總結

本文介紹了OpenCV CUDA模塊中圖像處理接口的基本使用方法,用這些CUDA接口基本上可以滿足視覺AI模型的部署需求,在嵌入式平臺上可以有效減少CPU資源的消耗。當然,OpenCV提供的CUDA版本接口也有限,必要的時候也只能自己手搓CUDA代碼了。

五. 參考資料

  1. CUDA-accelerated Computer Vision
  2. How To Run Inference Using TensorRT C++ API
  3. Using TensorRT with OpenCV CUDA
  4. Getting Started with OpenCV CUDA Module
  5. GPU-accelerated Computer Vision
  6. OpenCV CUDA samples

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11748.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11748.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11748.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

大模型介紹

大模型通常指的是參數量超過億級別&#xff0c;甚至千億級別的深度學習模型。這類模型能夠處理更加復雜的任務&#xff0c;并在各項基準測試中取得了優異的成績。大模型在自然語言處理、計算機視覺、推薦系統等領域都取得了顯著的成果。 大模型的主要優勢在于其強大的表征能力&…

k8s的核心組件etcd功能詳解【含etcd各類參數詳細說明】

etcd 是 Kubernetes 中的一個關鍵組件&#xff0c;用于存儲集群的配置信息、狀態和元數據。它通常作為 Kubernetes 集群的數據存儲后端&#xff0c;為其他組件提供可靠的分布式鍵值存儲服務。下面我會詳細介紹 etcd 的功能以及常見的參數&#xff0c;以及如何配置和使用 etcd。…

Linux實驗 Shell編程

實驗目的&#xff1a; 熟練掌握Shell程序的建立與執行&#xff1b;掌握Shell變量的兩種類型&#xff08;Shell環境變量和用戶自定義變量&#xff09;及其用法&#xff1b;掌握Shell中的特殊字符、算術與邏輯運算&#xff1b;掌握Shell中輸入輸出命令&#xff1b;掌握Shell程序…

在Windows環境下安裝CPU版的PyTorch

PytTorch是基于Python開發的&#xff0c;首先需要安裝Python&#xff0c;Python的安裝很簡單&#xff0c;這里不再贅述。而 Windows用戶能直接通過conda、pip和源碼編譯三種方式來安裝PyTorch。 打開PyTorch官網&#xff08;PyTorch&#xff09;&#xff0c;在主頁中根據自己的…

基于OpenCV年齡與性別識別系統

深入解析基于OpenCV年齡與性別識別系統 在這篇博客中&#xff0c;我們將詳細解析一個使用OpenCV進行年齡和性別識別的Python腳本。這個腳本展示了如何利用深度學習模型&#xff0c;從視頻或圖像中檢測人臉并預測每個人臉的年齡和性別。 1. 導入必要的模塊 import cv2 as cv …

ELK的詳解

ELK是由Elasticsearch、Logstash和Kibana三個開源軟件&#xff08;后來又新加了一個FileBeat&#xff09;組成的日志管理解決方案&#xff0c;這一組合在近年來得到了廣泛的關注和應用。以下是對這三個組件的詳細說明&#xff1a; Elasticsearch&#xff1a; Elasticsearch是…

nginx 負載均衡配置詳解

基于 ${nginx_home}/conf/nginx.conf 文件配置實現&#xff0c;如下&#xff1a; http {# 定義server地址upstream server_group {server 192.168.xxx.1:8080;server 192.168.xxx.2:8080;server 192.168.xxx.3:8080;}server {listen 80;location / {root html;index …

python數據分析——時間序列

時間序列 前言一、Datetime 模塊常用函數和數據結構的詳細解釋datetime模塊示例一示例二 二、時間運算示例一示例二示例三 三、時間序列分析自回歸(Autoregressive model/AR)模型示例 滑動平均(moving average model/MA)模型示例 自回歸滑動平均(Autoregressive moving average…

持續總結中!2024年面試必問 100 道 Java基礎面試題(四十五)

上一篇地址&#xff1a;持續總結中&#xff01;2024年面試必問 100 道 Java基礎面試題&#xff08;四十四&#xff09;-CSDN博客 八十九、在Java中&#xff0c;什么是線程局部變量&#xff08;ThreadLocal變量&#xff09;&#xff1f; 在Java中&#xff0c;ThreadLocal變量是…

企業微信hook接口協議,ipad協議http,發送鏈接的方式邀請成員進群

發送鏈接的方式邀請成員進群 參數名必選類型說明uuid是String每個實例的唯一標識&#xff0c;根據uuid操作具體企業微信 請求示例 {"uuid":"3240fde0-45e2-48c0-90e8-cb098d0ebe43","roomid":10696052955013729, "vids":[788130334…

Flutter 中的 CircleAvatar 小部件:全面指南

Flutter 中的 CircleAvatar 小部件&#xff1a;全面指南 在 Flutter 中&#xff0c;CircleAvatar 是一個用于顯示頭像的圓形控件&#xff0c;通常包含一個圖標、圖片或者一個簡單的文本字符。它在設計上與 Material Design 指南中的頭像規范相匹配&#xff0c;常用于展示用戶信…

C# 常用匯總

時間處理 public static class DateTimeHelper{/// <summary>/// 獲取當前時間戳&#xff08;Unix時間戳&#xff09; /// </summary>/// <returns></returns>public static long GetCurrentUnixTimestamp(){DateTimeOffset offset DateTimeOffset.…

Qt---文件系統

一、基本文件操作 1. QFile對文件進行讀和寫 QFile file( path 文件路徑) 讀&#xff1a; file.open(打開方式) QlODevice::readOnly 全部讀取->file.readAll()&#xff0c;按行讀->file.readLine()&#xff0c;atend()->判斷是否讀到文件尾 …

Java網絡編程基礎

Java網絡編程基礎主要涉及進程間通信、網絡通信協議、IP地址和端口以及Java提供的網絡應用編程接口等核心概念。 進程間通信是Java網絡編程的基礎。進程是運行中的程序&#xff0c;而進程間通信則是指不同進程之間進行數據交換和共享信息的過程。在Java中&#xff0c;進程間的…

STM32存儲左右互搏 USB接口FATS文件讀寫U盤

STM32存儲左右互搏 USB接口FATS文件讀寫U盤 STM32的USB接口可以例化為Host主機從而對U盤進行操作。SD卡/MicroSD/TF卡也可以通過讀卡器轉換成U盤使用。這里介紹STM32CUBEIDE開發平臺HAL庫實現U盤FATS文件訪問的例程。 USB接口介紹 常見的USB接口電路部分相似而有不同的連接器…

K8S -----二進制搭建 Kubernetes v1.20

目錄 一、準備環境 1.1 修改主機名 1.2 關閉防火墻&#xff08;三臺一起&#xff0c;這里只展示master01&#xff09; 1.3 在master添加hosts&#xff08;依舊是三臺一起&#xff09; 1.4 調整內核參數并開啟網橋模式 二、部署docker引擎 三、部署 etcd 集群 1.在mast…

15.JUC原子類

文章目錄 JUC原子類1.JUC中的Atomic原子操作包1.1. 基本原子類&#xff08;Basic Atomic Classes&#xff09;1.2. 數組原子類&#xff08;Array Atomic Classes&#xff09;1.3. 引用原子類&#xff08;Reference Atomic Classes&#xff09;4. 字段更新原子類&#xff08;Fie…

StackQueue+泛型簡單理解

&#x1f341; 個人主頁&#xff1a;愛編程的Tom&#x1f4ab; 本篇博文收錄專欄&#xff1a;Java專欄&#x1f449; 目前其它專欄&#xff1a;c系列小游戲 c語言系列--萬物的開始_ &#x1f389; 歡迎 &#x1f44d;點贊?評論?收藏&#x1f496;三連支持一…

ddpm Denoising Diffusion Probabilistic Model 學習筆記

目錄 Stable Diffusion 文章的貢獻抽象出來就兩個 潛空間上做擴散生成 ddpm(Denoising Diffusion Probabilistic Model)學習筆記 算法原理 unet預測噪聲 unet推理過程 重參數化技巧 &#xff08;1&#xff09;利用前一時刻的 xt-1 得到任意時刻的噪聲圖片 xt&#xff…

LeetCode2215找出兩數組的不同

題目描述 給你兩個下標從 0 開始的整數數組 nums1 和 nums2 &#xff0c;請你返回一個長度為 2 的列表 answer &#xff0c;其中&#xff1a;answer[0] 是 nums1 中所有 不 存在于 nums2 中的 不同 整數組成的列表。answer[1] 是 nums2 中所有 不 存在于 nums1 中的 不同 整數組…