【Python/C++】画像をグレースケール変換する方法【OpenCV】

グレースケール変換

画像処理ではカラー画像をグレースケール画像に変換してから処理することがよくあります。
OpenCVでは、cvtColor() を使用することで簡単にグレースケール変換できます。

この記事では、OpenCVを使用して画像をグレースケールに変換する方法を紹介します。

1. グレースケール変換とは

カラー画像では、1画素ごとに複数の色成分を持っています。
OpenCVで一般的に扱うカラー画像はBGR形式で、

B:Blue
G:Green
R:Red

の3チャンネルで構成されています。

一方、グレースケール画像は明るさを表す1チャンネルだけで構成されます。
8bit画像の場合、画素値は通常、

0 ~ 255

の範囲になります。

0   :黒
255 :白

となり、その間の値は灰色を表します。

2. グレースケール変換の計算

カラー画像をグレースケールに変換するとき、単純にB、G、Rの平均値を使用するわけではありません。
OpenCVのBGRからグレースケールへの変換では、各色成分に重みを付けて明るさを計算します。
概念的には次のような式になります。

Gray=0.114B+0.587G+0.299R\mathrm{Gray}=0.114B+0.587G+0.299R

人間の目は緑色に対する感度が高く、青色に対する感度が低いため、それぞれ異なる係数が使用されています。

この変換によって、カラー画像を明るさ情報だけの画像へ変換できます。

3. Pythonによるグレースケール変換

OpenCVをインストールしていない場合は、次のコマンドでインストールします。

pip install opencv-python

3.1. cvtColorでグレースケール変換する

例としてこの画像を読み込み、グレースケール変換を実行します。

カラー画像を読み込んだ後にグレースケールへ変換する場合は、cv2.cvtColor() を使用します。

import cv2

# カラー画像を読み込む
image = cv2.imread("shimons_labo.png")

# グレースケール変換
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# 保存
cv2.imwrite("shimons_labo_gray.png", gray)

変換部分は次のコードです。

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

cv2.COLOR_BGR2GRAY を指定することで、BGR画像からグレースケール画像へ変換します。

上記のコードを実行するとこのような画像が保存されます。

3.2. imreadで直接グレースケールとして読み込む

画像を最初からグレースケールとして使用する場合は、cv2.imread() の第2引数に cv2.IMREAD_GRAYSCALE を指定できます。

import cv2

gray = cv2.imread("shimons_labo.png", cv2.IMREAD_GRAYSCALE)

cv2.imwrite("shimons_labo_gray.png", gray)

この方法では、読み込み後に cv2.cvtColor() を実行する必要がありません。
グレースケール画像としてしか使用しない場合はこちらの書き方でも問題ありません。

一方、同じ画像をカラー画像としても使用する場合は、通常通りカラーで読み込んでから、

cv2.cvtColor()

で別途グレースケール画像を作成した方が扱いやすくなります。

3.3. 画像サイズとチャンネル数

カラー画像とグレースケール画像では、NumPy配列の形状が異なります。
カラー画像の場合、

print(image.shape)

の結果は例えば、

(368, 445, 3)

となります。

これは、

高さ:368
幅 :445
チャンネル数:3

を表します。

グレースケール画像の場合は、

print(gray.shape)

の結果が、

(368, 445)

となります。

グレースケール画像にはチャンネル方向の次元がありません。
画像処理ライブラリや機械学習モデルへ入力するときは、この配列形状の違いに注意が必要です。

4. C++によるグレースケール変換

C++でもPythonと同様に、OpenCVのcvtColor()を使用してカラー画像をグレースケール画像へ変換できます。

4.1. cvtColorでグレースケール変換する

カラー画像を読み込んだ後にグレースケールへ変換する場合は、cv::cvtColor()を使用します。

#include <opencv2/opencv.hpp>

int main(void){
    // カラー画像を読み込む
    cv::Mat mat_img = cv::imread("shimons_labo.png");

    // グレースケール変換
    cv::Mat mat_gray;
    cv::cvtColor(mat_img, mat_gray, cv::COLOR_BGR2GRAY);

    // 保存
    cv::imwrite("shimons_labo_gray.png", mat_gray);

    return 0;
}

