첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ MorphologyType.cs

namespace TestProject
{
    /// <summary>
    /// 모폴로지 타입
    /// </summary>
    public enum MorphologyType
    {
        /// <summary>
        /// 침식
        /// </summary>
        Erosion,

        /// <summary>
        /// 팽창
        /// </summary>
        Dilation
    }
}

 

728x90

 

▶ BoundaryExtractionFilterType.cs

namespace TestProject
{
    /// <summary>
    /// 경계 추출 필터 타입
    /// </summary>
    public enum BoundaryExtractionFilterType
    {
        /// <summary>
        /// 경계 추출
        /// </summary>
        BoundaryExtraction,

        /// <summary>
        /// 경계 선명
        /// </summary>
        BoundarySharpen,

        /// <summary>
        /// 경계 추적
        /// </summary>
        BoundaryTrace
    }
}

 

300x250

 

▶ BitmapHelper.cs

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace TestProject
{
    /// <summary>
    /// 비트맵 헬퍼
    /// </summary>
    public static class BitmapHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 비트맵 로드하기 - LoadBitmap(filePath)

        /// <summary>
        /// 비트맵 로드하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>비트맵</returns>
        public static Bitmap LoadBitmap(string filePath)
        {
            using(Bitmap bitmap = new Bitmap(filePath))
            {
                return new Bitmap(bitmap);
            }
        }

        #endregion
        #region 경계 추출 필터 적용하기 - ApplyBoundaryExtractionFilter(sourceBitmap, structureElementArray, filterType, applyBlue, applyGreen, applyRed)

        /// <summary>
        /// 경계 추출 필터 적용하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="structureElementArray">구조 요소 배열</param>
        /// <param name="filterType">경계 추출 필터 타입</param>
        /// <param name="applyBlue">청색 채널 적용 여부</param>
        /// <param name="applyGreen">녹색 채널 적용 여부</param>
        /// <param name="applyRed">적색 채널 적용 여부</param>
        /// <returns>비트맵</returns>
        public static Bitmap ApplyBoundaryExtractionFilter
        (
            Bitmap                       sourceBitmap,
            bool[,]                      structureElementArray,
            BoundaryExtractionFilterType filterType,
            bool                         applyBlue  = true,
            bool                         applyGreen = true,
            bool                         applyRed   = true
        )
        {
            Bitmap targetBitmap = null;

            if(filterType == BoundaryExtractionFilterType.BoundaryExtraction)
            {
                targetBitmap = GetBoundaryExtractionBitmap
                (
                    sourceBitmap,
                    structureElementArray,
                    applyBlue,
                    applyGreen,
                    applyRed
                );
            }
            else if(filterType == BoundaryExtractionFilterType.BoundarySharpen)
            {
                targetBitmap = GetBoundarySharpenBitmap
                (
                    sourceBitmap,
                    structureElementArray,
                    applyBlue,
                    applyGreen,
                    applyRed
                );
            }
            else if(filterType == BoundaryExtractionFilterType.BoundaryTrace)
            {
                targetBitmap = GetBoundaryTraceBitmap
                (
                    sourceBitmap,
                    structureElementArray,
                    applyBlue,
                    applyGreen,
                    applyRed
                );
            }

            return targetBitmap;
        }

        #endregion

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

        #region 모폴로지 연산 적용하기 - ApplyMorphologyOperation(sourceBitmap, structuringElementArray, morphologyType, applyBlue, applyGreen, applyRed)

        /// <summary>
        /// 모폴로지 연산 적용하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="structuringElementArray">구조 요소 배열</param>
        /// <param name="morphologyType">모폴로지 타입</param>
        /// <param name="applyBlue">청색 채널 적용 여부</param>
        /// <param name="applyGreen">녹색 채널 적용 여부</param>
        /// <param name="applyRed">적색 채널 적용 여부</param>
        /// <returns>비트맵</returns>
        private static Bitmap ApplyMorphologyOperation
        (
            Bitmap         sourceBitmap,
            bool[,]        structuringElementArray,
            MorphologyType morphologyType,
            bool           applyBlue  = true,
            bool           applyGreen = true,
            bool           applyRed   = true
        )
        {
            BitmapData sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            byte[] sourceArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];
            byte[] targetArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];

            Marshal.Copy(sourceBitmapData.Scan0, sourceArray, 0, sourceArray.Length);

            sourceBitmap.UnlockBits(sourceBitmapData);

            int  filterOffset = (structuringElementArray.GetLength(0) - 1) / 2;
            int  sourceOffset = 0;
            int  targetOffset = 0;
            byte blueErode    = 0;
            byte greenErode   = 0;
            byte redErode     = 0;
            byte blueDilate   = 0;
            byte greenDilate  = 0;
            byte redDilate    = 0;

            for(int offsetY = 0; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
            {
                for(int offsetX = 0; offsetX < sourceBitmap.Width - filterOffset; offsetX++)
                {
                    targetOffset = offsetY * sourceBitmapData.Stride + offsetX * 4;

                    blueErode  = 255;
                    greenErode = 255;
                    redErode   = 255;

                    blueDilate  = 0;
                    greenDilate = 0;
                    redDilate   = 0;

                    for(int filterY = -filterOffset; filterY <= filterOffset; filterY++)
                    {
                        for(int filterX = -filterOffset; filterX <= filterOffset; filterX++)
                        {
                            if(structuringElementArray[filterY + filterOffset, filterX + filterOffset] == true)
                            {
                                sourceOffset = targetOffset + (filterX * 4) + (filterY * sourceBitmapData.Stride);
                                sourceOffset = (sourceOffset < 0 ? 0 : (sourceOffset >= sourceArray.Length + 2 ? sourceArray.Length - 3 : sourceOffset));

                                blueDilate  = (sourceArray[sourceOffset    ] > blueDilate  ? sourceArray[sourceOffset    ] : blueDilate );
                                greenDilate = (sourceArray[sourceOffset + 1] > greenDilate ? sourceArray[sourceOffset + 1] : greenDilate);
                                redDilate   = (sourceArray[sourceOffset + 2] > redDilate   ? sourceArray[sourceOffset + 2] : redDilate  );

                                blueErode  = (sourceArray[sourceOffset    ] < blueErode  ? sourceArray[sourceOffset    ] : blueErode );
                                greenErode = (sourceArray[sourceOffset + 1] < greenErode ? sourceArray[sourceOffset + 1] : greenErode);
                                redErode   = (sourceArray[sourceOffset + 2] < redErode   ? sourceArray[sourceOffset + 2] : redErode  );
                            }
                        }
                    }

                    blueErode  = (applyBlue ? blueErode  : sourceArray[targetOffset]);
                    blueDilate = (applyBlue ? blueDilate : sourceArray[targetOffset]);

                    greenErode  = (applyGreen ? greenErode  : sourceArray[targetOffset + 1]);
                    greenDilate = (applyGreen ? greenDilate : sourceArray[targetOffset + 1]);

                    redErode  = (applyRed ? redErode  : sourceArray[targetOffset + 2]);
                    redDilate = (applyRed ? redDilate : sourceArray[targetOffset + 2]);

                    if(morphologyType == MorphologyType.Erosion)
                    {
                        targetArray[targetOffset    ] = blueErode;
                        targetArray[targetOffset + 1] = greenErode;
                        targetArray[targetOffset + 2] = redErode;
                    }
                    else if(morphologyType == MorphologyType.Dilation)
                    {
                        targetArray[targetOffset    ] = blueDilate;
                        targetArray[targetOffset + 1] = greenDilate;
                        targetArray[targetOffset + 2] = redDilate;
                    }

                    targetArray[targetOffset + 3] = 255;
                }
            }

            Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);

            BitmapData targetBitmapData = targetBitmap.LockBits
            (
                new Rectangle(0, 0, targetBitmap.Width, targetBitmap.Height),
                ImageLockMode.WriteOnly,
                PixelFormat.Format32bppArgb
            );

            Marshal.Copy(targetArray, 0, targetBitmapData.Scan0, targetArray.Length);

            targetBitmap.UnlockBits(targetBitmapData);

            return targetBitmap;
        }

        #endregion
        #region 색상 빼기 - SubtractColor(color1, color2)

        /// <summary>
        /// 색상 빼기
        /// </summary>
        /// <param name="color1">색상 1</param>
        /// <param name="color2">색상 2</param>
        /// <returns>색상</returns>
        private static byte SubtractColor(byte color1, byte color2)
        {
            int target = (int)color1 - (int)color2;

            return (byte)(target < 0 ? 0 : target);
        }

        #endregion
        #region 이미지 빼기 - SubtractImage(sourceBitmap, subtractBitmap)

        /// <summary>
        /// 이미지 빼기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="subtractBitmap">차감 비트맵</param>
        /// <returns>비트맵</returns>
        private static Bitmap SubtractImage(Bitmap sourceBitmap, Bitmap subtractBitmap)
        {
            BitmapData sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            byte[] targetByteArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];

            Marshal.Copy(sourceBitmapData.Scan0, targetByteArray, 0, targetByteArray.Length);

            sourceBitmap.UnlockBits(sourceBitmapData);

            BitmapData subtractBitmapData = subtractBitmap.LockBits
            (
                new Rectangle(0, 0, subtractBitmap.Width, subtractBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            byte[] subtractByteArray = new byte[subtractBitmapData.Stride * subtractBitmapData.Height];

            Marshal.Copy(subtractBitmapData.Scan0, subtractByteArray, 0, subtractByteArray.Length);

            subtractBitmap.UnlockBits(subtractBitmapData);

            for(int  k = 0; k + 4 < targetByteArray.Length && k + 4 < subtractByteArray.Length; k += 4)
            {
                targetByteArray[k    ] = SubtractColor(targetByteArray[k    ], subtractByteArray[k    ]);
                targetByteArray[k + 1] = SubtractColor(targetByteArray[k + 1], subtractByteArray[k + 1]);
                targetByteArray[k + 2] = SubtractColor(targetByteArray[k + 2], subtractByteArray[k + 2]);
                targetByteArray[k + 3] = 255;
            }

            Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);

            BitmapData targetBitmapData = targetBitmap.LockBits
            (
                new Rectangle (0, 0, targetBitmap.Width, targetBitmap.Height),
                ImageLockMode.WriteOnly,
                PixelFormat.Format32bppArgb
            );

            Marshal.Copy(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);

            targetBitmap.UnlockBits(targetBitmapData);

            return targetBitmap;
        }

        #endregion
        #region 경계 추출 비트맵 구하기 - GetBoundaryExtractionBitmap(sourceBitmap, structuringElementArray, applyBlue, applyGreen, applyRed)

        /// <summary>
        /// 경계 추출 비트맵 구하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="structuringElementArray">구조 요소 배열</param>
        /// <param name="applyBlue">청색 채널 적용 여부</param>
        /// <param name="applyGreen">녹색 채널 적용 여부</param>
        /// <param name="applyRed">적색 채널 적용 여부</param>
        /// <returns>비트맵</returns>
        private static Bitmap GetBoundaryExtractionBitmap
        (
            Bitmap  sourceBitmap,
            bool[,] structuringElementArray,
            bool    applyBlue  = true,
            bool    applyGreen = true,
            bool    applyRed   = true
        )
        {
            Bitmap resultBitmap = ApplyMorphologyOperation
            (
                sourceBitmap,
                structuringElementArray,
                MorphologyType.Dilation,
                applyBlue,
                applyGreen,
                applyRed
            );

            resultBitmap = SubtractImage(resultBitmap, sourceBitmap);

            return resultBitmap;
        }

        #endregion
        #region 경계 선명 비트맵 구하기 - GetBoundarySharpenBitmap(sourceBitmap, structuringElementArray, applyBlue, applyGreen, applyRed)

        /// <summary>
        /// 경계 선명 비트맵 구하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="structuringElementArray">구조 요소 배열</param>
        /// <param name="applyBlue">청색 채널 적용 여부</param>
        /// <param name="applyGreen">녹색 채널 적용 여부</param>
        /// <param name="applyRed">적색 채널 적용 여부</param>
        /// <returns>비트맵</returns>
        private static Bitmap GetBoundarySharpenBitmap
        (
            Bitmap  sourceBitmap,
            bool[,] structuringElementArray,
            bool    applyBlue  = true,
            bool    applyGreen = true,
            bool    applyRed   = true
        )
        {
            Bitmap targetBitmap = GetBoundaryExtractionBitmap
            (
                sourceBitmap,
                structuringElementArray,
                applyBlue,
                applyGreen,
                applyRed
            );

            targetBitmap = AddImage
            (
                ApplyMorphologyOperation
                (
                    sourceBitmap,
                    structuringElementArray,
                    MorphologyType.Dilation,
                    applyBlue,
                    applyGreen,
                    applyRed
                ),
                targetBitmap
            );

            return targetBitmap;
        }

        #endregion
        #region 색상 더하기 - AddColor(color1, color2)

        /// <summary>
        /// 색상 더하기
        /// </summary>
        /// <param name="color1">색상 1</param>
        /// <param name="color2">색상 2</param>
        /// <returns>색상</returns>
        private static byte AddColor(byte color1, byte color2)
        {
            int target = color1 + color2;

            return (byte)(target < 0 ? 0 : (target > 255 ? 255 : target));
        }

        #endregion
        #region 이미지 더하기 - AddImage(sourceBitmap, addBitmap)

        /// <summary>
        /// 이미지 더하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="addBitmap">가감 비트맵</param>
        /// <returns>비트맵</returns>
        private static Bitmap AddImage(Bitmap sourceBitmap, Bitmap addBitmap)
        {
            BitmapData sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle (0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            byte[] targetByteArray = new byte[sourceBitmapData.Stride * sourceBitmapData.Height];

            Marshal.Copy(sourceBitmapData.Scan0, targetByteArray, 0, targetByteArray.Length);

            sourceBitmap.UnlockBits(sourceBitmapData);

            BitmapData addBitmapData = addBitmap.LockBits
            (
                new Rectangle(0, 0, addBitmap.Width, addBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

            byte[] addByteArray = new byte[addBitmapData.Stride * addBitmapData.Height];

            Marshal.Copy(addBitmapData.Scan0, addByteArray, 0, addByteArray.Length);

            addBitmap.UnlockBits(addBitmapData);

            for(int k = 0; k + 4 < targetByteArray.Length && k + 4 < addByteArray.Length; k += 4)
            {
                targetByteArray[k    ] = AddColor(targetByteArray[k    ], addByteArray[k    ]);
                targetByteArray[k + 1] = AddColor(targetByteArray[k + 1], addByteArray[k + 1]);
                targetByteArray[k + 2] = AddColor(targetByteArray[k + 2], addByteArray[k + 2]);
                targetByteArray[k + 3] = 255;
            }

            Bitmap targetBitmap = new Bitmap(sourceBitmap.Width, sourceBitmap.Height);

            BitmapData targetBitmapData = targetBitmap.LockBits
            (
                new Rectangle(0, 0, targetBitmap.Width, targetBitmap.Height),
                ImageLockMode.WriteOnly,
                PixelFormat.Format32bppArgb
            );

            Marshal.Copy(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);

            targetBitmap.UnlockBits(targetBitmapData);

            return targetBitmap;
        }

        #endregion
        #region 경계 추적 비트맵 구하기 - GetBoundaryTraceBitmap(sourceBitmap, structuringElementArray, applyBlue, applyGreen, applyRed)

        /// <summary>
        /// 경계 추적 비트맵 구하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="structuringElementArray">구조 요소 배열</param>
        /// <param name="applyBlue">청색 채널 적용 여부</param>
        /// <param name="applyGreen">녹색 채널 적용 여부</param>
        /// <param name="applyRed">적색 채널 적용 여부</param>
        /// <returns>비트맵</returns>
        private static Bitmap GetBoundaryTraceBitmap
        (
            Bitmap  sourceBitmap,
            bool[,] structuringElementArray,
            bool    applyBlue  = true,
            bool    applyGreen = true,
            bool    applyRed   = true
        )
        {
            Bitmap targetBitmap = GetBoundaryExtractionBitmap
            (
                sourceBitmap,
                structuringElementArray,
                applyBlue,
                applyGreen,
                applyRed
            );

            targetBitmap = SubtractImage(sourceBitmap, targetBitmap);

            return targetBitmap;
        }

        #endregion
    }
}

 

▶ MainForm.cs

using System.Drawing;
using System.Windows.Forms;

namespace TestProject
{
    /// <summary>
    /// 메인 폼
    /// </summary>
    public partial class MainForm : Form
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainForm()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainForm()
        {
            InitializeComponent();

            Bitmap sourceBitmap = BitmapHelper.LoadBitmap("IMAGE\\sample.jpg");
            Bitmap targetBitmap = BitmapHelper.ApplyBoundaryExtractionFilter
            (
                sourceBitmap,
                new bool[3, 3]
                {
                    { true, true, true },
                    { true, true, true },
                    { true, true, true }
                },
                BoundaryExtractionFilterType.BoundaryExtraction,
                true,
                true,
                false
            );

            this.pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
            this.pictureBox.Image    = targetBitmap;
        }

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요