C#/VB.NETで画像処理⑩<画像のヒストグラム作成>
こんにちは、SKです。
C#/VB.NETで画像処理シリーズの第10弾。
カメラ画像のヒストグラムを作成します。
画像のヒストグラムとは、各ピクセルの輝度値の分布を表すグラフのこと。
画像同士の照合等に使われるこのヒストグラム、OpenCVでは簡単に計算できます。
OpenCVSharpとは?
(前回)画像のラベリング処理
動画手順
①ヒストグラムを作成する
OpenCVSharpのCalcHist関数でヒストグラムを作成します。
CalcHist関数
ヒストグラム — opencv 2.2 documentation
ヒストグラム その1: 計算して,プロットして,解析する !!! — OpenCV-Python Tutorials 1 documentation
・第1引数:入力画像の配列:Mat配列
・第2引数:画像チャンネルのインデックス:int配列
・第3引数:マスク画像:Mat
・第4引数:出力ヒストグラム:Mat
・第5引数:ヒストグラム次元数:int
・第6引数:ヒストグラムサイズ:int配列
・第7引数:計測する画素値の範囲:Rangef配列
引数が多く、入力画像を配列で指定する必要があるなど、やや複雑です。
B,G,Rそれぞれについて、下記のようにヒストグラムを計算します。
//ヒストグラム計算 Mat b_hist = new Mat(); Mat g_hist = new Mat(); Mat r_hist = new Mat(); Cv2.CalcHist(new Mat[] { img }, new int[] { 0 }, null, b_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) }); Cv2.CalcHist(new Mat[] { img }, new int[] { 1 }, null, g_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) }); Cv2.CalcHist(new Mat[] { img }, new int[] { 2 }, null, r_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) });
得られたヒストグラムはMat型ですが、中身は32bitの小数値が格納された1次元配列(サイズ=256)です。
②ヒストグラムを正規化する
ヒストグラムを線グラフで描画する用にMat画像histImage(512×400)を定義します。
そして、OpenCVSharpのNormalize関数でヒストグラムを正規化します。
今回は、0~400(histImageの高さ)の範囲で正規化することにします。
配列操作 — opencv 2.2 documentation
・第1引数:入力画像:Mat
・第2引数:出力画像:Mat
・第3引数:正規化範囲の下界:Mat
・第4引数:正規化範囲の上界:Mat
・第5引数:正規化の種類:NormTypes
//ヒストグラム画像 Mat histImage = new Mat(400, 512, MatType.CV_8UC3, Scalar.Black); //正規化 Cv2.Normalize(b_hist, b_hist, 0, histImage.Height, NormTypes.MinMax); Cv2.Normalize(g_hist, g_hist, 0, histImage.Height, NormTypes.MinMax); Cv2.Normalize(r_hist, r_hist, 0, histImage.Height, NormTypes.MinMax);
③ヒストグラム描画関数を定義する
B,G,Rそれぞれのヒストグラムを線グラフとして描画するための関数DrawHistを定義します。
forループでヒストグラム内の値とその次の値を取り出し、画像上の座標を計算し、Cv2.Line関数を使い線で繋いでいます。
private void DrawHist(Mat histImage, Mat hist, Scalar color) { int bin = histImage.Width / 256; for(int i = 0; i < 255; i++) { float value1 = hist.At<float>(i, 0); float value2 = hist.At<float>(i+1, 0); Point pt1 = new Point(i * bin, histImage.Height - value1); Point pt2 = new Point((i + 1) * bin, histImage.Height - value2); Cv2.Line(histImage, pt1, pt2, color, 2); } }
④ヒストグラム描画関数を呼び出す
③で定義したDrawHist関数をB、G、Rそれぞれのヒストグラムについて呼び出して描画した後、Cv2.ImShow関数で表示します。
//ヒストグラム表示 DrawHist(histImage, b_hist, Scalar.Blue); DrawHist(histImage, g_hist, Scalar.Lime); DrawHist(histImage, r_hist, Scalar.Red); Cv2.ImShow("Histogram", histImage); Cv2.WaitKey(1);
実行します。
カメラ画像と、ヒストグラムのグラフが表示されました。
最後に、コード全文を載せておきます。
C#
using System; using System.Threading.Tasks; using System.Windows.Forms; using OpenCvSharp; using OpenCvSharp.Extensions; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { Task.Run(() => { using (VideoCapture v = new VideoCapture(0)) using (Mat img = new Mat()) { while (true) { v.Read(img); Process(img); pictureBox1.Image = BitmapConverter.ToBitmap(img); } } }); } private void Process(Mat img) { //ヒストグラム計算 Mat b_hist = new Mat(); Mat g_hist = new Mat(); Mat r_hist = new Mat(); Cv2.CalcHist(new Mat[] { img }, new int[] { 0 }, null, b_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) }); Cv2.CalcHist(new Mat[] { img }, new int[] { 1 }, null, g_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) }); Cv2.CalcHist(new Mat[] { img }, new int[] { 2 }, null, r_hist, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) }); //ヒストグラム画像 Mat histImage = new Mat(400, 512, MatType.CV_8UC3, Scalar.Black); //正規化 Cv2.Normalize(b_hist, b_hist, 0, histImage.Height, NormTypes.MinMax); Cv2.Normalize(g_hist, g_hist, 0, histImage.Height, NormTypes.MinMax); Cv2.Normalize(r_hist, r_hist, 0, histImage.Height, NormTypes.MinMax); //ヒストグラム表示 DrawHist(histImage, b_hist, Scalar.Blue); DrawHist(histImage, g_hist, Scalar.Lime); DrawHist(histImage, r_hist, Scalar.Red); Cv2.ImShow("Histogram", histImage); Cv2.WaitKey(1); } private void DrawHist(Mat histImage, Mat hist, Scalar color) { int bin = histImage.Width / 256; for(int i = 0; i < 255; i++) { float value1 = hist.At<float>(i, 0); float value2 = hist.At<float>(i+1, 0); Point pt1 = new Point(i * bin, histImage.Height - value1); Point pt2 = new Point((i + 1) * bin, histImage.Height - value2); Cv2.Line(histImage, pt1, pt2, color, 2); } } } }
Imports OpenCvSharp Imports OpenCvSharp.Extensions Public Class Form1 Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load Task.Run(Sub() Using v As New VideoCapture(0) Using img As New Mat Do v.Read(img) Process(img) PictureBox1.Image = img.ToBitmap Loop End Using End Using End Sub) End Sub Private Sub Process(ByVal img As Mat) 'ヒストグラム計算 Dim b_hist As New Mat() Dim g_hist As New Mat() Dim r_hist As New Mat() Cv2.CalcHist(New Mat() {img}, New Integer() {0}, Nothing, b_hist, 1, New Integer() {256}, New Rangef() {New Rangef(0, 256)}) Cv2.CalcHist(New Mat() {img}, New Integer() {1}, Nothing, g_hist, 1, New Integer() {256}, New Rangef() {New Rangef(0, 256)}) Cv2.CalcHist(New Mat() {img}, New Integer() {2}, Nothing, r_hist, 1, New Integer() {256}, New Rangef() {New Rangef(0, 256)}) 'ヒストグラム画像 Dim histImage As Mat = New Mat(400, 512, MatType.CV_8UC3, Scalar.Black) '正規化 Cv2.Normalize(b_hist, b_hist, 0, histImage.Height, NormTypes.MinMax) Cv2.Normalize(g_hist, g_hist, 0, histImage.Height, NormTypes.MinMax) Cv2.Normalize(r_hist, r_hist, 0, histImage.Height, NormTypes.MinMax) 'ヒストグラム表示 DrawHist(histImage, b_hist, Scalar.Blue) DrawHist(histImage, g_hist, Scalar.Lime) DrawHist(histImage, r_hist, Scalar.Red) Cv2.ImShow("Histogram", histImage) Cv2.WaitKey(1) End Sub Private Sub DrawHist(ByVal histImage As Mat, ByVal hist As Mat, ByVal color As Scalar) Dim bin As Integer = histImage.Width / 256 For i = 0 To 254 Dim value1 As Single = hist.At(Of Single)(i, 0) Dim value2 As Single = hist.At(Of Single)(i + 1, 0) Dim pt1 As New Point(i * bin, histImage.Height - value1) Dim pt2 As New Point((i + 1) * bin, histImage.Height - value2) Cv2.Line(histImage, pt1, pt2, color, 2) Next End Sub End Class