【C#】Chartを使ったヒストグラム表示

Chartコントロールを使いたかった理由の一つに画像処理ではおなじみのヒストグラムをChartコントロールで表示したかったのですが、その簡単なプログラムです。

 

フォームにはChartコントロールを配置し、Chartの名前がchart1とした事を前提として、

// /////////////////////////////////////////////////////
// Chartコントロール内のグラフ、凡例、目盛り領域を削除
chart1.Series.Clear();
chart1.Legends.Clear();
chart1.ChartAreas.Clear();

// /////////////////////////////////////////////////////
// 目盛り領域の設定
var ca = chart1.ChartAreas.Add("Histogram");

// X軸
ca.AxisX.Title = "Brightness";  // タイトル
ca.AxisX.Minimum = 0;           // 最小値
ca.AxisX.Maximum = 256;         // 最大値
ca.AxisX.Interval = 64;         // 目盛りの間隔
// Y軸
ca.AxisY.Title = "Count";
ca.AxisY.Minimum = 0;

// /////////////////////////////////////////////////////
// データの追加
var hist = new Point[] {
    new Point(32, 10),
    new Point(96, 30),
    new Point(160, 50),
    new Point(224, 20)
};

// グラフの系列を追加
var s = chart1.Series.Add("Histogram");
// 棒グラフの隙間を無くす
s.SetCustomProperty("PointWidth", "1.0");
// データ設定
for (int i = 0; i < hist.Length; i++) {
    s.Points.AddXY(hist[i].X, hist[i].Y);
}

と書くだけで、モノクロ画像用のヒストグラムが簡単に作成できます。

 

(実行結果)

 

ここでのポイントとしては、棒グラフ(ChartTypeColumn)の時に、棒グラフの間隔を無くすにはPointWidthプロパティを1.0にすればよい(デフォルトは0.8)のですが、各グラフ特有のプロパティ設定は以下のように設定する必要があります。

(直接プロパティの設定ができません。)

// 棒グラフの隙間を無くす
s.SetCustomProperty("PointWidth", "1.0");

モノクロ画像の時はこれでもよいのですが、カラーだと、R,G,Bの3つのグラフを書かないといけないので、折れ線では表現しにくいので、折れ線(ChartTypeLine)を使ってみたいと思います。

 

要領は同じで、SeriesにR,G,B用の3つを追加することぐらいです。

// /////////////////////////////////////////////////////
// Chartコントロール内のグラフ、凡例、目盛り領域を削除
chart1.Series.Clear();
chart1.Legends.Clear();
chart1.ChartAreas.Clear();

// /////////////////////////////////////////////////////
// 目盛り領域の設定
var ca = chart1.ChartAreas.Add("Histogram");

// X軸
ca.AxisX.Title = "Brightness";  // タイトル
ca.AxisX.Minimum = 0;           // 最小値
ca.AxisX.Maximum = 256;         // 最大値
ca.AxisX.Interval = 64;         // 目盛りの間隔
// Y軸
ca.AxisY.Title = "Count";
ca.AxisY.Minimum = 0;

// /////////////////////////////////////////////////////
// データの追加
var histR = new Point[] {
    new Point(0, 10),
    new Point(63, 30),
    new Point(127, 80),
    new Point(191, 30),
    new Point(255, 15)
};
var histG = new Point[] {
    new Point(0, 70),
    new Point(63, 60),
    new Point(127, 40),
    new Point(191, 20),
    new Point(255, 10)
};
var histB = new Point[] {
    new Point(0, 10),
    new Point(63, 20),
    new Point(127, 30),
    new Point(191, 70),
    new Point(255, 90)
};

// グラフの系列を追加
var sR = chart1.Series.Add("HistogramR");
var sG = chart1.Series.Add("HistogramG");
var sB = chart1.Series.Add("HistogramB");

// グラフの種類を折れ線に設定する
sR.ChartType = sG.ChartType = sB.ChartType 
    = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;

// データ設定
for (int i = 0; i < histR.Length; i++)
{
    sR.Points.AddXY(histR[i].X, histR[i].Y);
    sG.Points.AddXY(histG[i].X, histG[i].Y);
    sB.Points.AddXY(histB[i].X, histB[i].Y);
}

(実行結果)

 

これでカラーのヒストグラムも表示できる事ができます!

とか言っても、見た目があまりにもショボイので、もう少し手を入れます。

 

今度はグラフの種類にスプライン面グラフ(SeriesChartType.SplineArea)を使ってみます。

// /////////////////////////////////////////////////////
// Chartコントロール内のグラフ、凡例、目盛り領域を削除
chart1.Series.Clear();
chart1.Legends.Clear();
chart1.ChartAreas.Clear();

// /////////////////////////////////////////////////////
// 目盛り領域の設定
var ca = chart1.ChartAreas.Add("Histogram");

// X軸
ca.AxisX.Title = "Brightness";  // タイトル
ca.AxisX.Minimum = 0;           // 最小値
ca.AxisX.Maximum = 256;         // 最大値
ca.AxisX.Interval = 64;         // 目盛りの間隔
// Y軸
ca.AxisY.Title = "Count";
ca.AxisY.Minimum = 0;

