HUSKING - kotteri

技術系Note

【C#】画像の2値化(閾値固定)

画像処理として、2値化をやってみる
閾値を固定にしています。いつか判別分析で求めてみたい。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace TestImage
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        
        /// <summary>
        /// Formロード時処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            // 元の画像を表示(左)
            string imagePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "test.jpg");
            pictureBox1.SizeMode = PictureBoxSizeMode.Zoom;
            pictureBox1.ImageLocation = imagePath;

            // 変換後の画像を表示(右)
            pictureBox2.SizeMode = PictureBoxSizeMode.Zoom;
            Bitmap img = new Bitmap(imagePath);
            CreateImage(img);
            pictureBox2.Image = img;
        }

        /// <summary>
        /// 画像を2値化する(24ビット画像のみ)
        /// </summary>
        /// <param name="srcImg">変換する画像</param>
        public void CreateImage(Bitmap srcImg)
        {
            BitmapData srcData = null;
            try
            {
                //=====================================================================
                // 変換する画像の1ピクセルあたりのバイト数を取得
                //=====================================================================
                PixelFormat pixelFormat = srcImg.PixelFormat;
                int pixelSize = Image.GetPixelFormatSize(pixelFormat) / 8;

                //=====================================================================
                // 変換する画像データをアンマネージ配列にコピー
                //=====================================================================
                srcData = srcImg.LockBits(
                    new Rectangle(0, 0, srcImg.Width, srcImg.Height),
                    ImageLockMode.ReadWrite,
                    pixelFormat);
                byte[] buf = new byte[srcData.Stride * srcData.Height];
                Marshal.Copy(srcData.Scan0, buf, 0, buf.Length);

                //=====================================================================
                // 2値化
                //=====================================================================
                for (int y = 0; y < srcData.Height; y++)
                {
                    for (int x = 0; x < srcData.Width; x++)
                    {
                        // ピクセルで考えた場合の開始位置を計算する
                        int pos = y * srcData.Stride + x * pixelSize;

                        // ピクセルの輝度を算出
                        int gray = (int)(0.299 * buf[pos + 2] + 0.587 * buf[pos + 1] + 0.114 * buf[pos]);

                        if (gray > 127)
                        {
                            // 閾値を超えた場合、白
                            buf[pos] = 0xFF;
                            buf[pos + 1] = 0xFF;
                            buf[pos + 2] = 0xFF;
                        }
                        else
                        {
                            // 閾値以下の場合、黒
                            buf[pos] = 0x0;
                            buf[pos + 1] = 0x0;
                            buf[pos + 2] = 0x0;
                        }
                    }
                }

                Marshal.Copy(buf, 0, srcData.Scan0, buf.Length);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                if (srcImg != null && srcData != null)
                {
                    srcImg.UnlockBits(srcData);
                }
            }
        }
    }

}

結果は以下の通り
f:id:huskworks53:20180821225651p:plain

閾値をちゃんとしないとダメですね。。