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

728x90
반응형

■ 이미지 윤곽선 검출하기

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


TestProject.zip


MainForm.cs

 

 

using System;

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

using System.Runtime.InteropServices;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 이용 가능한 픽셀 포맷

        /// </summary>

        private const PixelFormat AVAILABLE_PIXEL_FORMAT = PixelFormat.Format24bppRgb;

 

        /// <summary>

        /// 소스 비트맵

        /// </summary>

        private Bitmap sourceBitmap;

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            #region 이벤트를 설정한다.

 

            this.sourcePanel.Click                              += sourcePanel_Click;

            this.sourcePictureBox.Click                         += sourcePictureBox_Click;

            this.targetPanel.Click                              += targetPanel_Click;

            this.targetPictureBox.Click                         += targetPictureBox_Click;

            this.brightnessLowerLimitNumericUpDown.ValueChanged += brightnessLowerLimitNumericUpDown_ValueChanged;

            this.brightnessUpperLimitNumericUpDown.ValueChanged += brightnessUpperLimitNumericUpDown_ValueChanged;

            this.rgbRadioButton.CheckedChanged                  += rgbRadioButton_CheckedChanged;

            this.blackWhiteRadioButton.CheckedChanged           += blackWhiteRadioButton_CheckedChanged;

            this.grayscaleRadioButton.CheckedChanged            += grayscaleRadioButton_CheckedChanged;

            this.invertEdgeColorCheckBox.CheckedChanged         += invertEdgeColorCheckBox_CheckedChanged;

 

            #endregion

        }

 

        #endregion

 

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

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

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

 

        #region 소스 패널 클릭시 처리하기 - sourcePanel_Click(sender, e)

 

        /// <summary>

        /// 소스 패널 클릭시 처리하기

        /// </summary>

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

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

        private void sourcePanel_Click(object sender, EventArgs e)

        {

            LoadSourceImage();

        }

 

        #endregion

        #region 소스 픽처 박스 클릭시 처리하기 - sourcePictureBox_Click(sender, e)

 

        /// <summary>

        /// 소스 픽처 박스 클릭시 처리하기

        /// </summary>

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

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

        private void sourcePictureBox_Click(object sender, EventArgs e)

        {

            LoadSourceImage();

        }

 

        #endregion

        #region 타겟 패널 클릭시 처리하기 - targetPanel_Click(sender, e)

 

        /// <summary>

        /// 타겟 패널 클릭시 처리하기

        /// </summary>

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

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

        private void targetPanel_Click(object sender, EventArgs e)

        {

            SaveTargetImage();

        }

 

        #endregion

        #region 타겟 픽처 박스 클릭시 처리하기 - targetPictureBox_Click(sender, e)

 

        /// <summary>

        /// 타겟 픽처 박스 클릭시 처리하기

        /// </summary>

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

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

        private void targetPictureBox_Click(object sender, EventArgs e)

        {

            SaveTargetImage();

        }

 

        #endregion

 

        #region 명도 하한 숫자 UP/DOWN 값 변경시 처리하기 - brightnessLowerLimitNumericUpDown_ValueChanged(sender, e)

 

        /// <summary>

        /// 명도 하한 숫자 UP/DOWN 값 변경시 처리하기

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void brightnessLowerLimitNumericUpDown_ValueChanged(object sender, EventArgs e)

        {

            ProcessImage();

        }

 

        #endregion        

        #region 명도 상한 숫자 UP/DOWN 값 변경시 처리하기 - brightnessLowerLimitNumericUpDown_ValueChanged(sender, e)

 

        /// <summary>

        /// 명도 상한 숫자 UP/DOWN 값 변경시 처리하기

        /// </summary>

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

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

        private void brightnessUpperLimitNumericUpDown_ValueChanged(object sender, EventArgs e)

        {

            ProcessImage();

        }

 

        #endregion

 

        #region RGB 색상 라디오 버튼 체크 변경시 처리하기 - rgbRadioButton_CheckedChanged(sender, e)

 

        /// <summary>

        /// RGB 색상 라디오 버튼 체크 변경시 처리하기

        /// </summary>

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

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

        private void rgbRadioButton_CheckedChanged(object sender, EventArgs e)

        {

            if(this.rgbRadioButton.Checked)

            {

                ProcessImage();

            }

        }

 

        #endregion

        #region 흑백 색상 라디오 버튼 체크 변경시 처리하기 - blackWhiteRadioButton_CheckedChanged(sender, e)

 

        /// <summary>

        /// 흑백 색상 라디오 버튼 체크 변경시 처리하기

        /// </summary>

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

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

        private void blackWhiteRadioButton_CheckedChanged(object sender, EventArgs e)

        {

            if(this.blackWhiteRadioButton.Checked)

            {

                ProcessImage();

            }

        }

 

        #endregion

        #region 회색조 색상 라디오 버튼 체크 변경시 처리하기 - grayscaleRadioButton_CheckedChanged(sender, e)

 

        /// <summary>

        /// 회색조 색상 라디오 버튼 체크 변경시 처리하기

        /// </summary>

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

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

        private void grayscaleRadioButton_CheckedChanged(object sender, EventArgs e)

        {

            if(this.grayscaleRadioButton.Checked)

            {

                ProcessImage();

            }

        }

 

        #endregion

 

        #region 윤곽선 색상 반전 체크 박스 체크 변경시 처리하기 - invertEdgeColorCheckBox_CheckedChanged(sender, e)

 

        /// <summary>

        /// 윤곽선 색상 반전 체크 박스 체크 변경시 처리하기

        /// </summary>

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

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

        private void invertEdgeColorCheckBox_CheckedChanged(object sender, EventArgs e)

        {

            ProcessImage();

        }

 

        #endregion

 

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

 

        #region 소스 이미지 로드하기 - LoadSourceImage()

 

        /// <summary>

        /// 소스 이미지 로드하기

        /// </summary>

        private void LoadSourceImage()

        {

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

            {

                return;

            }

 

            this.sourceBitmap = new Bitmap(this.openFileDialog.FileName);

 

            if(this.sourceBitmap != null && this.sourceBitmap.PixelFormat == AVAILABLE_PIXEL_FORMAT)

            {

                this.sourcePictureBox.Image = this.sourceBitmap;

                    

                DetectImageContour();

            }

            else

            {

                this.sourceBitmap = null;

            }

        }

 

        #endregion

        #region 타겟 이미지 저장하기 - SaveTargetImage()

 

        /// <summary>

        /// 타겟 이미지 저장하기

        /// </summary>

        private void SaveTargetImage()

        {

            if(this.targetPictureBox.Image == null)

            {

                return;

            }

 

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

            {

                return;

            }

 

            if(this.saveFileDialog.FileName == this.openFileDialog.FileName)

            {

                return;

            }

 

            string fileExtension = new FileInfo(openFileDialog.FileName).Extension;

 

            switch(fileExtension)

            {

                case ".bmp" :

                {

                    this.targetPictureBox.Image.Save(saveFileDialog.FileName,ImageFormat.Bmp);

 

                    break;

                }

                case ".jpg"  :

                case ".jpeg" :

                {

                    this.targetPictureBox.Image.Save(saveFileDialog.FileName,ImageFormat.Jpeg);

 

                    break;

                }

                default :

                {

                    this.targetPictureBox.Image.Save(saveFileDialog.FileName,ImageFormat.Png);

 

                    break;

                }

            }

        }

 

        #endregion

 

        #region 이미지 윤곽선 탐지하기 - DetectImageContour()

 

        /// <summary>

        /// 이미지 윤곽선 탐지하기

        /// </summary>

        private void DetectImageContour()

        {

            Bitmap targetBitmap = (Bitmap)sourceBitmap.Clone();

 

            int targetBitmapWidth  = targetBitmap.Width;

            int targetBitmapHeight = targetBitmap.Height;

            

            Rectangle targetRectangle = new Rectangle(0, 0, targetBitmapWidth, targetBitmapHeight);

            

            BitmapData targetBitmapData = targetBitmap.LockBits(targetRectangle, ImageLockMode.ReadWrite, targetBitmap.PixelFormat);

            

            IntPtr targetBitmapHandle = targetBitmapData.Scan0;

 

            int totalPixelCount = targetBitmapWidth * targetBitmapHeight;

            int totalByteCount  = totalPixelCount * 3;

 

            int totalByteCountPerLine  = targetBitmapData.Stride;

            int actualByteCountPerLine = targetBitmapWidth * 3;

 

            int alignmentByteCount = totalByteCountPerLine - actualByteCountPerLine;

 

            totalByteCount += targetBitmapHeight * alignmentByteCount;

 

            byte[] sourceRGBArray = new byte[totalByteCount];

            

            Marshal.Copy(targetBitmapHandle, sourceRGBArray, 0, totalByteCount);

            

            byte[,,] targetRGBArray        = new byte[targetBitmapWidth, targetBitmapHeight, 3];

            float[,] targetBrightnessArray = new float[targetBitmapWidth,targetBitmapHeight];

            

            int sourceIndex = 0;

            

            for(int y = 0; y < targetBitmapHeight; y++)

            {

                for(int x = 0; x < targetBitmapWidth; x++)

                {

                    targetRGBArray[x, y, 0] = sourceRGBArray[sourceIndex + 2]; // Red

                    targetRGBArray[x, y, 1] = sourceRGBArray[sourceIndex + 1]; // Green

                    targetRGBArray[x, y, 2] = sourceRGBArray[sourceIndex + 0]; // Blue

 

                    targetBrightnessArray[x, y] = Color.FromArgb

                    (

                        sourceRGBArray[sourceIndex + 2],

                        sourceRGBArray[sourceIndex + 1],

                        sourceRGBArray[sourceIndex + 0]

                    ).GetBrightness();

                    

                    sourceIndex += 3;

                }

                

                sourceIndex += alignmentByteCount;

            }

 

            float lowerLimit = (float)this.brightnessLowerLimitNumericUpDown.Value;

            float upperLimit = (float)this.brightnessUpperLimitNumericUpDown.Value;

            

            float maximumValue = 0;

            

            for(int y = 1; y < targetBitmapHeight - 1; y++)

            {

                for(int x = 1; x < targetBitmapWidth - 1; x++)

                {

                    maximumValue = Math.Abs(targetBrightnessArray[x - 1, y - 1] - targetBrightnessArray[x + 1, y + 1]);

                    

                    if(maximumValue < Math.Abs(targetBrightnessArray[x - 1, y + 1] - targetBrightnessArray[x + 1, y - 1]))

                    {

                        maximumValue = Math.Abs(targetBrightnessArray[x - 1, y + 1] - targetBrightnessArray[x + 1, y - 1]);

                    }

                    

                    if(maximumValue < Math.Abs(targetBrightnessArray[x, y + 1] - targetBrightnessArray[x, y - 1]))

                    {

                        maximumValue = Math.Abs(targetBrightnessArray[x, y + 1] - targetBrightnessArray[x, y - 1]);

                    }

                    

                    if(maximumValue < Math.Abs(targetBrightnessArray[x - 1, y] - targetBrightnessArray[x + 1, y]))

                    {

                        maximumValue = Math.Abs(targetBrightnessArray[x - 1, y] - targetBrightnessArray[x + 1, y]);

                    }

                    

                    if(this.invertEdgeColorCheckBox.Checked)

                    {

                        if(maximumValue < lowerLimit)

                        {

                            targetRGBArray[x, y, 0] = (byte)255;

                            targetRGBArray[x, y, 1] = (byte)255;

                            targetRGBArray[x, y, 2] = (byte)255;

                        }

                        else if(maximumValue > upperLimit)

                        {

                            targetRGBArray[x, y, 0] = (byte)0;

                            targetRGBArray[x, y, 1] = (byte)0;

                            targetRGBArray[x, y, 2] = (byte)0;

                        }

                    }

                    else

                    {

                        if(maximumValue < lowerLimit)

                        {

                            targetRGBArray[x ,y, 0] = (byte)0;

                            targetRGBArray[x ,y, 1] = (byte)0;

                            targetRGBArray[x ,y, 2] = (byte)0;

                        }

                        else if(maximumValue>upperLimit)

                        {

                            targetRGBArray[x, y, 0] = (byte)255;

                            targetRGBArray[x, y, 1] = (byte)255;

                            targetRGBArray[x, y, 2] = (byte)255;

                        }

                    }

                }

            }

            

            if(this.blackWhiteRadioButton.Checked)

            {

                for(int y = 1; y < targetBitmapHeight - 1; y++)

                {

                    for(int x = 1; x < targetBitmapWidth - 1; x++)

                    {

                        if(this.invertEdgeColorCheckBox.Checked)

                        {

                            if(targetRGBArray[x, y, 0] < 255 || targetRGBArray[x, y, 1] < 255 || targetRGBArray[x, y, 2] < 255)

                            {

                                targetRGBArray[x, y, 0] = targetRGBArray[x, y, 1] = targetRGBArray[x, y, 2] = (byte)0;

                            }

                        }

                        else

                        {

                            if(targetRGBArray[x, y, 0] > 0 || targetRGBArray[x, y, 1] > 0 || targetRGBArray[x, y, 2] > 0)

                            {

                                targetRGBArray[x, y, 0] = targetRGBArray[x, y, 1] = targetRGBArray[x, y, 2] = (byte)255;

                            }

                        }

                    }

                }

            }

            

            if(this.grayscaleRadioButton.Checked)

            {

                for(int y = 1; y < targetBitmapHeight - 1; y++)

                {

                    for(int x = 1; x < targetBitmapWidth - 1; x++)

                    {

                        byte brightness = (byte)((0.299 * targetRGBArray[x, y, 0]) + (0.587 * targetRGBArray[x, y, 1]) +

                            (0.114 * targetRGBArray[x, y, 2]));

 

                        targetRGBArray[x, y, 0] = targetRGBArray[x, y, 1] = targetRGBArray[x, y, 2] = brightness;

                    }

                }

            }

            

            sourceIndex = 0;

            

            for(int y = 0; y < targetBitmapHeight; y++)

            {

                for(int x = 0; x < targetBitmapWidth; x++)

                {

                    sourceRGBArray[sourceIndex + 2] = targetRGBArray[x, y, 0]; // Red

                    sourceRGBArray[sourceIndex + 1] = targetRGBArray[x, y, 1]; // Green

                    sourceRGBArray[sourceIndex + 0] = targetRGBArray[x, y, 2]; // Blue

                    

                    sourceIndex += 3;

                }

                

                sourceIndex += alignmentByteCount;

            }

            

            Marshal.Copy(sourceRGBArray, 0, targetBitmapHandle, totalByteCount);

            

            targetBitmap.UnlockBits(targetBitmapData);

            

            this.targetPictureBox.Image = targetBitmap;

        }

 

        #endregion

 

        #region 이미지 처리하기 - ProcessImage()

 

        /// <summary>

        /// 이미지 처리하기

        /// </summary>

        private void ProcessImage()

        {

            if(this.sourceBitmap != null)

            {

                DetectImageContour();

            }

        }

 

        #endregion

    }

}

 

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

728x90
반응형
Posted by 사용자 icodebroker

댓글을 달아 주세요