OpenCV

【Python】OpenCVによる画像入出力の基本 ~imread, imwrite~

【Python】OpenCVによる画像入出力の基本 ~imread, imwrite~

PythonでOpenCVを使って画像入出力する方法について解説します。

OpenCVによる画像入出力

OpenCVは、有名なコンピュータービジョンのライブラリです。OpenCVのPythonバインドであるcv2モジュールを使用することで、PythonでOpenCVの機能を使用することができます。

OpenCVでは、画像の入出力のための関数として、imread関数とimwrite関数を提供しています。これらの関数は様々なファイルフォーマット(BMP, PNG, JPEG, TIFF等)の画像をサポートしています。

この記事では、PythonでOpenCVを使って画像入出力する方法について紹介します。

cv2モジュールでの画像表現

OpenCVのPythonバインドであるcv2モジュールでは、画像はNumpyの配列(ndarray)を使って表現されています。NumPyは、Pythonで高速な数学的演算を可能にするライブラリで、特に大量のデータに対する効率的な操作と計算を提供します。

OpenCVにおいて、カラー画像は以下のようなデータ構造として表現されています。

OpenCVの画像表現

imgという画像配列を作成したとしましょう。この時、水平(列)方向をx、垂直(行)方向をy、カラーチャンネルをchannelとすると、各ピクセルにアクセスするための引数の位置としてはimg[y, x, channel]となります。例えば、img[0, 1, 2]とすると、0行、1列の赤(R)チャンネルのピクセル値を表します。

OpenCVでは、BGRというカラー順が標準的な構造となっています。RGBという順番の方が感覚的に分かりやすい人がいると思いますが、これはOpenCVが開発され始めた当初、BGRの色順が一般的なハードウェアで採用されていたなどの背景によるもので、互換性を保つためにBGRが標準となっています。ただし、必要に応じてcv2.cvtColor(image, cv2.COLOR_BGR2RGB)というようにRGB順に変換することも可能です。

実際のどのようなデータ構造になるか簡単な例で確認してみましょう。

import cv2
import numpy as np

# NumPyのndarrayを生成
img = np.ones((5, 5), dtype=np.uint8)

print(img)
print(type(img))
print(img.dtype)
print(img.shape, "\n")

# グレースケール → カラー画像へ変換
img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

print(img)
print(type(img))
print(img.dtype)
print(img.shape)
【実行結果】
[[1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]
 [1 1 1 1 1]]
<class 'numpy.ndarray'>
uint8
(5, 5) 

[[[1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]]

 [[1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]]

 [[1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]]

 [[1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]]

 [[1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]
  [1 1 1]]]
<class 'numpy.ndarray'>
uint8
(5, 5, 3)

上記サンプルでは、NumPyでグレースケール画像を想定して5×5画像配列をones関数を使用して作成しています。この配列は、NumPyのnumpy.ndarray型の配列です。dtypeで型を確認するとuint8となっています。また、配列形状はshapeを使って確認でき(5, 5)であることが確認できます。dtypeshapeは画像の情報を得るうえでよく使用するので覚えておきましょう。

このグレースケール画像をcv2.cvtColor関数を使用して、カラー画像に変換しています。shapeを見てみると分かりますが(5, 5, 3)です。最初に説明したように水平(列)方向をx、垂直(行)方向をy、カラーチャンネルをchannelとすると(y, x, channel)となっています。また、カラーチャンネルの順番はBGRであることに注意してください。

OpenCVでは、このように画像表現がされることをまずは理解しておいてください。以降では、imreadimwriteを使用した画像入出力の具体例を紹介します。

imread、imwriteによる画像の読み込み、書き込み

ここでは、imread関数及びimwrite関数を用いた画像の読み込みや書き込みについて紹介します。

基本的な使い方

imreadを使用して画像を読み込み、imwriteを使用して別ファイルとして書き込みを行ってみましょう。imreadimwriteは、以下のように使用します。

import cv2

# 画像ファイルを読み込む
img = cv2.imread("lena.jpg")

print(img)
print(type(img))
print(img.dtype)
print(img.shape)

# 画像ファイルを書き込む
cv2.imwrite("lena_output.png", img)
【実行結果】
[[[128 138 225]
  [127 137 224]
  [126 136 224]
  ...
  [126 145 236]
  [110 129 220]
  [ 86 104 197]]

 ...(途中省略)...

 [[ 56  19  81]
  [ 58  21  83]
  [ 68  34  98]
  ...
  [ 81  68 176]
  [ 81  72 183]
  [ 84  74 188]]]
<class 'numpy.ndarray'>
uint8
(512, 512, 3)

まず、OpenCVの機能を使用するためにcv2モジュールをインポートしておきます。

画像ファイルを読み込むには、cv2.imread("lena.jpg")という形で、JPG画像ファイルのパスを指定します。今回の例では、画像処理でよく出てくるレナの画像を使用していますが、皆さんのお好きな任意の画像を指定してください。imreadで指定するパスは、絶対パスや相対パスで指定できます。

取得した画像のdtypeshape等を確認して見てみるとuint8(512, 512, 3)のカラー画像となっていることが分かります。

