OpenCV 5.0.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 5.0.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
ヒストグラムの計算

前のチュートリアル: ヒストグラム平坦化
次のチュートリアル: ヒストグラム比較

原著者Ana Huamán
互換性OpenCV >= 3.0

目標

このチュートリアルでは、以下の方法を学ぶ:

  • OpenCVの関数 cv::split を使って、画像を対応するプレーンに分割する。
  • OpenCVの関数 cv::calcHist を使って画像配列のヒストグラムを計算する
  • 関数 cv::normalize を使って配列を正規化する
覚え書き
前のチュートリアル(ヒストグラム平坦化)では、画像ヒストグラムと呼ばれる特定の種類のヒストグラムについて述べた。今回はそれをより一般的な概念として考える。読み進めてほしい!

ヒストグラムとは何か?

  • ヒストグラムとは、あらかじめ定義されたビンの集合に整理されたデータのカウントを集めたものである
  • データと言うとき、それを輝度値に限定しているわけではない(前のチュートリアル ヒストグラム平坦化 で見たように)。集めるデータは、画像を記述するうえで有用だと考えるどんな特徴であってもよい。
  • 例を見てみよう。ある行列が画像の情報(すなわち \(0-255\) の範囲の輝度)を含んでいるとする。
  • このデータを整理された形でカウントしたい場合はどうなるだろうか。この場合の情報値の範囲が256個の値であることが分かっているので、その範囲を次のように下位部分(ビンと呼ぶ)に分割できる。

    \[\begin{array}{l} [0, 255] = { [0, 15] \cup [16, 31] \cup ....\cup [240,255] } \\ range = { bin_{1} \cup bin_{2} \cup ....\cup bin_{n = 15} } \end{array}\]

    そして、各 \(bin_{i}\) の範囲に入るピクセル数をカウントしていける。これを上記の例に適用すると、下の画像が得られる(x軸はビンを、y軸は各ビン内のピクセル数を表す)。

  • これはヒストグラムがどのように機能し、なぜ有用なのかを示す単純な例にすぎない。ヒストグラムは色の輝度だけでなく、測定したい任意の画像特徴(すなわち勾配、方向など)のカウントを保持できる。
  • Let's identify some parts of the histogram:
    1. dims: データを集めたいパラメータの数。この例では、グレースケール画像で各ピクセルの輝度値のみをカウントしているため、dims = 1 である。
    2. bins: 各次元における分割数である。この例では bins = 16 である
    3. range: 測定する値の範囲。この場合は range = [0,255]
  • 2つの特徴を数えたい場合はどうなるだろうか。この場合、得られるヒストグラムは3Dプロットになる(x と y がそれぞれの特徴の \(bin_{x}\) と \(bin_{y}\) に対応し、z が \((bin_{x}, bin_{y})\) の各組み合わせのカウント数になる)。さらに多くの特徴についても同様である(もちろん、より扱いが難しくなる)。

OpenCV が提供するもの

単純な用途向けに、OpenCVは関数 cv::calcHist を実装している。これは配列の集合(通常は画像や画像のプレーン)のヒストグラムを計算する。最大32次元まで扱える。以下のコードで実際に見ていく。

コード

  • What does this program do?
    • 画像を読み込む
    • 関数 cv::split を使って画像を R, G, B のプレーンに分割する
    • 関数 cv::calcHist を呼び出して、各 1 チャンネルプレーンのヒストグラムを計算する
    • 3つのヒストグラムをウィンドウにプロットする

説明

  • 元画像を読み込む

  • 元画像を R, G, B の3つのプレーンに分離する。これにはOpenCVの関数 cv::split を使う:

    入力は分割する画像(この場合は3チャンネル)であり、出力は Mat のベクトルである)

  • これで各プレーンのヒストグラムを設定する準備が整った。ここでは B, G, R のプレーンを扱っているため、値は \([0,255]\) の区間の範囲をとることがわかっている
  • ビン (bin) の数を決める (5, 10...):

  • 値の範囲を設定する(前述のとおり、0から255の間)

  • ビンを同じサイズ(一様)にし、最初にヒストグラムをクリアしたいので、次のようにする:

  • OpenCVの関数 cv::calcHist を使ってヒストグラムを計算する:

  • where the arguments are (C++ code):
    • &bgr_planes[0]: 元の配列
    • 1: 元の配列の数(この場合は1つ使用している。ここに配列のリストを入力することもできる)
    • 0: 測定するチャンネル (dim)。この場合は単に強度(各配列はシングルチャンネル)なので、0と書くだけでよい。
    • Mat(): 元の配列に使用するマスク(0は無視するピクセルを示す)。定義しなければ使用されない
    • b_hist: ヒストグラムを格納する Mat オブジェクト
    • 1: ヒストグラムの次元数。
    • histSize: 使用する各次元あたりのビンの数
    • histRange: 各次元ごとに測定する値の範囲
    • uniformaccumulate: ビンのサイズは同じで、最初にヒストグラムがクリアされる。
  • ヒストグラムを表示するための画像を作成する:

  • 描画の前に、まずヒストグラムを cv::normalize で正規化し、入力した引数で示される範囲に値が収まるようにしている点に注意:

  • this function receives these arguments (C++ code):
    • b_hist: 入力配列
    • b_hist: 出力先の正規化済み配列(入力と同じでもよい)
    • 0histImage.rows: この例では、r_hist の値を正規化する際の下限と上限である
    • NORM_MINMAX: 正規化の種類を示す引数(前述のとおり、設定した2つの限界値の間に値を調整する)
    • -1: 出力先の正規化済み配列が入力と同じ型になることを意味する
    • Mat(): 省略可能なマスク
  • ビンへのアクセス方法に注目する(この場合はこの1Dヒストグラムにおいて):

    次の式を使う(C++コード):

    b_hist.at<float>(i)

    ここで \(i\) は次元を示す。2Dヒストグラムの場合は、次のようなものを使うことになる:

    b_hist.at<float>( i, j )
  • 最後にヒストグラムを表示し、ユーザーが終了するのを待つ:

結果

  1. 次に示すような画像を入力引数として使うと:
  1. 次のヒストグラムが生成される: