첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
유용한 소스 코드가 있으면 icodebroker@naver.com으로 보내주시면 감사합니다.
블로그 자료는 자유롭게 사용하세요.

728x90
반응형

■ 비트맵 이진화 적용하기

------------------------------------------------------------------------------------------------------------------------


TestProject.zip



BitmapHelper.cs

 

 

using System.Drawing;

using System.Drawing.Imaging;

using System.Runtime.InteropServices;

 

namespace TestProject

{

    /// <summary>

    /// 비트맵 헬퍼

    /// </summary>

    public class BitmapHelper

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Field

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region Field

 

        /// <summary>

        /// 이미지 바이트 배열

        /// </summary>

        public byte[] ImageByteArray;

 

        /// <summary>

        /// 행 크기 바이트 수

        /// </summary>

        public int RowSizeByteCount;

 

        /// <summary>

        /// 픽셀 데이터 크기

        /// </summary>

        public const int PixelDataSize = 32;

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Private

 

        #region Field

 

        /// <summary>

        /// 비트맵

        /// </summary>

        private Bitmap bitmap;

 

        /// <summary>

        /// 비트맵 데이터

        /// </summary>

        private BitmapData bitmapData;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 너비 - Width

 

        /// <summary>

        /// 너비

        /// </summary>

        public int Width

        {

            get

            {

                return this.bitmap.Width;

            }

        }

 

        #endregion

        #region 높이 - Height

 

        /// <summary>

        /// 높이

        /// </summary>

        public int Height

        {

            get

            {

                return this.bitmap.Height;

            }

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - BitmapHelper(bitmap)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="bitmap">비트맵</param>

        public BitmapHelper(Bitmap bitmap)

        {

            this.bitmap = bitmap;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 비트맵 잠그기 - LockBitmap()

 

        /// <summary>

        /// 비트맵 잠그기

        /// </summary>

        public void LockBitmap()

        {

            Rectangle boundRectangle = new Rectangle(0, 0, this.bitmap.Width, this.bitmap.Height);

 

            this.bitmapData = this.bitmap.LockBits

            (

                boundRectangle,

                ImageLockMode.ReadWrite,

                PixelFormat.Format32bppArgb

            );

 

            RowSizeByteCount = this.bitmapData.Stride;

 

            int totalSize = this.bitmapData.Stride * this.bitmapData.Height;

 

            ImageByteArray = new byte[totalSize];

 

            Marshal.Copy(this.bitmapData.Scan0, ImageByteArray, 0, totalSize);

        }

 

        #endregion

        #region 비트맵 잠금 해제하기 - UnlockBitmap()

 

        /// <summary>

        /// 비트맵 잠금 해제하기

        /// </summary>

        public void UnlockBitmap()

        {

            int totalSize = this.bitmapData.Stride * this.bitmapData.Height;

 

            Marshal.Copy(ImageByteArray, 0, this.bitmapData.Scan0, totalSize);

 

            this.bitmap.UnlockBits(this.bitmapData);

 

            ImageByteArray = null;

 

            this.bitmapData = null;

        }

 

        #endregion

        #region 픽셀 구하기 - GetPixel(x, y, red, green, blue, alpha)

 

        /// <summary>

        /// 픽셀 구하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="red">빨강색</param>

        /// <param name="green">녹색</param>

        /// <param name="blue">파랑색</param>

        /// <param name="alpha">불투명도</param>

        public void GetPixel(int x, int y, out byte red, out byte green, out byte blue, out byte alpha)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            blue  = ImageByteArray[i++];

            green = ImageByteArray[i++];

            red   = ImageByteArray[i++];

            alpha = ImageByteArray[i  ];

        }

 

        #endregion

        #region 픽셀 설정하기 - SetPixel(x, y, red, green, blue, alpha)

 

        /// <summary>

        /// 픽셀 설정하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="red">빨강색</param>

        /// <param name="green">녹색</param>

        /// <param name="blue">파랑색</param>

        /// <param name="alpha">불투명도</param>

        public void SetPixel(int x, int y, byte red, byte green, byte blue, byte alpha)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            ImageByteArray[i++] = blue;

            ImageByteArray[i++] = green;

            ImageByteArray[i++] = red;

            ImageByteArray[i  ] = alpha;

        }

 