上記の例では、読み込んだJPGファイルを別のPNGファイルへ保存しています。画像ファイルの書き込みの際には、cv2.imwrite("lena_output.png", img)という形で、出力ファイルパスと画像データ(img)を渡します。

imwriteは、BGRまたはグレースケールの画像である必要があり、出力に指定する画像フォーマットに適した値である必要があります。例えば、BMP画像であれば1チャンネル 8bitといった形です。

オプションを指定する

imreadを使用する際に、読み込みのオプション指定をすることが可能です。例えば、元ファイルがカラーである画像をグレースケールで読み込む場合は、以下のようにします。

import cv2

# 画像ファイルを読み込む
img = cv2.imread("lena.jpg", cv2.IMREAD_GRAYSCALE)

print(img)
print(type(img))
print(img.dtype)
print(img.shape)

# 画像ファイルを書き込む
cv2.imwrite("lena_gray_output.png", img)
【実行結果】
[[163 162 161 ... 170 154 130]
 [162 162 162 ... 173 155 126]
 [162 162 163 ... 170 155 128]
 ...
 [ 43  42  51 ... 103 101  99]
 [ 41  42  55 ... 103 105 106]
 [ 42  44  57 ... 102 106 109]]
<class 'numpy.ndarray'>
uint8
(512, 512)

上記のように、cv2.IMREAD_GRAYSCALEというオプション情報をファイルパスに続いて指定することでグレースケールで読み込むことができます。shapeで形状を見てみると(512, 512)となっておりグレースケールの画像となっていることが分かります。imwriteで出力した画像を見ていただければグレースケールに変換されていることが確認できます。

なお、デフォルトのオプションとしては、cv2.IMREAD_COLORとなっています。他にも指定できるオプションがいくつかありますが、それらのオプションについては、OpenCVのドキュメントにあるImreadModesの部分を参照してください。

画像をbytearrayに変換する

画像データを扱う際にバイト列に変換して扱うと便利なケースがあります。例えばデータをシリアライズしての保存したり、ネットワーク越しに送受信したりする場合等です。ここでは、画像として読み込んだデータをbytearrayに変換する方法を紹介します。

import cv2
import numpy as np

# 画像ファイルを読み込む
img = cv2.imread("lena.jpg")
img_shape = img.shape
img_dtype = img.dtype

# bytearrayに変換する
bgr_bytearray = bytearray(img.tobytes())
print(bgr_bytearray)
print(len(bgr_bytearray), "\n")

# bytearrayからnumpy.ndarrayに戻す
# bgr_img = np.array(bgr_bytearray, dtype=np.uint8).reshape(512, 512, 3)
bgr_img = np.array(bgr_bytearray, dtype=img_dtype).reshape(img_shape)
print(bgr_img)
print(bgr_img.shape)

# 画像ファイルを書き込む
cv2.imwrite("lena_output_byte.png", bgr_img)
【実行結果】
bytearray(b'\x80\x8a\xe1\x7f\x89\xe0~\x88\xe0}\x87
...(途中省略)...
\x18b=\x1ddC#jI,uK0zM4~N8\x86[E\x97S@\x97VE\xa0YJ\xacSF\xacQD\xb0QH\xb7TJ\xbc')
786432 

[[[128 138 225]
  [127 137 224]
  [126 136 224]
  ...
  [126 145 236]
  [110 129 220]
  [ 86 104 197]]

...(途中省略)...

 [[ 56  19  81]
  [ 58  21  83]
  [ 68  34  98]
  ...
  [ 81  68 176]
  [ 81  72 183]
  [ 84  74 188]]]
(512, 512, 3)

上記例では、読み込んだ画像をPythonのbytearrayへ変換し、その後に元に戻して画像出力する一連の流れを実行しています。

bytearrayへ変換する際には「bgr_bytearray = bytearray(img.tobytes())」というように読み込んだ画像(img)を渡しています。ここで渡す際にはtobytes()でバイト列にしてから渡しています。

bytearrayからNumPyのndarrayに戻す際には「bgr_img = np.array(bgr_bytearray, dtype=img_dtype).reshape(img_shape)」というようにnumpy.arrayを使用します。この際には、dtypeで適切なデータ型を渡すことが重要です。また、形状を戻すにはreshapeに形状を渡します。

今回の例では読み込み時に変数に情報を取得しておいて渡しましたが、コメントアウトしてある行のように「bgr_img = np.array(bgr_bytearray, dtype=np.uint8).reshape(512, 512, 3)」というように指定していただいても構いません。

以上のようにすることで、必要に応じて画像データをbytearrayに変換して扱うことも可能です。

まとめ

PythonでOpenCVを使って画像入出力する方法について解説しました。

OpenCVでは、画像の入出力のための関数として、imread関数とimwrite関数を提供しています。これらの関数は様々なファイルフォーマット(BMP, PNG, JPEG, TIFF等)の画像をサポートしています。

この記事では、OpenCVにおける画像表現の基本を最初に説明した後に、imreadimwriteの基本的な使い方を紹介しました。また、必要に応じてバイト列のbytearrayに変換する方法についても説明しています。

OpenCVは、非常に強力なコンピュータビジョンのライブラリで、画像の入出力は画像を扱うにあたっての基本となります。しっかりと使い方を理解していただきたいと思います。