Python, OpenCVでBGRとRGBを変換するcvtColor
OpenCVの関数imread()で画像ファイルを読み込むと色の順番がBGR(青、緑、赤)になる。一方、Pillowでは色の順番はRGB(赤、緑、青)を前提としている。
そのため、Pillowの関数とOpenCVの関数を両方使いたい場合はBGRとRGBを変換する必要がある。
OpenCVの関数cvtColor()を使う方法と、単純にndarrayの順番を入れ替える方法がある。
ここでは、以下の内容について説明する。
- OpenCVはBGR、PillowはRGB
- OpenCVの関数
cvtColor()でBGRとRGBを変換 cvtColor()を使わずにBGRとRGBを変換
OpenCVはBGR、PillowはRGB
カラー画像を読み込む場合、OpenCVのimread()では行(高さ) x 列(幅) x 色(3)のNumPy配列ndarrayとして読み込まれる。色の順番はBGR(青、緑、赤)。
画像を保存するOpenCVの関数imwrite()はBGRの順番を前提としているので、そのまま使うと正しい画像として保存される。
import cv2
import numpy as np
from PIL import Image
im_cv = cv2.imread('data/src/lena.jpg')
cv2.imwrite('data/dst/lena_bgr_cv.jpg', im_cv)

Pillowで画像処理を行う場合、Image.fromarray()でndarrayをPIL.Imageオブジェクトに変換できるが、Pillowでは色の順番はRGB(赤、緑、青)を前提としている。
そのため、OpenCVのimread()で読み込んだ画像のndarrayをそのままPIL.Imageオブジェクトに変換して保存すると、色が誤った画像として保存されてしまう。
pil_img = Image.fromarray(im_cv)
pil_img.save('data/dst/lena_bgr_pillow.jpg')

ndarrayとPIL.Imageオブジェクトを相互に変換してPillowの関数とOpenCVの関数を両方使いたい場合は、以下に示す方法でBGRとRGBを変換する必要がある。
OpenCVの関数cvtColor()でBGRとRGBを変換
OpenCVの関数cvtColor()を使うとRGBやBGR、HSVなど様々な色空間を相互に変換できる。
dst = cv2.cvtColor(src, code)
引数codeに指定する値については以下のドキュメントを参照。
引数codeをcv2.COLOR_BGR2RGBとすると、その名前の通りBGRからRGBへの変換となる。
RGBに変換すると、PIL.Imageオブジェクトに変換し保存しても正しい画像として保存される。
im_rgb = cv2.cvtColor(im_cv, cv2.COLOR_BGR2RGB)
Image.fromarray(im_rgb).save('data/dst/lena_rgb_pillow.jpg')

RGB変換後にOpenCVのimwrite()で保存すると、当然、誤った色の画像となる。
cv2.imwrite('data/dst/lena_rgb_cv.jpg', im_rgb)

なお、RGBからBGRへ変換する場合は、引数codeをcv2.COLOR_RGB2BGRとする。PIL.Imageオブジェクトで読み込んでndarrayに変換後、OpenCVのimwrite()で保存する場合はこちらを使う。
im_pillow = np.array(Image.open('data/src/lena.jpg'))
im_bgr = cv2.cvtColor(im_pillow, cv2.COLOR_RGB2BGR)
cv2.imwrite('data/dst/lena_bgr_cv_2.jpg', im_bgr)
cvtColor()はカラー画像をグレースケールに変換するのにも利用できる。
cvtColor()を使わずにBGRとRGBを変換
BGRとRGBを変換するだけであればcvtColor()を使わなくても、NumPyの基本機能で実現できる。
方法はいくつかあるが、例えば以下のようにできる。2通り。
im_bgr = cv2.imread('data/src/lena.jpg')
im_rgb = im_bgr[:, :, [2, 1, 0]]
Image.fromarray(im_rgb).save('data/dst/lena_swap.jpg')
im_rgb = im_bgr[:, :, ::-1]
Image.fromarray(im_rgb).save('data/dst/lena_swap_2.jpg')
前者はファンシーインデックス、後者はスライスを利用している。