Pythonで画像の色を分解、結合する方法。OpenCVとNumPyの使い方を解説

2021年6月19日

今日は読み込んだ画像の色を分解したり結合する方法を紹介します。

画像の色を分解、結合する方法

代表的な色分解の方法としては、OpenCV の cv2.split() 関数を使う方法とNumPy のスライシングとインデックス機能([ :, :, i ])を使う方法があります。

色結合する方法としては、 OpenCV の cv2.merge() 関数を使う方法とNumPy の np.concatenate() 関数を使う方法があります。

順に紹介していきます。

OpenCVを使って色分解

以下のリストを見てください。

import cv2
import numpy as np
img = cv2.imread('./test_photo/hana/dora_1.jpg')  # 画像読み込み
img_bgr = cv2.split(img)  # 色分解
cv2.imshow('img_0', img_bgr[0])
cv2.imshow('img_1', img_bgr[1])
cv2.imshow('img_2', img_bgr[2])
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.imread() 関数で画像を読み込むと色の順番は「BGR」になります。

読み込んだ画像は img に代入されます。

img を cv2.split() 関数を使って「B」「G」「R」に色分解します。

img_bgr の 0番目(img_bgr[0] )で青(Blue)の成分を抽出できます。

同様に img_bgr[1] で緑(Green)の成分、img_bgr[2] で赤(Red)の成分を抽出できます。

プログラムを実行すると以下の画像が表示されます。

左から青成分、緑成分、赤成分を抽出した画像です。

一見白黒画像のように見えますが、冒頭のドラえもんの写真を思い出してください。

一番左の画像は、青成分が多い顔の輪郭と体の部分は明るくなっています。

一方、一番右の画像は赤成分が多い鼻と首輪の部分は明るくなっています。

つまり、各色成分の濃淡画像となっています。

次にこれらの画像を結合してみます。

OpenCVを使って色結合

以下をリストを見てください。