変換部分は次のコードです。

cv::cvtColor(mat_img, mat_gray, cv::COLOR_BGR2GRAY);

第1引数に変換元の画像、第2引数に変換後の画像を指定します。

第3引数にcv::COLOR_BGR2GRAYを指定することで、BGR画像からグレースケール画像へ変換します。

Pythonでは、

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

のように戻り値として変換後の画像を受け取りますが、C++では出力先となるcv::Matを第2引数に指定する点が異なります。

変換結果はPythonで実行した場合と同じグレースケール画像になります。

4.2. imreadで直接グレースケールとして読み込む

画像を最初からグレースケールとして使用する場合は、cv::imread()の第2引数に cv::IMREAD_GRAYSCALEを指定できます。

#include <opencv2/opencv.hpp>

int main(void){
    // グレースケール画像として読み込む
    cv::Mat mat_gray = cv::imread("shimons_labo.png", cv::IMREAD_GRAYSCALE);

    // 保存
    cv::imwrite("shimons_labo_gray.png", mat_gray);

    return 0;
}

この方法では、画像を読み込んだ後にcv::cvtColor()を実行する必要がありません。
グレースケール画像としてしか使用しない場合はこちらの書き方でも問題ありません。

一方、同じ画像をカラー画像としても使用する場合は、通常通りカラーで読み込んでから cv::cvtColor()でグレースケール画像を作成した方が扱いやすくなります。

4.3. チャンネル数を確認する

C++ではcv::Mat::channels()を使用すると画像のチャンネル数を確認できます。
カラー画像の場合は、

std::cout << mat_img.channels() << std::endl;

とすると、

3

となります。

グレースケール画像の場合は、

std::cout << mat_gray.channels() << std::endl;

とすると、

1

となります。

OpenCVで一般的に扱うBGRカラー画像は3チャンネルですが、グレースケール画像は明るさを表す1チャンネルだけで構成されています。

PythonではNumPy配列のshapeから画像サイズとチャンネル数を確認しますが、C++ではcv::Matrowscolschannels()などから確認できます。

例えば、

std::cout << "Height: " << mat_gray.rows << std::endl;
std::cout << "Width: " << mat_gray.cols << std::endl;
std::cout << "Channels: " << mat_gray.channels() << std::endl;

とすることで、次のように画像の高さ(Height)・幅(Width)・チャンネル数(Channels)を確認できます。

Height: 368
Width: 445
Channels: 1

5. グレースケール変換を使用する場面

グレースケール変換は、色の情報を必要としない画像処理の前処理としてよく使用されます。
例えば、

  • 2値化
  • エッジ検出
  • ハフ変換
  • テンプレートマッチング
  • 輪郭検出

などです。

色ではなく明るさの変化だけを使用する処理では、カラー画像の3チャンネルをそのまま扱う必要がありません。

グレースケールへ変換することで、後続処理をシンプルにできます。

ただし、色そのものを利用して対象物を抽出したい場合は、グレースケール化すると色情報が失われます。

例えば特定の色だけを抽出する場合は、BGRやHSVなどのカラー画像をそのまま使用します。

6. まとめ

OpenCVでは、cvtColor() を使用してカラー画像をグレースケールへ変換できます。
Pythonでは、

gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

C++では、

cv::cvtColor(mat_img, mat_gray, cv::COLOR_BGR2GRAY);

のように記述します。

また、最初からグレースケール画像として読み込む場合は、Pythonではcv2.IMREAD_GRAYSCALE、C++ではcv::IMREAD_GRAYSCALEを指定することもできます。

グレースケール変換は、2値化やエッジ検出など、色情報を必要としない画像処理の前処理としてよく使用されます。

一方で、一度グレースケール化すると色情報は失われるため、色を利用する処理ではカラー画像をそのまま使用します。

↓画像処理リファレンストップ