はじめに
魚眼カメラで撮影した等距離射影方式の画像を,普通のカメラの中心射影方式のへの変換をするなどの,画像を幾何学的変換することがあります。僕は研究で,中央付近を大きく,外側を小さくする変換をするために使いました。
ここでは,等距離射影方式の画像を中心射影方式の画像に変換する場合を取り上げ,紹介していきます。
なぜ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の色あるのでこうなっています。変換後の 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)
結果
結果は次のようになりました。
| 魚眼画像 | 補正後 |
|---|---|
![]() |
![]() |
文献
-
iは2次元を1次元に変えた状態の座標なので,2次元で表すとi/size2, i%size2となります。 ↩

