CuPyを使った画像の幾何学変換with OpenCV

はじめに

魚眼カメラで撮影した等距離射影方式の画像を普通のカメラの中心射影方式のへの変換をするなどの画像を幾何学的変換することがあります僕は研究で中央付近を大きく外側を小さくする変換をするために使いました

ここでは等距離射影方式の画像を中心射影方式の画像に変換する場合を取り上げ紹介していきます

なぜCuPyを使うのか

CuPyNumPyと互換性を持つNVIDIAGPUで動作することのできるライブラリです幾何学変換は大量の画素を移動させるので安直なループで実装すると処理時間が長いですなのでGPUを使って並列化しようということです

実装

作成したコード等はここにあります

import

今回は画像を扱うライブラリにOpenCVを使いますimportするのはこの2つだけです

1
2
import cv2
import cupy as cp

ElementwiseKernel

CuPyで並列化を行うにはElementwiseKernelで簡単に実装できます詳しくはCuPyElementwiseKernelで楽にGPUの恩恵を受けるで詳しく説明されています

入力を変換前の画像img1とそのサイズsize1と変換後のサイズsize2出力を変換後の画像img2としていますここでは簡単にするため入出力する画像は正方形を前提にします

5–10行目では変換前後の画素の関係を計算しています計算式については説明を省きます

11–13行目で変換後の画像に画素情報を格納しています入力画像はRGB3色あるのでこうなっています変換後のi1には変換前のi2, j2にある画素を格納することになります

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
trans_kernel = cp.ElementwiseKernel(
    in_params='raw uint8 img1, int16 size1, int16 size2',
    out_params='raw uint8 img2',
    operation='''
        float x = (i % size2) - (size2 / 2.0) + 0.5;
        float y = (i / size2) - (size2 / 2.0) + 0.5;
        float ang1 = sqrt(float(x * x + y * y));
        float ang2 = 800 * atan(ang1 / 784); // Magic number: 800, 784
        int j2 = (ang2 * x / ang1) + (size1 / 2);
        int i2 = (ang2 * y / ang1) + (size1 / 2);
        img2[i * 3 + 0] = img1[(i2 * size1 + j2)*3 + 0];
        img2[i * 3 + 1] = img1[(i2 * size1 + j2)*3 + 1];
        img2[i * 3 + 2] = img1[(i2 * size1 + j2)*3 + 2];
    ''',
    name='trans_kernel'
)

入出力

12行目ではimreadで読み込んだ画像img1CuPyの配列に変換しています3行目では変換後の画像を格納するための空の画像img2_cpを生成しています

5行目ではtrans_kernelを呼び出しGPUを使って並列処理をして変換します

78行目でimg2_cpOpenCVで扱えるNumPy形式に変換し出力します

1
2
3
4
5
6
7
8
img1 = cv2.imread('img1.png')
img1_cp = cp.asarray(img1).astype(cp.uint8)
img2_cp = cp.zeros((1920, 1920, 3)).astype(cp.uint8)

trans_kernel(img1_cp, img1.shape[1], 1920, img2_cp, size=(1920 * 1920))

img2 = cp.asnumpy(img2_cp)
cv2.imwrite('img2.png', img2)

結果

結果は次のようになりました

魚眼画像 補正後

ひとこと

この記事は僕が研究で使った内容の一部でもありますがどちらかと言ったら後輩のために書いたメモですQiitaに投稿したら僕の中で一番LGTMが多くなったんですよねやっぱり変人が好む物を書くより一般的でメジャーな物の方がいいんですね

文献

  1. geometric-transformation (GitHub)
  2. CuPyElementwiseKernelで楽にGPUの恩恵を受ける(Qiita)
  3. CuPyを使った画像の幾何学変換with OpenCV (Qiita)

  1. i2次元を1次元に変えた状態の座標なので2次元で表すとi/size2, i%size2となります ↩︎