numpyとOpenCVの座標定義

OpenCVを使って画像操作をする際にいつも混乱するので、改めて整理しました。

画像をOpenCVで読み込んで、切り出す時、矩形を描く時、xとyの座標指定の順番がわからなくなることが私は非常によくあります。
x方向に並ぶようにパッチを作ったつもりなのに、なぜか(なぜも何もないのですが)y方向にパッチを切り出していたり。

出力したあとで、「あー、また逆だ」とならないためにここできっちり理解しておくことにします。

画像の読み込み

まずはいつものように画像を読み込んでおきます。
以降のコードはこの状態から動くようになっています。

import numpy as np
import os
import matplotlib.pyplot as plt
import cv2

# 画像の読み込み
img = cv2.imread('IMG004.JPG')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

画像サイズと画像イメージは下の通りです。
横長の画像ですね。

# 画像サイズの確認
print(img.shape) # (3264, 4928, 3)

# 画像表示
plt.imshow(img)
plt.show()
(3264, 4928, 3)

numpyでスライス

では、numpyで矩形領域を切り出してみます。

# 一部を切り出して表示
plt.imshow(img[500:1500,2000:3500,:])
plt.show()

画像の中央上あたりを切り出しています。
y, xの順で座標を定義していることがわかります。

OpenCVで矩形表示

次に、OpenCVで同じ場所を矩形で示してみます。

# 矩形を描画
cv2.rectangle(img, (2000, 500), (3500, 1500), (255, 0, 0), thickness=20)

cv2.rectangle()では、左上座標、右下座標をタプルで指定する必要があります。
ここでは、x, yの順で座標を定義しています。

OpenCVのresize()も試してみましたが座標定義は同じく(x, y)ですね。
リサイズ後のnumpyのサイズは (row, col) で扱われますので、ちょっと混乱します。

# リサイズ
img_resize = cv2.resize(img, (500, 1000))
print(img_resize.shape) # (1000, 500, 3)

結論

多分なのですが、numpyは行列 (row, column) で配列を定義し、OpenCVはx, yで定義しているためにこのような挙動になるのでしょう。
私は画像を扱うことが多くて、(x, y) の順での指定に慣れてしまっていたため、混乱してしまったのですが、思想を理解して使えば問題ないと思います。

(おまけ) パッチ画像を作る

最後に、Deep Learningのタイトルらしく、500ピクセルずつに分割したパッチ画像を作ってみました。

# 500x500で画像を切り取って出力
img_size = 500

# 最終的に出力される画像の枚数を確認
num_x = img.shape[1]//img_size
num_y = img.shape[0]//img_size

# 切り出しパッチ数分のsubplotからなるfigureを作成
fig, axes = plt.subplots(nrows=num_y, ncols=num_x)

# x方向で順に切り出し
for y in range(num_y):
    for x in range(num_x):

        # 画像切り出し
        img_area = img[img_size*y:min(img_size*(y+1), img.shape[0]), 
                       img_size*x:min(img_size*(x+1), img.shape[1]),
                       :]

        # 切り出した画像をfigureに配置
        axes[y,x].imshow(img_area)
        axes[y,x].set_title('{}_{}'.format(y,x), fontsize=8)
        axes[y,x].axis('off')

plt.show()

全部並べてみるとこんな感じです。
matplotlibも行列で扱っていますし、OpenCVの座標定義だけがちょっと特殊、という認識でいたほうがいいのかもしれません。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です