        #endregion

        #region 빨강색 구하기 - GetRed(x, y)

 

        /// <summary>

        /// 빨강색 구하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <returns>빨강색</returns>

        public byte GetRed(int x, int y)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            return ImageByteArray[i + 2];

        }

 

        #endregion

        #region 빨강색 설정하기 - SetRed(x, y, red)

 

        /// <summary>

        /// 빨강색 설정하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="red">빨강색</param>

        public void SetRed(int x, int y, byte red)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            ImageByteArray[i + 2] = red;

        }

 

        #endregion

        #region 녹색 구하기 - GetGreen(x, y)

 

        /// <summary>

        /// 녹색 구하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <returns>녹색</returns>

        public byte GetGreen(int x, int y)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            return ImageByteArray[i + 1];

        }

 

        #endregion

        #region 녹색 설정하기 - SetGreen(x, y, green)

 

        /// <summary>

        /// 녹색 설정하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="green">녹색</param>

        public void SetGreen(int x, int y, byte green)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            ImageByteArray[i + 1] = green;

        }

 

        #endregion

        #region 파랑색 구하기 - GetBlue(x, y)

 

        /// <summary>

        /// 파랑색 구하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <returns>파랑색</returns>

        public byte GetBlue(int x, int y)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            return ImageByteArray[i];

        }

 

        #endregion

        #region 파랑색 설정하기 - SetBlue(x, y, blue)

 

        /// <summary>

        /// 파랑색 설정하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="blue">파랑색</param>

        public void SetBlue(int x, int y, byte blue)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            ImageByteArray[i] = blue;

        }

 

        #endregion

        #region 붙투명도 구하기 - GetAlpha(x, y)

 

        /// <summary>

        /// 붙투명도 구하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <returns>불투명도</returns>

        public byte GetAlpha(int x, int y)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            return ImageByteArray[i + 3];

        }

 

        #endregion

        #region 불투명도 설정하기 - SetAlpha(x, y, alpha)

 

        /// <summary>

        /// 불투명도 설정하기

        /// </summary>

        /// <param name="x">X</param>

        /// <param name="y">Y</param>

        /// <param name="alpha">불투명도</param>

        public void SetAlpha(int x, int y, byte alpha)

        {

            int i = y * this.bitmapData.Stride + x * 4;

 

            ImageByteArray[i + 3] = alpha;

        }

 

        #endregion

    }

}

 

 

MainForm.cs

 

 

