Jetsonで画像処理をするならやっぱりGPUアクセラレーションを試さないと ということで、OpenCVからCUDAを使って基本画像処理をすることにした。
本記事では、Raspberry Pi Camera V2(imx219 camera module)から画像ストリームデータを取得し、それに対して画像処理を行う。
OpenCV4.3.0のインストール
僕が買ったJetson NanoにはJetPack 4.3がインストールされていて、OpenCVのバージョンは4.1.1になっている。4.1.1自体はCUDAに対応しているようだが、デフォルトで入っていたOpenCVはCUDA対応でビルドされていなかったらしい。
この記事を書いている時点での最新のOpenCVは4.3.0が最新なので、アップデートを兼ねてインストールすることにした。
インストールの際には下記の資料を参考にさせていただきました。
https://qiita.com/daisuzu_/items/8cc8de8ea8dc557a2aad
上記の記事では4.1.2だが、4.3.0に置き換えても同じことができる。
インストール時にめちゃくちゃメモリを食うのでスワップ領域の確保は必須。
下記に自分が使用したスクリプトを示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
#!/bin/bash # # Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. # # NVIDIA Corporation and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation # and any modifications thereto. Any use, reproduction, disclosure or # distribution of this software and related documentation without an express # license agreement from NVIDIA Corporation is strictly prohibited. # if [ "$#" -ne 1 ]; then echo "Usage: $0 <Install Folder>" exit fi folder="$1" user="nvidia" passwd="nvidia" echo "** Remove other OpenCV first" sudo sudo apt-get purge *libopencv* echo "** Install requirement" sudo apt-get update sudo apt-get install -y build-essential cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev sudo apt-get install -y libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev sudo apt-get install -y python2.7-dev python3.6-dev python-dev python-numpy python3-numpy sudo apt-get install -y libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libdc1394-22-dev sudo apt-get install -y libv4l-dev v4l-utils qv4l2 v4l2ucp sudo apt-get install -y curl sudo apt-get update echo "** Download opencv-4.3.0" cd $folder curl -L https://github.com/opencv/opencv/archive/4.3.0.zip -o opencv-4.3.0.zip curl -L https://github.com/opencv/opencv_contrib/archive/4.3.0.zip -o opencv_contrib-4.3.0.zip unzip opencv-4.3.0.zip unzip opencv_contrib-4.3.0.zip cd opencv-4.3.0/ echo "** Apply patch" sed -i 's/include <Eigen\/Core>/include <eigen3\/Eigen\/Core>/g' modules/core/include/opencv2/core/private.hpp sed -i 's/{CUDNN_INCLUDE_DIR}\/cudnn.h/{CUDNN_INCLUDE_DIR}\/cudnn_version.h/g' cmake/FindCUDNN.cmake echo "** Building..." mkdir release cd release/ cmake \ -D CMAKE_BUILD_TYPE=RELEASE \ -D OPENCV_GENERATE_PKGCONFIG=ON \ -D WITH_CUDA=ON \ -D WITH_CUDNN=ON \ -D CUDA_ARCH_BIN="5.3,6.2,7.2" \ -D CUDA_ARCH_PTX="" \ -D OPENCV_EXTRA_MODULES_PATH=../../opencv_contrib-4.3.0/modules \ -D WITH_GSTREAMER=ON \ -D WITH_LIBV4L=ON \ -D BUILD_opencv_cudaarithm=ON -D BUILD_opencv_cudabgsegm=ON -D BUILD_opencv_cudacodec=ON \ -D BUILD_opencv_cudafeatures2d=ON -D BUILD_opencv_cudafilters=ON -D BUILD_opencv_cudaimgproc=ON \ -D BUILD_opencv_cudalegacy=ON -D BUILD_opencv_cudaobjdetect=ON -D BUILD_opencv_cudaoptflow=ON \ -D BUILD_opencv_cudastereo=ON -D BUILD_opencv_cudawarping=ON -D BUILD_opencv_cudev=ON \ -D BUILD_opencv_python2=ON \ -D BUILD_opencv_python3=ON \ -D BUILD_TESTS=OFF \ -D BUILD_PERF_TESTS=OFF \ -D BUILD_EXAMPLES=OFF \ -D CMAKE_INSTALL_PREFIX=/usr/local .. make -j$(nproc) #sudo make install #echo 'export PYTHONPATH=$PYTHONPATH:'$PWD'/python_loader/' >> ~/.bashrc #source ~/.bashrc echo "** Install opencv-4.3.0 successfully" echo "** Bye :)" |
インストール作業は基本的に上記のスクリプトを走らせるだけ。
必要な周辺パッケージのインストール⇒opencvのダウンロード、zip解凍⇒ビルド
という手順を実行してくれる。
cmakeのオプションを一部変更する。
CUDAを使用するためにWITH_CUDAオプションをON。(-D WITH_CUDA=ON
)
あと、自分の場合はYOLOを使用するためにpkg-configを使えるようにしておいた。(-D OPENCV_GENERATE_PKGCONFIG=ON
)
pythonからOpenCV+CUDAを使ってみる
準備ができたところで、簡単なテストスクリプトを組んで見ることにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
import numpy as np import cv2 def gstreamer_pipeline( capture_width=3280, capture_height=2464, display_width=1920, display_height=1080, framerate=21, flip_method=0, ): return ( "nvarguscamerasrc ! " "video/x-raw(memory:NVMM), " "width=(int)%d, height=(int)%d, " "format=(string)NV12, framerate=(fraction)%d/1 ! " "nvvidconv flip-method=%d ! " "video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! " "videoconvert ! " "video/x-raw, format=(string)BGR ! appsink" % ( capture_width, capture_height, framerate, flip_method, display_width, display_height, ) ) def camera_capture(): USE_GPU = 1 cap = cv2.VideoCapture(gstreamer_pipeline(), cv2.CAP_GSTREAMER) if cap.isOpened(): cv2.namedWindow("Camera Test", cv2.WINDOW_AUTOSIZE) img_gpu_src = cv2.cuda_GpuMat() img_gpu_dst = cv2.cuda_GpuMat() while cv2.getWindowProperty("Camera Test", 0) >= 0: ##image processing WITH CUDA if USE_GPU == 1: ret, img = cap.read() img_gpu_src.upload(img) img_gpu_dst = cv2.cuda.cvtColor(img_gpu_src, cv2.COLOR_BGR2GRAY) img_dst = img_gpu_dst.download() #image processing WITHOUT CUDA else: ret, img = cap.read() img_dst = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) cv2.imshow("Camera Test", img_dst) keyCode = cv2.waitKey(30) & 0xFF #Stop processing at ESC key if keyCode == 27: break cap.release() cv2.destroyAllWindows() else: print("Couldn't open camera!") if __name__ == "__main__": camera_capture() |
cvtColor()
をCUDAで処理している。
このプログラムの中でやっていることはごく単純で
- Gstreamerでカメラの画像を連続的にキャプチャ
- imgにキャプチャ画像(カラー)を格納
- GPUでカラー画像をモノクロ画像に変換
- ウィンドウに表示
となっている。
CPU, GPU間のデータのやりとりや処理について、以下のように理解した。
①GPUのリソースを利用するためには、まずはGpuMat
によってアロケーションを行う必要がある。
②次に、CPU領域にある画像配列img
をGPUの領域へアップロードupload()
する。
③画像処理は、対象の関数cvtColor()
の直前に”cuda”をつけるだけ。
④最後に、GPUで扱っていた配列img_gpu_dst
をCPU領域へdownload()
によって戻してやる。
アロケーションの部分のオーバーヘッドが大きいようなので、上記のスクリプトだともしかしたらCPUより遅いかもしれない。
単にGPUを使えば何でも速くなるわけではなく、プログラムの順序や構造に工夫が必要。