// /////////////////////////////////////////////////////
// データの追加
var histR = new Point[] {
    new Point(0, 10),
    new Point(63, 30),
    new Point(127, 80),
    new Point(191, 30),
    new Point(255, 15)
};
var histG = new Point[] {
    new Point(0, 70),
    new Point(63, 60),
    new Point(127, 40),
    new Point(191, 20),
    new Point(255, 10)
};
var histB = new Point[] {
    new Point(0, 10),
    new Point(63, 20),
    new Point(127, 30),
    new Point(191, 70),
    new Point(255, 90)
};

// グラフの系列を追加
var sR = chart1.Series.Add("HistogramR");
var sG = chart1.Series.Add("HistogramG");
var sB = chart1.Series.Add("HistogramB");

// グラフの種類をスプライン面グラフに設定する
sR.ChartType = sG.ChartType = sB.ChartType
    = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.SplineArea;

// 輪郭線の太さ
sR.BorderWidth = sG.BorderWidth = sB.BorderWidth = 2;

// 輪郭線の色
sR.BorderColor = Color.Red;
sG.BorderColor = Color.Green;
sB.BorderColor = Color.Blue;

// 塗りつぶしの色の設定(半透明)
sR.Color = Color.FromArgb(150, Color.Red);
sG.Color = Color.FromArgb(150, Color.Green);
sB.Color = Color.FromArgb(150, Color.Blue);

// データ設定
for (int i = 0; i < histR.Length; i++)
{
    sR.Points.AddXY(histR[i].X, histR[i].Y);
    sG.Points.AddXY(histG[i].X, histG[i].Y);
    sB.Points.AddXY(histB[i].X, histB[i].Y);
}

(実行結果)

 

これで、かなりそれっぽくないですか?

プログラム的に書くと、塗りつぶしの色に半透明の色を設定できるところがポイントでしょうかね。

 

Chartコントロールの使用方法へ戻る

【C#】Chartコントロールをとりあえず使ってみる

Chartコントロールは、本当に多くのプロパティがあるため、一つ一つを調べてからプログラムをするよりも、まずはVisual Studioのフォームエディタ上でいろいろ触ってみてから、プロパティの意味するところを覚えて、プログラムを組むようにした方が早そうです。

 

という事で、フォームエディタ上で、いろいろ触ってみました。

 

コントロールの追加

ツールボックスのデータの中にあるChartを選択し、フォームへ追加します。

 

 

グラフデータの追加

グラフのデータはSeriesプロパティで管理されています。

 

Series(コレクション)の部分をクリックし、  をクリックするとエディタが表示されます。

 

 

グラフのデータはデータPointsプロパティの(コレクション)の部分をクリックし、  をクリックすると各データを編集する画面↓が表示されます。

 

 

追加のボタンをクリックするたびにデータが追加されます。

下図は5個のデータを追加した様子

 

 

各データの値はデータのXValueとYValuesの値を設定します。

ここでYの値だけ複数形になっている?!と思えたら、少しChartコントロールに慣れてきている感じでしょうか?

複数形は複数の設定ができます。

 

この状態で5個のデータを表示したのがこちら↓

 

 

グラフの種類の設定

グラフの種類は各系列(Series)ごとに設定ができ(できない組み合わせもあります)、グラフのChartTypeで設定します。

 

 

ここで、縦の棒グラフ(Column)から折れ線グラフ(Line)に変更すると

 

 

上図のようになります。

 

マーカーの追加

マーカーはSeriesプロパティのMakerSyleというプロパティがあります。

 

デフォルトではマーカーなし(None)になっていますが、これをSquareにしてみます。

エクセルっぽい!

 

軸の名前の設定

グラフの軸を管理しているのはChartAreaプロパティになります。

ChartArea(コレクション)の部分をクリックし、  をクリックするとエディタが表示されます。

 

軸を管理しているのがAxesプロパティでさらにエディタを開きます。

 

 

Axisコレクションには最初から

X axis X軸(主軸)
Y(Value) axis Y軸(主軸)
Secondary X axis X軸(第二軸)
Secondary Y axis Y軸(第二軸)

の4つが用意されており、各軸のTitleプロパティで軸の名前を設定します。

 

 

X軸とY軸のTitleを設定した結果↓

 

 

グラフのタイトルの設定

グラフのタイトルはTitleプロパティになります。

Title(コレクション)の部分をクリックし、  をクリックするとエディタが表示されます。

 

 

(Text)の部分を編集するとグラフのタイトルが表示されます。

 

凡例領域の設定

凡例領域の設定はLegendsロパティになります。

Legends(コレクション)の部分をクリックし、  をクリックするとエディタが表示されます。

 

 

Legends1のTitleプロパティに凡例表示と入力してみると

 

 

ん??

Series1の部分が変わるかと思ったら、その上に表示されるだけ?