using System;

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor

        ////////////////////////////////////////////////////////////////////////////////////////// Public

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            this.openMenuItem.Click     += openMenuItem_Click;

            this.saveAsMenuItem.Click   += saveAsMenuItem_Click;

            this.exitMenuItem.Click     += exitMenuItem_Click;

            this.cutoffScrollBar.Scroll += cutoffScrollBar_Scroll;

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        ////////////////////////////////////////////////////////////////////////////////////////// Private

        //////////////////////////////////////////////////////////////////////////////// Event

 

        #region 열기 메뉴 항목 클릭시 처리하기 - openMenuItem_Click(sender, e)

 

        /// <summary>

        /// 열기 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void openMenuItem_Click(object sender, EventArgs e)

        {

            if(this.openFileDialog.ShowDialog() == DialogResult.OK)

            {

                this.sourcePictureBox.Image = GetBitmap(this.openFileDialog.FileName);

 

                ProcessBinaryContrast();

            }

        }

 

        #endregion

        #region 다른 이름으로 저장 메뉴 항목 클릭시 처리하기 - saveAsMenuItem_Click(sender, e)

 

        /// <summary>

        /// 다른 이름으로 저장 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void saveAsMenuItem_Click(object sender, EventArgs e)

        {

            if(this.saveFileDialog.ShowDialog() == DialogResult.OK)

            {

                SaveBitmap((Bitmap)this.targetPictureBox.Image, this.saveFileDialog.FileName);

            }

        }

 

        #endregion

        #region 종료 메뉴 항목 클릭시 처리하기 - exitMenuItem_Click(sender, e)

 

        /// <summary>

        /// 종료 메뉴 항목 클릭시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void exitMenuItem_Click(object sender, EventArgs e)

        {

            Close();

        }

 

        #endregion

        #region 컷오프 스크롤바 스크롤시 처리하기 - cutoffScrollBar_Scroll(sender, e)

 

        /// <summary>

        /// 컷오프 스크롤바 스크롤시 처리하기

        /// </summary>

        /// <param name="sender">이벤트 발생자</param>

        /// <param name="e">이벤트 인자</param>

        private void cutoffScrollBar_Scroll(object sender, ScrollEventArgs e)

        {

            this.cutoffValueLabel.Text = this.cutoffScrollBar.Value.ToString();

 

            ProcessBinaryContrast();

        }

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////// Function

 

        #region 비트맵 구하기 - GetBitmap(filePath)

 

        /// <summary>

        /// 비트맵 구하기

        /// </summary>

        /// <param name="filePath">파일 경로</param>

        /// <returns>비트맵</returns>

        private Bitmap GetBitmap(string filePath)

        {

            using(Bitmap bitmap = new Bitmap(filePath))

            {

                return new Bitmap(bitmap);

            }

        }

 

        #endregion

        #region 이진 대비 적용하기 - ApplyBinaryContrast(targetBitmap, cutoff)

 

        /// <summary>

        /// 이진 대비 적용하기

        /// </summary>

        /// <param name="targetBitmap">타겟 비트맵</param>

        /// <param name="cutoff">컷오프</param>

        private void ApplyBinaryContrast(Bitmap targetBitmap, int cutoff)

        {

            BitmapHelper helper = new BitmapHelper(targetBitmap);

 

            helper.LockBitmap();

 

            for(int y = 0; y < targetBitmap.Height; y++)

            {

                for(int x = 0; x < targetBitmap.Width; x++)

                {

                    byte red;

                    byte green;

                    byte blue;

                    byte alpha;

 

                    helper.GetPixel(x, y, out red, out green, out blue, out alpha);

 

                    if(red + green + blue > cutoff)

                    {

                        helper.SetPixel(x, y, 255, 255, 255, 255);

                    }

                    else

                    {

                        helper.SetPixel(x, y, 0, 0, 0, 255);

                    }

                }

            }

 

            helper.UnlockBitmap();

        }

 

        #endregion

        #region 이진 대비 처리하기 - ProcessBinaryContrast()

 

        /// <summary>

        /// 이진 대비 처리하기

        /// </summary>

        private void ProcessBinaryContrast()

        {

            if(this.sourcePictureBox.Image == null)

            {

                return;

            }

 

            Cursor = Cursors.WaitCursor;

 

            Bitmap bitmap = new Bitmap(this.sourcePictureBox.Image);

 

            ApplyBinaryContrast(bitmap, 3 * this.cutoffScrollBar.Value);

 

            if(this.targetPictureBox.Image != null)

            {

                this.targetPictureBox.Image.Dispose();

            }

 

            this.targetPictureBox.Image   = bitmap;

            this.targetPictureBox.Left    = this.sourcePictureBox.Right + 4;

 

            Cursor = Cursors.Default;

        }

 

        #endregion

        #region 비트맵 저장하기 - SaveBitmap(bitmap, filePath)

 

        /// <summary>

        /// 비트맵 저장하기

        /// </summary>

        /// <param name="bitmap">비트맵</param>

        /// <param name="filePath">파일 경로</param>

        public void SaveBitmap(Bitmap bitmap, string filePath)

        {

            string extension = Path.GetExtension(filePath);

 

            switch(extension.ToLower())

            {

                case ".bmp"  : bitmap.Save(filePath, ImageFormat.Bmp ); break;

                case ".exif" : bitmap.Save(filePath, ImageFormat.Exif); break;

                case ".gif"  : bitmap.Save(filePath, ImageFormat.Gif ); break;

                case ".jpg"  :

                case ".jpeg" : bitmap.Save(filePath, ImageFormat.Jpeg); break;

                case ".png"  : bitmap.Save(filePath, ImageFormat.Png ); break;

                case ".tif"  :

                case ".tiff" : bitmap.Save(filePath, ImageFormat.Tiff); break;

                default      : throw new NotSupportedException("Unknown file extension " + extension);

            }

        }

 

        #endregion

    }

}

 

------------------------------------------------------------------------------------------------------------------------

728x90
반응형
Posted by 사용자 icodebroker

댓글을 달아 주세요