첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
본 블로그는 광고를 포함하고 있습니다.
광고 클릭에서 발생하는 수익금은 모두 블로그 콘텐츠 향상을 위해 쓰여집니다.

728x90
반응형
728x170

TestProject.zip
다운로드

▶ BitmapHelper.cs

using System;
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 카툰 필터 적용하기 - ApplyCartoonFilter(sourceBitmap, level, filterSize, threshold)

        /// <summary>
        /// 카툰 필터 적용하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="level">레벨</param>
        /// <param name="filterSize">필터 크기</param>
        /// <param name="threshold">임계치</param>
        /// <returns>비트맵</returns>
        public static Bitmap ApplyCartoonFilter(Bitmap sourceBitmap, int level, int filterSize, byte threshold)
        {
            Bitmap oilPaintBitmap = ApplyOilPaintFilter(sourceBitmap, level, filterSize);
            Bitmap edgeBitmap     = ApplyGradientBasedEdgeDetectionFilter(sourceBitmap, threshold);

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

            byte[] oilPaintByteArray = new byte[oilPaintBitmapData.Stride * oilPaintBitmapData.Height];

            Marshal.Copy(oilPaintBitmapData.Scan0, oilPaintByteArray, 0, oilPaintByteArray.Length);

            oilPaintBitmap.UnlockBits(oilPaintBitmapData);

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

            byte[] edgeByteArray = new byte[edgeBitmapData.Stride * edgeBitmapData.Height];

            Marshal.Copy(edgeBitmapData.Scan0, edgeByteArray, 0, edgeByteArray.Length);

            edgeBitmap.UnlockBits(edgeBitmapData);

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

            for(int k = 0; k + 4 < oilPaintByteArray.Length; k += 4)
            {
                if(edgeByteArray[k] == 255 || edgeByteArray[k + 1] == 255 || edgeByteArray[k + 2] == 255)
                {
                    targetByteArray[k    ] = 0;
                    targetByteArray[k + 1] = 0;
                    targetByteArray[k + 2] = 0;
                    targetByteArray[k + 3] = 255;
                }
                else
                {
                    targetByteArray[k    ] = oilPaintByteArray[k    ];
                    targetByteArray[k + 1] = oilPaintByteArray[k + 1];
                    targetByteArray[k + 2] = oilPaintByteArray[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

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

        #region 바이트 자르기 - ClipByte(value)

        /// <summary>
        /// 바이트 자르기
        /// </summary>
        /// <param name="value">값</param>
        /// <returns>바이트</returns>
        private static byte ClipByte(double value)
        {
            return (byte)(value > 255 ? 255 : (value < 0 ? 0 : value));
        }

        #endregion
        #region 임계치 체크하기 - CheckThreshold(sourceByteArray, offset1, offset2, gradientValue, threshold, divideBy)

        /// <summary>
        /// 임계치 체크하기
        /// </summary>
        /// <param name="sourceByteArray">소스 바이트 배열</param>
        /// <param name="offset1">오프셋 1</param>
        /// <param name="offset2">오프셋 2</param>
        /// <param name="gradientValue">그라디언트 값</param>
        /// <param name="threshold">임계치</param>
        /// <param name="divideBy">젯수</param>
        /// <returns>임계치 체크 결과</returns>
        private static bool CheckThreshold
        (
            byte[]  sourceByteArray,
            int     offset1,
            int     offset2,
            ref int gradientValue,
            byte    threshold,
            int     divideBy = 1
        )
        {
            gradientValue += Math.Abs(sourceByteArray[offset1    ] - sourceByteArray[offset2    ]) / divideBy;
            gradientValue += Math.Abs(sourceByteArray[offset1 + 1] - sourceByteArray[offset2 + 1]) / divideBy;
            gradientValue += Math.Abs(sourceByteArray[offset1 + 2] - sourceByteArray[offset2 + 2]) / divideBy;

            return (gradientValue >= threshold);
        }

        #endregion
        #region 오일 페인트 필터 적용하기 - ApplyOilPaintFilter(sourceBitmap, level, filterSize)

        /// <summary>
        /// 오일 페인트 필터 적용하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="level">레벨</param>
        /// <param name="filterSize">필터 크기</param>
        /// <returns>비트맵</returns>
        private static Bitmap ApplyOilPaintFilter(Bitmap sourceBitmap, int level, int filterSize)
        {
            BitmapData sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

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

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

            sourceBitmap.UnlockBits(sourceBitmapData);

            int[] intensityArray = new int [level];
            int[] blueArray      = new int [level];
            int[] greenArray     = new int [level];
            int[] redArray       = new int [level];

            level = level - 1;

            int filterOffset     = (filterSize - 1) / 2;
            int targetOffset     = 0;
            int sourceOffset     = 0;
            int currentIntensity = 0;
            int maximumIntensity = 0;
            int maximumIndex     = 0;

            double blue  = 0;
            double green = 0;
            double red   = 0;

            for(int offsetY = filterOffset; offsetY < sourceBitmap.Height - filterOffset; offsetY++)
            {
                for(int offsetX = filterOffset; offsetX < sourceBitmap.Width - filterOffset; offsetX++)
                {
                    blue = green = red = 0;

                    currentIntensity = maximumIntensity = maximumIndex = 0;

                    intensityArray = new int[level + 1];
                    blueArray      = new int[level + 1];
                    greenArray     = new int[level + 1];
                    redArray       = new int[level + 1];

                    targetOffset = offsetY * sourceBitmapData.Stride + offsetX * 4;

                    for(int filterY = -filterOffset; filterY <= filterOffset; filterY++)
                    {
                        for(int filterX = -filterOffset; filterX <= filterOffset; filterX++)
                        {
                            sourceOffset = targetOffset + (filterX * 4) + (filterY * sourceBitmapData.Stride);

                            currentIntensity = (int )Math.Round
                            (
                                ((double)(sourceByteArray[sourceOffset    ] +
                                          sourceByteArray[sourceOffset + 1] +
                                          sourceByteArray[sourceOffset + 2]) / 3.0 * (level)) / 255.0
                            );

                            intensityArray[currentIntensity] += 1;

                            blueArray [currentIntensity] += sourceByteArray[sourceOffset    ];
                            greenArray[currentIntensity] += sourceByteArray[sourceOffset + 1];
                            redArray  [currentIntensity] += sourceByteArray[sourceOffset + 2];

                            if(intensityArray[currentIntensity] > maximumIntensity)
                            {
                                maximumIntensity = intensityArray[currentIntensity];

                                maximumIndex = currentIntensity;
                            }
                        }
                    }

                    blue  = blueArray [maximumIndex] / maximumIntensity;
                    green = greenArray[maximumIndex] / maximumIntensity;
                    red   = redArray  [maximumIndex] / maximumIntensity;

                    targetByteArray[targetOffset    ] = ClipByte(blue);
                    targetByteArray[targetOffset + 1] = ClipByte(green);
                    targetByteArray[targetOffset + 2] = ClipByte(red);
                    targetByteArray[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(targetByteArray, 0, targetBitmapData.Scan0, targetByteArray.Length);

            targetBitmap.UnlockBits(targetBitmapData);

            return targetBitmap;
        }

        #endregion
        #region 그라디언트 기반 가장자리 탐지 필터 적용하기 - ApplyGradientBasedEdgeDetectionFilter(sourceBitmap, threshold)

        /// <summary>
        /// 그라디언트 기반 가장자리 탐지 필터 적용하기
        /// </summary>
        /// <param name="sourceBitmap">소스 비트맵</param>
        /// <param name="threshold">임계치</param>
        /// <returns>비트맵</returns>
        private static Bitmap ApplyGradientBasedEdgeDetectionFilter(Bitmap sourceBitmap, byte threshold = 0)
        {
            BitmapData sourceBitmapData = sourceBitmap.LockBits
            (
                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),
                ImageLockMode.ReadOnly,
                PixelFormat.Format32bppArgb
            );

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

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

            sourceBitmap.UnlockBits(sourceBitmapData);

            int  offset    = 0;
            int  gradientValue   = 0;
            bool exceedThreshold = false;

            for(int offsetY = 1; offsetY < sourceBitmap.Height - 1; offsetY++)
            {
                for(int offsetX = 1; offsetX < sourceBitmap.Width - 1; offsetX++)
                {
                    offset          = offsetY * sourceBitmapData.Stride + offsetX * 4;
                    gradientValue   = 0;
                    exceedThreshold = true;

                    CheckThreshold
                    (
                        sourceByteArray,
                        offset - 4,
                        offset + 4,
                        ref gradientValue,
                        threshold,
                        2
                    );

                    exceedThreshold = CheckThreshold
                    (
                        sourceByteArray,
                        offset - sourceBitmapData.Stride,
                        offset + sourceBitmapData.Stride,
                        ref gradientValue,
                        threshold,
                        2
                    );

                    if(exceedThreshold == false)
                    {
                        gradientValue = 0;

                        exceedThreshold = CheckThreshold
                        (
                            sourceByteArray,
                            offset - 4,
                            offset + 4,
                            ref gradientValue,
                            threshold
                        );

                        if(exceedThreshold == false)
                        {
                            gradientValue = 0;

                            exceedThreshold = CheckThreshold
                            (
                                sourceByteArray,
                                offset - sourceBitmapData.Stride,
                                offset + sourceBitmapData.Stride,
                                ref gradientValue,
                                threshold
                            );

                            if(exceedThreshold == false)
                            {
                                gradientValue = 0;

                                CheckThreshold
                                (
                                    sourceByteArray,
                                    offset - 4 - sourceBitmapData.Stride,
                                    offset + 4 + sourceBitmapData.Stride,
                                    ref gradientValue,
                                    threshold,
                                    2
                                );

                                exceedThreshold = CheckThreshold
                                (
                                    sourceByteArray,
                                    offset - sourceBitmapData.Stride + 4,
                                    offset - 4 + sourceBitmapData.Stride,
                                    ref gradientValue,
                                    threshold,
                                    2
                                );

                                if(exceedThreshold == false)
                                {
                                    gradientValue = 0;

                                    exceedThreshold = CheckThreshold
                                    (
                                        sourceByteArray,
                                        offset - 4 - sourceBitmapData.Stride,
                                        offset + 4 + sourceBitmapData.Stride,
                                        ref gradientValue,
                                        threshold
                                    );

                                    if(exceedThreshold == false)
                                    {
                                        gradientValue = 0;

                                        exceedThreshold = CheckThreshold
                                        (
                                            sourceByteArray,
                                            offset - sourceBitmapData.Stride + 4,
                                            offset + sourceBitmapData.Stride - 4,
                                            ref gradientValue,
                                            threshold
                                        );
                                    }
                                }
                            }
                        }
                    }

                    targetByteArray[offset    ] = (byte)(exceedThreshold ? 255 : 0);
                    targetByteArray[offset + 1] = targetByteArray[offset];
                    targetByteArray[offset + 2] = targetByteArray[offset];
                    targetByteArray[offset + 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
    }
}

 

728x90

 

▶ 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.ApplyCartoonFilter(sourceBitmap, 20, 5, 250);

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

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

댓글을 달아 주세요