Legendsプロパティはあくまでも凡例表示全体(各系列の凡例を表示している領域)のプロパティ設定であって、各系列(Series)の設定ではありません。

 

Series1の部分の表示名を変更するにはSeriesプロパティのNameプロパティを設定します。

 

 

凡例の名前が変更されました↓

 

他にも様々なプロパティがあるので、いじりたおすと面白いと思います。

 

これで、この操作をプログラムで表現できれば、何とかなりそうな気がしてきた!

 

Chartコントロールの使用方法へ戻る

【C#】Chartコントロールの主なプロパティ

Chartコントロールを使うにはSeriesプロパティが最も重要になりますが、次にLegends,ChartAreas,Titlesの3つ、Annotationsは、ほとんど使わないと思います。

 

 

●Seriesプロパティ

グラフのデータやグラフの種類を取得、設定します。

エクセルのグラフでいうところのグラフの種類系列を追加した近い感じです。

●Legendsプロパティ

凡例表示領域に関する情報を取得、設定します。

●ChartAreasプロパティ

グラフの目盛り領域に関する情報を取得、設定します。

●Titlesプロパティ

グラスのタイトルに関する情報を取得、設定します。

●Annotationsプロパティ

グラフの注釈に関する情報を取得、設定します。

 

どのプロパティも□□□□□sというように複数形になっているからも想像できると思いますが、複数個設定する事ができます。

 

Chartコントロールの使用方法へ戻る

【C#】SplitContainerの境界線に描画する

SplitContainerの境界線をクリックすると片側のPanelを閉じるなどのGUIで、境界線の部分に三角形などを書きたい場合があります。

こんな感じ↓

 

その場合、どうするのか?

Splitterのオブジェクトを探してみても存在しないのですが、SplitContainerの構造を理解すると、Splitter部分に描画する事ができます。

 

上図のようにSplitContainerはContainerの上に2つのパネル(Panel1,Panel2)が張り付いているイメージです。

SplitterはPanel1とPanel2の隙間から見えるContainerそのものとなっています。

そのため、Splitterに描画するには、隙間から見えるContainerに描画すればOKです。

また、参考までに主なSplitContainerのプロパティは以下の通りです。

 

その事を理解したうえでSplitContainerのPaintイベントで三角印を書くサンプルは以下のようになります。

 private void splitContainer1_Paint(object sender, PaintEventArgs e)
 {
     Point[] p = new Point[3];

     p[0] = new Point(splitContainer1.SplitterDistance, splitContainer1.Height / 2 - splitContainer1.SplitterWidth * 2 / 3);
     p[1] = new Point(splitContainer1.SplitterDistance, splitContainer1.Height / 2 + splitContainer1.SplitterWidth * 2 / 3);
     p[2] = new Point(splitContainer1.SplitterDistance + splitContainer1.SplitterWidth, splitContainer1.Height / 2);

     e.Graphics.FillPolygon(Brushes.Gray, p);
 }

 private void splitContainer1_Resize(object sender, EventArgs e)
 {
     splitContainer1.Refresh();
 }

上記のプログラムはかなりいい加減なので、お好みで修正して下さい。

 

Paintイベントだけでは、SplitContainerのリサイズの時に描画してくれないので、ResizeイベントでコントロールをRefresh()してPaintイベントを発生させています。

 

C#へ戻る

【Excel】正規化相関

エクセルで内積ノルムの計算ができれば、正規化相関も計算できる!

と思ったら、エクセルの関数で用意されていました。

 

書式は

 

= CORREL(“配列1”, “配列2”)

 

となります。

 

 

この正規化相関の計算式はテンプレートマッチングでもおなじみの

 

 

という式になります。

 

こんな式まで用意されているのは、ちょっとしたアルゴリズムの検証などに便利です。

 

← Excelへ戻る

【Excel】ベクトルのノルムの計算

ベクトルのノルム(大きさ)の計算のおさらいですが、ベクトルを

 

とすると、ノルムは

 

となります。

これをエクセルで計算するには?と探してみるものの、直接的なエクセルの関数は無さそうでした。

そこで、前回内積の計算を紹介しましたが、同じベクトルの内積を計算してルート(SQRT()関数)を使うと、とりあえずできますが、さらに探すと SUMSQ なる関数が用意されていました。

 

このSUMSQ関数は、各要素を2乗して合計を計算する関数ですが、これで、ノルムのルートの中身が計算できます。

 

ということで、ベクトルのノルムの計算は

 

= SQRT(SUMSQ(“セルの範囲”))

 

として

 

のように、SQRTSUMSQ の2つの関数の組み合わせで、計算する事ができます。

 

← Excelへ戻る

【Excel】内積の計算

エクセルで内積の計算をするには SUMPRODUCT 関数を用います。

 

 

 

SUMPRODUCT関数を検索すると、品物の単価と個数を掛け合わせて合計金額を計算する例が多いのですが、各配列の要素を掛け合わせた合計の計算は内積そのもので、個人的には、この内積の計算の出番が多そうです。

 

← Excelへ戻る