import cv2
import numpy as np
img = cv2.imread('./test_photo/hana/dora_1.jpg')  # 画像読み込み
img_bgr = cv2.split(img)  # 色分解
cv2.imshow('img_0', img_bgr[0])
cv2.imshow('img_1', img_bgr[1])
cv2.imshow('img_2', img_bgr[2])
img_bgr = cv2.merge((img_bgr[0], img_bgr[1], img_bgr[2]))  # bgr順に色結合
cv2.imshow('img_x', img_bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

色の結合には cv2.merge() 関数を使います。

色結合時の順番は、B(青)、G(緑)、R(赤)の順です。

これで元の色の画像が表示されます。

ここで結合させる色の順番を変えるとどうなるか見てみます。

結合させる色の順番を変えるとどうなる?

cv2.merge() の中の img_bgr[0] と img_bgr[1] と img_bgr[2] の順番を入れ替えるだけです。

以下のリストを見てください。

import cv2
import numpy as np
img = cv2.imread('./test_photo/hana/dora_1.jpg')  # 画像読み込み
img_bgr = cv2.split(img)  # 色分解
cv2.imshow('img_0', img_bgr[0])
cv2.imshow('img_1', img_bgr[1])
cv2.imshow('img_2', img_bgr[2])
img_rgb = cv2.merge((img_bgr[2], img_bgr[1], img_bgr[0]))  # rgb順に色結合
img_gbr = cv2.merge((img_bgr[1], img_bgr[0], img_bgr[2]))  # gbr順に色結合
img_grb = cv2.merge((img_bgr[1], img_bgr[2], img_bgr[0]))  # grb順に色結合
cv2.imshow('img_rgb', img_rgb)
cv2.imshow('img_gbr', img_gbr)
cv2.imshow('img_grb', img_grb)
cv2.waitKey(0)
cv2.destroyAllWindows()

3種類の色違いドラえもんができました。

左から「RGB」、「GBR」、「GRB」の色順で結合させたドラえもんです。

一番左の画像は、鼻を見ると分かるように、青と赤を入れ替えた画像になっています。

真ん中の画像は顔の輪郭を見ると分かるように、緑と青を入れ替えた画像になっています。

一番右の画像は、赤と緑、青と赤を入れ替えた画像になっています。

NumPyを使って色分解

以下をリストを見てください。

import cv2
import numpy as np
img = cv2.imread('./test_photo/hana/dora_1.jpg')  # 画像読み込み
img_bgr = [img[:,:,i] for i in range(3)]  # 色分解
cv2.imshow('img_0', img_bgr[0])
cv2.imshow('img_1', img_bgr[1])
cv2.imshow('img_2', img_bgr[2])
cv2.waitKey(0)
cv2.destroyAllWindows()

先ほどの cv2.split() 関数を img[ : , : , i ] として色成分ごとに分解します。

これは、NumPyのスライシングインデックス機能を利用する方法です。

img[ : , : , 0 ] は Blue(青)成分

img[ : , : , 1 ] は Green(緑)成分

img[ : , : , 2 ] は Red(赤)成分

を表します。

NumPy の表記の仕方はご存じの方も多いと思いますが一応記載しておきます。

[ : ] は全部の要素を指定します。

[ : , : , 0] は、画像の 縦方向と横方向の全要素(全ピクセル)に対して色成分が青のものという意味です。

なお for ループを使わずに内包表記にしているのは処理速度を早くするためです。

なお、色分解した画像は OpenCV の所で示した画像と同じ濃淡画像ができます。

NumPyを使って色結合

以下のリストを見てください。

import cv2
import numpy as np
img = cv2.imread('./test_photo/hana/dora_1.jpg')  # 画像読み込み
img_bgr = [img[:,:,i] for i in range(3)]  # 色分解
cv2.imshow('img_0', img_bgr[0])
cv2.imshow('img_1', img_bgr[1])
cv2.imshow('img_2', img_bgr[2])
img_b0 = np.array(img_bgr[0])  # ndarray 型に変換
img_g0 = np.array(img_bgr[1])  # ndarray 型に変換
img_r0 = np.array(img_bgr[2])  # ndarray 型に変換
img_b = img_b0.reshape(img_b0.shape[0],img_b0.shape[1],1)
img_g = img_g0.reshape(img_g0.shape[0],img_g0.shape[1],1)
img_r = img_r0.reshape(img_r0.shape[0],img_r0.shape[1],1)
img_bgr = np.concatenate((img_b, img_g, img_r), axis=2)  # bgr順に色結合
img_rgb = np.concatenate((img_r, img_g, img_b), axis=2)  # rgb順に色結合
img_gbr = np.concatenate((img_g, img_b, img_r), axis=2)  # gbr順に色結合
img_grb = np.concatenate((img_g, img_r, img_b), axis=2)  # grb順に色結合
cv.imshow('img_bgr', img_bgr)
cv.imshow('img_rgb', img_rgb)
cv.imshow('img_gbr', img_gbr)
cv.imshow('img_grb', img_grb)
cv2.waitKey(0)
cv2.destroyAllWindows()

cv2.merge() 関数の方がシンプルですが、一応 NumPy でもできます。

NumPy では、np.concatenate() 関数を使います。

これは、配列を深さ方向に結合する関数です。

色分解すると img_bgr[ ] は2次元配列になります。

元の画像は色情報を持った3次元配列なので、2次元配列のままでは元のカラー画像が再現できません。

そこで、img_bgr[ ] を np.array() 関数で一旦 ndarray 型に変換します。

その後 reshape() 関数で3次元配列に変換します。

reshape() のカッコの中の第三引数に1を入れることで1次元増やし3次元配列とします。

次に、np.concatenate() 関数の axis = 2 とすることで深さ方向の結合を行います。

例えば、np.concatenate((img_b, img_g, img_r), axis=2) の意味は、色順を img_b、img_g、img_r の順番として、深さ方向に各画像を結合しなさいということです。

これを実行すると以下の画像が表示されます。

左から「BGR」、「RGB」、「GBR」、「GRB」の色順に結合させたドラえもんになります。

処理速度が速いのはどっち?

色分解は OpenCV でも NumPy でもどちらでもできますが、処理速度が速いのはどっちでしょうか?

結論から言うと、NumPy を使った方が 2%くらい速いです。

NumPy 強し!

なお実行環境によっても処理速度は若干変わると思いますが、大量の画像を処理する場合はやはり NumPy を使った方が良いと思います。

まとめ

  • OpenCV で色分解するなら cv2.split() 関数を使う。
  • OpenCV で色結合するなら cv2.merge() 関数を使う。
  • NumPy で色分解するならスライシングとインデックス機能( [ :, :, i ] )を使う。
  • NumPy で色結合するなら np.concatenate() 関数を使う。
  • 処理速度は NumPy の方が 若干(2%程度)速い