C#でグレースケールや画像の明るさ調整、輪郭抽出などの画像処理をする方法を考えます。
グレースケール画像を取得する
アプリケーションはC# WindowsFormsで作ります。デザイナで以下のようなものを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); // 初期状態でチェックボックスにチェックが入っている状態にする checkBoxChangeBrightness.Checked = true; checkBoxChangeBoundaryValue.Checked = true; } // pictureBox2のImageプロパティに新しいBitmapをセットしたら古いものはDisposeする Bitmap _bitmap = null; Bitmap pictureBoxBitmap2 { set { _bitmap?.Dispose(); _bitmap = value; pictureBox2.Image = _bitmap; } } } |
フォームがLoadされたらリソースに追加しておいた画像データをpictureBox1(左側のPictureBox)のImageプロパティにセットします。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { protected override void OnLoad(EventArgs e) { Bitmap bitmap = Properties.Resources._25642734_s; pictureBox1.Image = bitmap; base.OnLoad(e); } } |
GetGrayscaleBitmapメソッドは引数で渡したBitmapのグレースケール画像を取得します。やっていることは各ピクセルのRGBの平均値を算出し、その値を引数にしてColor.FromArgbメソッドを呼び出して生成した色をBitmap.SetPixelメソッドでセットしているだけです(赤緑青では明るさが違うので本当はこんな単純な方法では不十分なのですが)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public partial class Form1 : Form { Bitmap GetGrayscaleBitmap(Bitmap bitmap) { Bitmap ret = new Bitmap(bitmap.Width, bitmap.Height); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { Color color = bitmap.GetPixel(x, y); int value = (color.R + color.G + color.B) / 3; Color newColor = Color.FromArgb(value, value, value); ret.SetPixel(x, y, newColor); } } return ret; } } |
ボタンがクリックされたらpictureBox1.ImageをGetGrayscaleBitmapメソッドに渡して新しいBitmapを取得し、これをpictureBoxBitmap2プロパティにセットしています。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private void button1_Click(object sender, EventArgs e) { Bitmap bitmap = (Bitmap)pictureBox1.Image; pictureBoxBitmap2 = GetGrayscaleBitmap(bitmap); } } |
画像の明るさ調整
画像の明るさ調整をします。TrackBarがスライドされると画像を明るくしたり暗くさせます。
GetBrightenedBitmapメソッドは第一引数で渡されたBitmapを第二引数分だけ明るくします。負数の場合は暗くします。
この部分はもっと工夫の余地があるのですが、ここでは各ピクセルのColorを取得して、ColorのRGBプロパティを第二引数分だけ増減させるという簡単な処理をしています。0よりも小さくなる場合は0、255より大きくなる場合は255にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public partial class Form1 : Form { Bitmap GetBrightenedBitmap(Bitmap bitmap, int value) { Bitmap ret = new Bitmap(bitmap.Width, bitmap.Height); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { Color color = bitmap.GetPixel(x, y); int r = color.R + value > 255 ? 255 : color.R + value; int g = color.G + value > 255 ? 255 : color.G + value; int b = color.B + value > 255 ? 255 : color.B + value; r = r < 0 ? 0 : r; g = g < 0 ? 0 : g; b = b < 0 ? 0 : b; Color newColor = Color.FromArgb(r, g, b); ret.SetPixel(x, y, newColor); } } return ret; } } |
チェックボックスがオンになっている場合だけTrackBarを操作可能にします。そしてtrackBarBrightness.Valueが変更されたら画像の明るさ調整をして、これを右側のPictureBoxに表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public partial class Form1 : Form { private void checkBoxChangeBrightness_CheckedChanged(object sender, EventArgs e) { trackBarBrightness.Enabled= checkBoxChangeBrightness.Checked; } private void trackBarBrightness_ValueChanged(object sender, EventArgs e) { checkBoxChangeBrightness.Text = $"明るさ変更 {trackBarBrightness.Value}"; Bitmap bitmap = (Bitmap)pictureBox1.Image; pictureBoxBitmap2 = GetBrightenedBitmap(bitmap, trackBarBrightness.Value); } } |
輪郭抽出をする
輪郭抽出をするための処理ですが、隣のピクセルと大きく色が異なる場合、そこは輪郭であると判断します。
画像の明るさ調整をします。TrackBarがスライドされると画像を明るくしたり暗くさせます。
GetEdgeBitmapメソッドは第一引数で渡されたBitmapにグレイスケール処理をして、右隣または下のピクセルのColor.Rプロパティの値が第二引数以上変化しているなら、そこに輪郭線を描画します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
public partial class Form1 : Form { Bitmap GetEdgeBitmap(Bitmap bitmap, int boundaryValue) { // グレースケール処理をしたときの各ピクセルの明度を二次元配列に格納する int[,] ints = new int[bitmap.Height, bitmap.Width]; for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { Color color = bitmap.GetPixel(x, y); ints[y, x] = (color.R + color.G + color.B) / 3; } } bool[,] isBorders = new bool[bitmap.Height, bitmap.Width]; // 各ピクセルの明度が格納された二次元配列の要素を比較する // 右と下に対応する値と比較してどちらかが条件を満たしていれば輪郭線を構成する点とみなす for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { bool b1 = x + 1 < bitmap.Width && Math.Abs(ints[y, x] - ints[y, x + 1]) > boundaryValue; bool b2 = y + 1 < bitmap.Height && Math.Abs(ints[y, x] - ints[y + 1, x]) > boundaryValue; isBorders[y, x] = b1 || b2; } } // 取得された輪郭線を構成する点から輪郭線が描画されたBitmapを生成する Bitmap ret = new Bitmap(bitmap.Width, bitmap.Height); for (int y = 0; y < bitmap.Height; y++) { for (int x = 0; x < bitmap.Width; x++) { if (isBorders[y, x]) ret.SetPixel(x, y, Color.Black); else ret.SetPixel(x, y, Color.White); } } return ret; } } |
チェックボックスがオンになっている場合だけTrackBarを操作可能にします。そしてtrackBarBoundaryValue.Valueが変更されたら輪郭抽出の処理をして、これを右側のPictureBoxに表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public partial class Form1 : Form { private void checkBoxChangeBoundaryValue_CheckedChanged(object sender, EventArgs e) { trackBarBoundaryValue.Enabled = checkBoxChangeBoundaryValue.Checked; } private void trackBarBoundaryValue_ValueChanged(object sender, EventArgs e) { Bitmap bitmap = (Bitmap)pictureBox1.Image; int boundaryValue = trackBarBoundaryValue.Value; checkBoxChangeBoundaryValue.Text = $"境界値変更 {boundaryValue}"; pictureBoxBitmap2 = GetEdgeBitmap(bitmap, boundaryValue); } } |