Python版 OpenCVの基本

PythonでOpenCVを使うには

import cv2
import numpy as np

PythonとOpenCVのインストールの方法はこちらです。インストールが終わったなら早速始めてみましょう。OpenCVを使うために、OpenCVのモジュールをインポートします。”import opencv”ではないので注意してください。関数を使うときもcv2.resize()というようにして使います。numpyも画像を行列として保持するために必要なので一緒にインポートしておきます。as npとすることで、np.array()というように短く書くことができるようになります。

画像とは何か

OpenCVで読み込んだ画像はNumPyの配列として保持されます。縦N×横Mピクセルの画像で、グレースケールの画像であれば(すなわち1チャンネルの画像であれば)、N×Mの行列として保持され、カラー画像であれば、BGRの3チャンネルなのでN×M×3の行列として保持されます。下は、縦横200*300の画像をimgという行列として読み込み、その行列の形状をimg.shapeとすることで取得し、表示するコードです。

画像を扱う上で重要なのが、左上端が原点(0,0)となることです。右に行くに従ってx座標が増えていくのは普通に感じると思いますが、下に行くに従ってy座標が増えていくのには違和感を覚えるかもしれません。しかし、これは画像処理ライブラリ全般によく見られる仕様です。

import cv2
import numpy as np

img = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_COLOR)
print(img.shape)
(200, 300, 3)

img.shapeで取得できる形状は(縦、横、チャンネル数)の順となることに注意してください。画素にアクセスするときにも、img[x,y]ではなく、img[y,x]となります。

画像を読み込む

import cv2
import numpy as np

img = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_COLOR)
print("IMREAD_COLOR:", img.shape)

img = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_GRAYSCALE)
print("IMREAD_GRAYSCALE:", img.shape)
IMREAD_COLOR: (200, 300, 3)
IMREAD_GRAYSCALE: (200, 300)

すでに出てきていますが、cv2.imread関数を使います。画像とは何か、のセクションではIMREAD_COLORという定数を指定していましたが、IMREAD_GRAYSCALEを指定すると、グレースケールで読み込めます。このとき、画像を格納する行列の形状を出力すると、(縦、横)となり、チャンネル数に相当する軸は表示されません。

画像を表示する

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

img = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_COLOR)
plt.imshow(img)
plt.show()

まず、matplotlib.pyplotをpltとして読み込んでいます。imgに画像を読み込んだ後に、plt.imshow() plt.show()とすることで、画像を表示できます。しかし、青い画像を読み込んだはずなのに、赤い画像が表示されています。これは、OpenCVがBGRの順で画像を読み込むのに対し、matplotlibはRGBの順で画像を解釈しようとするためです。これを解決すつためにはcv2.cvtColor関数を使います。img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)によってBGRからRGBに変換します。2はto(〜へ)の意味です。

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

imgBGR = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_COLOR)
imgRGB = cv2.cvtColor(imgBGR, cv2.COLOR_BGR2RGB)
plt.imshow(imgRGB)
plt.show()

画像を保存する

import cv2
import numpy as np

img = cv2.imread("/Users/ym/Desktop/sample.png", cv2.IMREAD_COLOR)
cv2.imwrite("/Users/ym/Desktop/output.png", img)

cv2,imwrite関数で保存します。引数には保存するファイル名のパスと、保存したい画像が格納されているnumpy配列を指定します。画像の拡張子に使えるのは以下の通りです。

  • Windows bitmaps – *.bmp, *.dib (always supported)
  • JPEG files – *.jpeg, *.jpg, *.jpe
  • JPEG 2000 files – *.jp2
  • Portable Network Graphics – *.png
  • Portable image format – *.pbm, *.pgm, *.ppm (always supported)
  • Sun rasters – *.sr, *.ras (always supported)
  • TIFF files – *.tiff, *.tif

これらの拡張子をつける限り、自動的にその形式で保存されます。ちなみに、imread関数で読み込めるのもこれらの形式です。

画像をグレースケール化する

これ以降は次の画像を用いることにします。

グレースケール化する前に、まずmatplotlibでBGRをRGBにしてから表示します。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_COLOR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.show()

次にグレースケールにしてから表示してみます。または、はじめからIMREAD_GRAYSCALEを指定することによってグレースケールで読み込みます。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_COLOR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(img)
plt.show()

 

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_GRAYSCALE)
plt.imshow(img)
plt.show()

予想していた灰色の画像とは違っていたかもしれません。実はmatplotlibはグレースケール画像は自動的に疑似カラーで表示します。ひとまず、カラーバーを表示してみましょう。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_GRAYSCALE)
plt.imshow(img)
plt.colorbar()
plt.show()

次に、疑似カラーではないグレースケール画像そのものを出力したいとします。そのときにはimshowのオプションとしてcmap=”gray”を指定します。cmapはカラーマップの意味です。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_GRAYSCALE)
plt.imshow(img, cmap="gray")
plt.colorbar()
plt.show()

カラーマップ

グレーの画像が表示できました。ちなみに、私がよく使うカラーマップは “gray” “jet” “hsv” です。jetもhsvも、きれいなグラデーションですが、hsvは最大値と最小値が同じ赤色となります。したがって、角度など最大値と最小値が同じ意味を持つ場合にhsvを使っています(0度と360度は同じ意味を持つということです)。

画像をリサイズする

cv2.resize関数でリサイズを行います。注意が必要なのはresize関数の中で指定する各軸の長さの順序が(x,y)の順になっていることです。shapeで取得される順とは異なるので注意が必要です。横100 縦200にリサイズする場合には次のようにします。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (100,200))
cv2.imwrite("/User/ym/Desktop/resized.png", img)

縦横半分にリサイズする場合には次のようにします。resize関数の中で指定する各軸の長さの順序とshapeで取得される順が異なるので注意が必要です。

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

img = cv2.imread("/Users/ym/Desktop/green.jpg", cv2.IMREAD_COLOR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# img.shape => (300,200)
w = img.shape[1]
h = img.shape[0]
img = cv2.resize(img, (w//2,h//2))
plt.imshow(img, cmap="jet")
plt.colorbar()
plt.show()

動画の処理

cv2.VideoCaptureで動画ファイルを再生できます。動画ファイルはパラパラ漫画のようなもので、単なる静止画像が連続して格納されただけのものです。cv2.VideoCaptureは動画ファイルから静止画像を1枚ずつ取り出してくれます。cap.read()とすることで、次のフレームを読み込みます。

import cv2
import numpy as np

# 動画ファイルの指定
cap = cv2.VideoCapture("video.mp4")

# 動画終了まで繰り返し
while(cap.isOpened()):
    # フレームの取得
    ret, frame = cap.read()
    # 表示
    cv2.imshow("image", frame)
    # qが押されたら終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
        
cap.release()
cv2.destroyAllWindows()