産業用UVCカメラのすすめ 産業用UVCカメラのすすめ

OpenCV+CUDA

JetPack4.40にデフォルトで入っているOpenCVはCUDA対応でビルドされていないので、PythonやC++でOpenCVのCUDA関数を利用するにはGPU(CUDA)に対応したOpenCVをインストールし直す必要があります。ここではOpenCVでGPU(CUDA)を利用するための手順について説明します。

ビルドで高速処理するためにJetson Nanoのパフォーマンスを最大化します。

sudo nvpmodel -m 0
sudo jetson_clocks

4.3.0のビルドではメモリ領域をかなり使用するため、通常のビルド方法では途中でメモリ領域が確保できずにビルドエラーになります。したがって、6GB程度、スワップ領域を一時的に確保します。

sudo dd if=/dev/zero of=/swapfile bs=1M count=6144
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

下記のNVIDIAのフォーラムに OpenCVを全自動でビルドしてインストールするスクリプトを使用します。
install_opencv4.3.0_Jetson.shを実行することでopencv 4.3.0だけでなく依存パッケージもインストールすることができます。

cd
wget https://raw.githubusercontent.com/AastaNV/JEP/master/script/install_opencv4.3.0_Jetson.sh
chmod 755 install_opencv4.3.0_Jetson.sh
sudo ./install_opencv4.3.0_Jetson.sh opencv430

以上でCUDAに対応したOpenCVのインストールが完了します。

PythonにてOpenCV+CUDAを使用した例は下記の通りです。

def gstreamer_pipeline(
    capture_width=2592,
    capture_height=1944,
    display_width=1920,
    display_height=1080,
    framerate=30,
    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,
        )
    )

GStreamerで画像をストリームで受けとり、OpenCVのVideoCaptureメソッドで処理するために、下記の関数gstreamer_pipelineを定義します。

ここでは、2592×1944@30fpsの解像度のカラーカメラをフルHD(1920×1080)のディスプレイで表示するために下記のように定義しています。

def camera_capture():
 
   #gstreamerのパイプラインを利用してカメラデバイスオープン
   # gstreamer_pipelineの関数を呼び出しパイプラインをセット
    cap = cv2.VideoCapture(gstreamer_pipeline(), cv2.CAP_GSTREAMER)
    if cap.isOpened():
        cv2.namedWindow("Camera GPUTest", cv2.WINDOW_AUTOSIZE)
      #GPUのリソースを利用するためにデバイスメモリ確保
        img_gpu_src = cv2.cuda_GpuMat() 
        img_gpu_dst = cv2.cuda_GpuMat()
 
        while cv2.getWindowProperty("Camera GPUTest", 0) >= 0:
            
            ret, img = cap.read()
         #ホスト->デバイスへのメモリ転送(upload )
            img_gpu_src.upload(img)
         #GPUでカラー画像をモノクロ画像に変換
            img_gpu_dst = cv2.cuda.cvtColor(img_gpu_src, cv2.COLOR_BGR2GRAY)
         #デバイス->ホストへのメモリ転送(download )
            img_dst = img_gpu_dst.download()
            #GPUで処理した画像を表示
         cv2.imshow("Camera GPUTest", img_dst)

            keyCode = cv2.waitKey(30) & 0xFF
            #Stop processing at ESC key
            if keyCode == 27:
                break
 
        cap.release()
        cv2.destroyAllWindows()
    else:
        print("can’t open camera!")

CPUとGPUの記憶領域(メモリ)が物理的に独立であるためCPUとGPUの間のデータ転送が必要になります。そのためCUDAプログラミングでは、CPUからGPUにメモリ転送するためデバイス(GPU)側メモリの確保と、ホスト->デバイスへのメモリ転送(upload)、デバイス->ホストへのメモリ転送(download)の一連の処理が必要となります。画像処理のGPU(CUDA)関数は、基本的にcv2 の後ろにcuda が付くだけでGPUのバッググラウンドの処理を気にしなくても簡単に並列処理を記載できます。

ただし、CPUとGPU間のデータ転送が遅いため、GPUを使った高速処理にはCPUからGPU間のデータ転送を最小化する必要があります。