10
7

More than 1 year has passed since last update.

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

Last updated at Posted at 2021-12-01

はじめに

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

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

なぜ CuPy を使うのか

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

実装

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

import

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

import cv2
import cupy as cp

ElementwiseKernel

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

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

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

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

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'
)

入出力

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

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

7,8 行目で img2_cp を OpenCV で扱える NumPy 形式に変換し,出力します。

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)

結果

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

魚眼画像 補正後
img1 img2

文献

  1. geometric-transformation (GitHub)
  2. CuPyのElementwiseKernelで楽にGPUの恩恵を受ける (Qiita)

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

10
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
7