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

■ 스캔 이미지 왜곡 자동 보정하기

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


TestProject.zip


StripInfo.cs

 

 

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

 

namespace TestProject

{

    /// <summary>

    /// 스트립 정보

    /// </summary>

    [Serializable]

    public class StripInfo

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 스트립 배열

        /// </summary>

        public int[][] StripArray { get; private set; }

 

        /// <summary>

        /// 너비

        /// </summary>

        public int Width { get; private set; }

 

        /// <summary>

        /// 높이

        /// </summary>

        public int Height { get; private set; }

 

        /// <summary>

        /// 스케일

        /// </summary>

        public float Scale { get; private set; }

 

        /// <summary>

        /// 소스 너비

        /// </summary>

        public int SourceWidth { get; private set; }

 

        /// <summary>

        /// 소스 높이

        /// </summary>

        public int SourceHeight { get; private set; }

 

        /// <summary>

        /// 수직 여부

        /// </summary>

        public bool IsVertical { get; private set; }

 

        /// <summary>

        /// 최대 높이

        /// </summary>

        public const int MAXIMUM_HEIGHT = 600;

 

        #endregion

 

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

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

 

        #region 생성자 - StripInfo(sourceBitmap, stripCount, isVertical)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="sourceBitmap">소스 비트맵</param>

        /// <param name="stripCount">스트립 카운트</param>

        /// <param name="isVertical">수직 여부</param>

        public StripInfo(Bitmap sourceBitmap, int stripCount, bool isVertical = false)

        {

            SourceWidth  = sourceBitmap.Width;

            SourceHeight = sourceBitmap.Height;

            IsVertical   = isVertical;

 

            Scale = SourceHeight > MAXIMUM_HEIGHT ? 1f * MAXIMUM_HEIGHT / SourceHeight : 1f;

 

            Width  = stripCount;

            Height = (int)((IsVertical ? SourceWidth : SourceHeight) * Scale);

 

            int targetWidth  = isVertical ? Height : Width;

            int targetHeight = isVertical ? Width  : Height;

 

            using(Bitmap targetBitmap = new Bitmap(targetWidth, targetHeight))

            {

                using(Graphics targetGraphics = Graphics.FromImage(targetBitmap))

                {

                    targetGraphics.InterpolationMode = InterpolationMode.Low;

 

                    targetGraphics.DrawImage(sourceBitmap, 0, 0, targetWidth, targetHeight);

 

                    using(BitmapWrapper bitmapWrapper = new BitmapWrapper(targetBitmap, true))

                    {

                        StripArray = new int[Width][];

 

                        for(int x = 0; x < StripArray.Length; x++)

                        {

                            int[] stripHeightArray = StripArray[x] = new int[Height];

 

                            if(isVertical)

                            {

                                for(int y = 0; y < stripHeightArray.Length; y++)

                                {

                                    stripHeightArray[y] = bitmapWrapper[y, x].G;

                                }

                            }

                            else

                            {

                                for(int y = 0; y < stripHeightArray.Length; y++)

                                {

                                    stripHeightArray[y] = bitmapWrapper[x, y].G;

                                }

                            }

                        }

                    }

                }

            }

        }

 

        #endregion

    }

}

 

 

BitmapWrapper.cs

 

 

using System;

using System.Collections;

using System.Collections.Generic;

using System.Drawing;

using System.Drawing.Imaging;

using System.Runtime.InteropServices;

 

namespace TestProject

{

    /// <summary>

    /// 비트맵 래퍼

    /// </summary>

    public class BitmapWrapper : IDisposable, IEnumerable<Point>

    {

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

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

 

        #region Field

 

        /// <summary>

        /// 소스 비트맵

        /// </summary>

        private Bitmap sourceBitmap;

 

        /// <summary>

        /// 소스 비트맵 데이터

        /// </summary>

        private BitmapData sourceBitmapData;

 

        /// <summary>

        /// 소스 바이트 배열

        /// </summary>

        private byte[] sourceByteArray;

 

        /// <summary>

        /// 복사 바이트 배열

        /// </summary>

        private byte[] copyByteArray;

 

        /// <summary>

        /// 스트라이드

        /// </summary>

        private int stride;

 

        #endregion

 

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

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

 

        #region 비트맵 너비 - BitmapWidth

 

        /// <summary>

        /// 비트맵 너비

        /// </summary>

        public int BitmapWidth { get; private set; }

 

        #endregion

        #region 비트맵 높이 - BitmapHeight

 

        /// <summary>

        /// 비트맵 높이

        /// </summary>

        public int BitmapHeight { get; private set; }

 

        #endregion

        #region 디폴트 색상 - DefaultColor

 

        /// <summary>

        /// 디폴트 색상

        /// </summary>

        public Color DefaultColor { get; set; }

 

        #endregion

 

        #region 인덱서 - this[x, y]

 

        /// <summary>

        /// 인덱서

        /// </summary>

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

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

        /// <returns>색상</returns>

        public Color this[int x, int y]

        {

            get

            {

                int i = GetIndex(x, y);

 

                return i < 0 ? DefaultColor :

                    Color.FromArgb

                    (

                        sourceByteArray[i + 3],

                        sourceByteArray[i + 2],

                        sourceByteArray[i + 1],

                        sourceByteArray[i    ]

                    );

            }

            set

            {

                int i = GetIndex(x, y);

 

                if(i >= 0)

                {

                    copyByteArray[i    ] = value.B;

                    copyByteArray[i + 1] = value.G;

                    copyByteArray[i + 2] = value.R;

                    copyByteArray[i + 3] = value.A;

                };

            }

        }

 

        #endregion

        #region 인덱서 - this[point]

 

        /// <summary>

        /// 인덱서

        /// </summary>

        /// <param name="point">포인트</param>

        /// <returns>색상</returns>

        public Color this[Point point]

        {

            get

            {

                return this[point.X, point.Y];

            }

            set

            {

                this[point.X, point.Y] = value;

            }

        }

 

        #endregion

 

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

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

 

        #region 생성자 - BitmapWrapper(sourceBitmap, copySourceBitmap)

 

        /// <summary>

        /// 생성자

        /// </summary>

        /// <param name="sourceBitmap">소스 비트맵</param>

        /// <param name="copySourceBitmap">소스 비트맵 복사 여부</param>

        public BitmapWrapper(Bitmap sourceBitmap, bool copySourceBitmap = false)

        {

            BitmapWidth  = sourceBitmap.Width;

            BitmapHeight = sourceBitmap.Height;

 

            this.sourceBitmap = sourceBitmap;

 

            this.sourceBitmapData = sourceBitmap.LockBits

            (

                new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height),

                ImageLockMode.ReadWrite,

                PixelFormat.Format32bppArgb

            );

 

            this.stride = this.sourceBitmapData.Stride;

 

            this.sourceByteArray = new byte[this.stride * BitmapHeight];

 

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

 

            this.copyByteArray = copySourceBitmap ? (byte[])sourceByteArray.Clone() :

                new byte[this.stride * BitmapHeight];

        }

 

        #endregion

 

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

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

 

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

 

        /// <summary>

        /// 픽셀 설정하기

        /// </summary>

        /// <param name="point">포인트</param>

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

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

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

        public void SetPixel(Point point, double red, double green, double blue)

        {

            if(red < 0)

            {

                red = 0;

            }

 

            if(red >= 256)

            {

                red = 255;

            }

 

            if(green < 0)

            {

                green = 0;

            }

 

            if(green >= 256)

            {

                green = 255;

            }

 

            if(blue < 0)

            {

                blue = 0;

            }

 

            if(blue >= 256)

            {

                blue = 255;

            }

 

            this[point.X, point.Y] = Color.FromArgb((int)red, (int)green, (int)blue);

        }

 

        #endregion

        #region 교환하기 - Swap()

 

        /// <summary>

        /// 교환하기

        /// </summary>

        public void Swap()

        {

            byte[] temporaryByteArray = sourceByteArray;

 

            sourceByteArray = copyByteArray;

 

            copyByteArray = temporaryByteArray;

        }

 

        #endregion

        #region 열거자 구하기 - GetEnumerator()

 

        /// <summary>

        /// 열거자 구하기

        /// </summary>

        /// <returns>포인트 열거자</returns>

        public IEnumerator<Point> GetEnumerator()

        {

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

            {

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

                {

                    yield return new Point(x, y);

                }

            }

        }

 

        #endregion

        #region (IEnumerable) 열거자 구하기 - GetEnumerator()

 

        /// <summary>

        /// 열거자 구하기

        /// </summary>

        /// <returns></returns>

        IEnumerator IEnumerable.GetEnumerator()

        {

            return GetEnumerator();

        }

 

        #endregion

        #region 리소스 해제하기 - Dispose()

 

        /// <summary>

        /// 리소스 해제하기

        /// </summary>

        public void Dispose()

        {

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

 

            sourceBitmap.UnlockBits(sourceBitmapData);

        }

 

        #endregion

 

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

 

        #region 인덱스 구하기 - GetIndex(x, y)

 

        /// <summary>

        /// 인덱스 구하기

        /// </summary>

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

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

        /// <returns></returns>

        private int GetIndex(int x, int y)

        {

            return (x < 0 || x >= BitmapWidth || y < 0 || y >= BitmapHeight) ? -1 : x * 4 + y * stride;

        }

 

        #endregion

    }

}

 

 

BitmapHelper.cs

 

 

using System.Drawing;

using System.Drawing.Drawing2D;

 

namespace TestProject

{

    /// <summary>

    /// 비트맵 헬퍼

    /// </summary>

    public static class BitmapHelper

    {

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

        ////////////////////////////////////////////////////////////////////////////////////////// Static

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

 

        #region 비트맵 회전하기 - RotateBitmap(sourceBitmap, angle)

 

        /// <summary>

        /// 비트맵 회전하기

        /// </summary>

        /// <param name="sourceBitmap">소스 비트맵</param>

        /// <param name="angle">각도</param>

        /// <returns>회전 비트맵</returns>

        public static Bitmap RotateBitmap(Bitmap sourceBitmap, double angle)

        {

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

 

            using(Graphics graphics = Graphics.FromImage(targetBitmap))

            {

                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

 

                graphics.TranslateTransform(sourceBitmap.Width / 2, sourceBitmap.Height / 2);

 

                graphics.RotateTransform(-(float)angle);

 

                graphics.DrawImage

                (

                    sourceBitmap,

                    -sourceBitmap.Width  / 2,

                    -sourceBitmap.Height / 2,

                    sourceBitmap.Width,

                    sourceBitmap.Height

                );

            }

 

            return targetBitmap;

        }

 

        #endregion

    }

}

 

 

SkewCalculator.cs

 

 

using System;

 

namespace TestProject

{

    /// <summary>

    /// SKEW 계산기

    /// </summary>

    public static class SkewCalculator

    {

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

        ////////////////////////////////////////////////////////////////////////////////////////// Static

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

 

        #region 회전 각도 구하기 - GetRotateAngle(stripInfo, stripX1, stripX2, maximumSkew)

 

        /// <summary>

        /// 회전 각도 구하기

        /// </summary>

        /// <param name="stripInfo">스트립 정보</param>

        /// <param name="stripX1">스트립 X1</param>

        /// <param name="stripX2">스트립 X2</param>

        /// <param name="maximumSkew">최대 SKEW</param>

        /// <returns></returns>

        public static double GetRotateAngle(StripInfo stripInfo, int stripX1, int stripX2, int maximumSkew = 70)

        {

            double shift  = 0d;

            int    shift1 = FindBestShift(stripInfo, stripX1    , stripX2    , -maximumSkew, maximumSkew);

            int    shift2 = FindBestShift(stripInfo, stripX1 + 1, stripX2 + 1, -maximumSkew, maximumSkew);

 

            if(Math.Abs(shift1 - shift2) < 2)

            {

                shift = (shift1 + shift2) / 2d;

            }

            else

            {

                if(Math.Abs(shift1) < Math.Abs(shift2))

                {

                    shift = shift1;

                }

                else

                {

                    shift = shift2;

                }

            }

 

            return Math.Atan2

            (

                shift / stripInfo.Scale,

                (stripX2 - stripX1) * stripInfo.SourceWidth / stripInfo.Width

            );

        }

 

        #endregion

 

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

 

        #region 정정 값 계산하기 - CalculateCorrection(stripInfo, x1, x2, shift)

 

        /// <summary>

        /// 정정 값 계산하기

        /// </summary>

        /// <param name="stripInfo">스트립 정보</param>

        /// <param name="x1">X1</param>

        /// <param name="x2">X2</param>

        /// <param name="shift">시프트</param>

        /// <returns>정정 값</returns>

        private static double CalculateCorrection(StripInfo stripInfo, int x1, int x2, int shift)

        {

            int startIndex = shift < 0 ? -shift : 0;

            int endIndex   = shift > 0 ? stripInfo.Height - shift - 1 : stripInfo.Height - 1;

 

            int[] stripArray1 = stripInfo.StripArray[x1];

            int[] stripArray2 = stripInfo.StripArray[x2];

 

            int res = 0;

 

            for(int i = startIndex; i <= endIndex; i++)

            {

                res += -Math.Abs(stripArray1[i] - stripArray2[i + shift]);

            }

 

            return 1d * stripArray1.Length * res / (stripArray1.Length - Math.Abs(shift));

        }

 

        #endregion

        #region 최적 시프트 찾기 - FindBestShift(stripInfo, x1, x2, startShift, endShift)

 

        /// <summary>

        /// 최적 시프트 찾기

        /// </summary>

        /// <param name="stripInfo">스트립 정보</param>

        /// <param name="x1">X1</param>

        /// <param name="x2">X2</param>

        /// <param name="startShift">시작 시프트</param>

        /// <param name="endShift">종료 시프트</param>

        /// <returns>최적 시프트</returns>

        private static int FindBestShift(StripInfo stripInfo, int x1, int x2, int startShift, int endShift)

        {

            double bestCorrection = double.MinValue;

 

            int bestShift = 0;

 

            for(int shift = startShift; shift <= endShift; shift++)

            {

                double correction = CalculateCorrection(stripInfo, x1, x2, shift);

 

                if(correction > bestCorrection)

                {

                    bestCorrection = correction;

 

                    bestShift = shift;

                }

            }

 

            return bestShift;

        }

 

        #endregion

    }

}

 

 

ZoomPictureBox.cs

 

 

using System;

using System.ComponentModel;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 확대/축소 픽처 박스

    /// </summary>

    public class ZoomPictureBox : UserControl

    {

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

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

 

        #region 표시 중심점 변경시 이벤트 - VisibleCenterPointChanged

 

        /// <summary>

        /// 표시 중심점 변경시 이벤트

        /// </summary>

        public event EventHandler VisibleCenterPointChanged;

 

        #endregion

 

        //////////////////////////////////////////////////////////////////////////////////////////////////// Enumeration

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

 

        #region 마우스 상태 - MouseState

 

        /// <summary>

        /// 마우스 상태

        /// </summary>

        private enum MouseState

        {

            /// <summary>

            /// NONE

            /// </summary>

            None,

            

            /// <summary>

            /// DRAG

            /// </summary>

            Drag

        }

 

        #endregion

 

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

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

 

        #region Field

 

        /// <summary>

        /// 소스 비트맵

        /// </summary>

        private Bitmap sourceBitmap;

 

        /// <summary>

        /// 표시 중심점

        /// </summary>

        private PointF visibleCenterPoint;

 

        /// <summary>

        /// 확대/축소

        /// </summary>

        private float zoom = 1f;

 

        /// <summary>

        /// 마우스 상태

        /// </summary>

        private MouseState mouseState;

 

        /// <summary>

        /// 드래그 시작 포인트

        /// </summary>

        private Point dragStartPoint;

 

        /// <summary>

        /// 드래그 시작 표시 중심점

        /// </summary>

        private PointF dragStartVisibleCenterPoint;

 

        /// <summary>

        /// 소스 비트맵 너비

        /// </summary>

        private int sourceBitmapWidth;

 

        /// <summary>

        /// 소스 비트맵 높이

        /// </summary>

        private int sourceBitmapHeight;

 

        #endregion

 

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

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

 

        #region 축소/확대 델타 - ZoomDelta

 

        /// <summary>

        /// 축소/확대 델타

        /// </summary>

        [DefaultValue(0.1f)]

        public float ZoomDelta { get; set; }

 

        #endregion

        #region 사용자 드래그 허용 여부 - AllowUserDrag

 

        /// <summary>

        /// 사용자 드래그 허용 여부

        /// </summary>

        [DefaultValue(true)]

        public bool AllowUserDrag { get; set; }

 

        #endregion

        #region 사용자 확대/축소 허용 여부 - AllowUserZoom

 

        /// <summary>

        /// 사용자 확대/축소 허용 여부

        /// </summary>

        [DefaultValue(true)]

        public bool AllowUserZoom { get; set; }

 

        #endregion

        #region 보간 모드 - InterpolationMode

 

        /// <summary>

        /// 보간 모드

        /// </summary>

        public InterpolationMode InterpolationMode { get; set; }

 

        #endregion

        #region 픽셀 오프셋 모드 - PixelOffsetMode

 

        /// <summary>

        /// 픽셀 오프셋 모드

        /// </summary>

        public PixelOffsetMode PixelOffsetMode { get; set; }

 

        #endregion

        #region 소스 비트맵 - SourceBitmap

 

        /// <summary>

        /// 소스 비트맵

        /// </summary>

        [DefaultValue(null)]

        public Bitmap SourceBitmap

        {

            get

            {

                return this.sourceBitmap;

            }

            set

            {

                this.sourceBitmap = value;

 

                if(value == null)

                {

                    this.sourceBitmapWidth  = 0;

                    this.sourceBitmapHeight = 0;

 

                    VisibleCenterPoint = new PointF(0, 0);

                }

                else

                {

                    this.sourceBitmapWidth  = value.Width;

                    this.sourceBitmapHeight = value.Height;

 

                    VisibleCenterPoint = new PointF(this.sourceBitmapWidth / 2f, this.sourceBitmapHeight / 2f);

                }

 

                Invalidate();

            }

        }

 

        #endregion

        #region 확대/축소 - Zoom

 

        /// <summary>

        /// 확대/축소

        /// </summary>

        [DefaultValue(1f)]

        public float Zoom

        {

            get

            {

                return this.zoom;

            }

 

            set

            {

                if(Math.Abs(value) <= float.Epsilon)

                {

                    throw new Exception("Zoom must be more then 0");

                }

 

                this.zoom = value;

 

                Invalidate();

            }

        }

 

        #endregion

        #region 표시 중심 포인트 - VisibleCenterPoint

 

        /// <summary>

        /// 표시 중심 포인트

        /// </summary>

        public PointF VisibleCenterPoint

        {

            get

            {

                return this.visibleCenterPoint;

            }

 

            set

            {

                this.visibleCenterPoint = value;

 

                OnVisibleCenterPointChanged();

            }

        }

 

        #endregion

 

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

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

 

        #region 생성자 - ZoomPictureBox()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public ZoomPictureBox()

        {

            SetStyle

            (

                ControlStyles.AllPaintingInWmPaint  |

                ControlStyles.UserPaint             |

                ControlStyles.OptimizedDoubleBuffer |

                ControlStyles.ResizeRedraw,

                true

            );

 

            ZoomDelta = 0.1f;

 

            AllowUserDrag = true;

 

            AllowUserZoom = true;

 

            InterpolationMode = InterpolationMode.Bicubic;

            PixelOffsetMode   = PixelOffsetMode.HighQuality;

        }

 

        #endregion

 

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

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

 

        #region 표시 중심 포인트 변경시 처리하기 - OnVisibleCenterPointChanged()

 

        /// <summary>

        /// 표시 중심 포인트 변경시 처리하기

        /// </summary>

        public virtual void OnVisibleCenterPointChanged()

        {

            VisibleCenterPointChanged?.Invoke(this, EventArgs.Empty);

 

            Invalidate();

        }

 

        #endregion

        #region 비트맵 업데이트 하기 - UpdateBitmap(sourceBitmap)

 

        /// <summary>

        /// 비트맵 업데이트 하기

        /// </summary>

        /// <param name="sourceBitmap">소스 비트맵</param>

        public void UpdateBitmap(Bitmap sourceBitmap)

        {

            this.sourceBitmap = sourceBitmap;

 

            if(sourceBitmap != null)

            {

                this.sourceBitmapWidth  = sourceBitmap.Width;

                this.sourceBitmapHeight = sourceBitmap.Height;

            }

 

            Invalidate();

        }

 

        #endregion

 

        #region 축소하기 - DecreaseZoom()

 

        /// <summary>

        /// 축소하기

        /// </summary>

        public void DecreaseZoom()

        {

            Zoom = (float)Math.Exp(Math.Log(this.zoom) - ZoomDelta);

        }

 

        #endregion

        #region 확대하기 - IncreazeZoom()

 

        /// <summary>

        /// 확대하기

        /// </summary>

        public void IncreazeZoom()

        {

            Zoom = (float)Math.Exp(Math.Log(this.zoom) + ZoomDelta);

        }

 

        #endregion

 

        #region 비트맵 포인트 구하기 - GetBitmapPoint(clientPoint)

 

        /// <summary>

        /// 비트맵 포인트 구하기

        /// </summary>

        /// <param name="clientPoint">클라이언트 포인트</param>

        /// <returns>비트맵 포인트</returns>

        public Point GetBitmapPoint(Point clientPoint)

        {

            float deltaX = (clientPoint.X - ClientSize.Width  / 2f) / Zoom + this.visibleCenterPoint.X;

            float deltaY = (clientPoint.Y - ClientSize.Height / 2f) / Zoom + this.visibleCenterPoint.Y;

 

            return new Point((int)deltaX, (int)deltaY);

        }

 

        #endregion

        #region 클라이언트 포인트 구하기 - GetClientPoint(bitmapPoint)

 

        /// <summary>

        /// 클라이언트 포인트 구하기

        /// </summary>

        /// <param name="bitmapPoint">비트맵 포인트</param>

        /// <returns>클라이언트 포인트</returns>

        public Point GetClientPoint(Point bitmapPoint)

        {

            float deltaX = (bitmapPoint.X - visibleCenterPoint.X) * Zoom + ClientSize.Width  / 2f;

            float deltaY = (bitmapPoint.Y - visibleCenterPoint.Y) * Zoom + ClientSize.Height / 2f;

 

            return new Point((int)deltaX, (int)deltaY);

        }

 

        #endregion

 

        #region 스크린샷 구하기 - GetScreenshot()

 

        /// <summary>

        /// 스크린샷 구하기

        /// </summary>

        /// <returns>스크린샷</returns>

        public Image GetScreenshot()

        {

            Image targetImage = new Bitmap(ClientSize.Width, ClientSize.Height);

 

            using(Graphics targetGraphics = Graphics.FromImage(targetImage))

            {

                OnPaint(new PaintEventArgs(targetGraphics, ClientRectangle));

            }

 

            return targetImage;

        }

 

        #endregion

 

        ////////////////////////////////////////////////////////////////////////////////////////// Protected

 

        #region 마우스 휠 처리하기 - OnMouseWheel(e)

 

        /// <summary>

        /// 마우스 휠 처리하기

        /// </summary>

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

        protected override void OnMouseWheel(MouseEventArgs e)

        {

            base.OnMouseWheel(e);

 

            if(!AllowUserZoom)

            {

                return;

            }

 

            if(e.Delta > 0)

            {

                IncreazeZoom();

            }

 

            if(e.Delta < 0)

            {

                DecreaseZoom();

            }

        }

 

        #endregion

        #region 마우스 DOWN 처리하기 - OnMouseDown(e)

 

        /// <summary>

        /// 마우스 DOWN 처리하기

        /// </summary>

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

        protected override void OnMouseDown(MouseEventArgs e)

        {

            base.OnMouseDown(e);

 

            if(AllowUserDrag)

            {

                if(e.Button == MouseButtons.Left)

                {

                    Cursor = Cursors.SizeAll;

 

                    this.mouseState = MouseState.Drag;

                }

            }

 

            this.dragStartPoint = e.Location;

 

            this.dragStartVisibleCenterPoint = VisibleCenterPoint;

        }

 

        #endregion

        #region 마우스 이동시 처리하기 - OnMouseMove(e)

 

        /// <summary>

        /// 마우스 이동시 처리하기

        /// </summary>

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

        protected override void OnMouseMove(MouseEventArgs e)

        {

            base.OnMouseMove(e);

 

            if(mouseState == MouseState.Drag)

            {

                int deltaX = e.Location.X - this.dragStartPoint.X;

                int deltaY = e.Location.Y - this.dragStartPoint.Y;

 

                VisibleCenterPoint = new PointF

                (

                    this.dragStartVisibleCenterPoint.X - deltaX / this.zoom,

                    this.dragStartVisibleCenterPoint.Y - deltaY / this.zoom

                );

            }

        }

 

        #endregion

        #region 마우스 UP 처리하기 - OnMouseUp(e)

 

        /// <summary>

        /// 마우스 UP 처리하기

        /// </summary>

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

        protected override void OnMouseUp(MouseEventArgs e)

        {

            base.OnMouseUp(e);

 

            Cursor = Cursors.Default;

 

            this.mouseState = MouseState.None;

 

            Invalidate();

        }

 

        #endregion

 

        #region 페인트시 처리하기 - OnPaint(e)

 

        /// <summary>

        /// 페인트시 처리하기

        /// </summary>

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

        protected override void OnPaint(PaintEventArgs e)

        {

            if(this.sourceBitmap == null)

            {

                return;

            }

 

            e.Graphics.ResetTransform();

 

            e.Graphics.InterpolationMode = InterpolationMode;

            e.Graphics.PixelOffsetMode   = PixelOffsetMode;

 

            if(this.mouseState == MouseState.Drag || Zoom < 1f)

            {

                e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;

            }

 

            float deltaX = -this.visibleCenterPoint.X * Zoom + ClientSize.Width  / 2f;

            float deltaY = -this.visibleCenterPoint.Y * Zoom + ClientSize.Height / 2f;

 

            e.Graphics.DrawImage

            (

                this.sourceBitmap,

                deltaX,

                deltaY,

                this.sourceBitmap.Width  * Zoom,

                this.sourceBitmap.Height * Zoom

            );

 

            base.OnPaint(e);

        }

 

        #endregion

    }

}

 

 

MainForm.cs

 

 

using System;

using System.Drawing;

using System.Windows.Forms;

 

namespace TestProject

{

    /// <summary>

    /// 메인 폼

    /// </summary>

    public partial class MainForm : Form

    {

        //////////////////////////////////////////////////////////////////////////////////////////////////// 랴디

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

 

        #region Field

 

        /// <summary>

        /// 스트립 카운트

        /// </summary>

        private int STRIP_COUNT = 10;

 

        #endregion

 

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

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

 

        #region 생성자 - MainForm()

 

        /// <summary>

        /// 생성자

        /// </summary>

        public MainForm()

        {

            InitializeComponent();

 

            this.sourcePictureBox.Paint += pictureBox_Paint;

            this.targetPictureBox.Paint += pictureBox_Paint;

        }

 

        #endregion

 

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

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

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

 

        #region 파일 열기 메뉴 클릭시 처리하기 - openFileMenuItem_Click(sender, e)

 

        /// <summary>

        /// 파일 열기 메뉴 클릭시 처리하기

        /// </summary>

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

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

        private void openFileMenuItem_Click(object sender, EventArgs e)

        {

            OpenFileDialog openFileDialog = new OpenFileDialog { Filter = "Image|*.png;*.jpeg;*.jpg;*.tif" };

 

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

            {

                ProcessBitmap(openFileDialog.FileName);

            }

        }

 

        #endregion

        #region 픽처 박스 페인트시 처리하기 - pictureBox_Paint(sender, e)

 

        /// <summary>

        /// 픽처 박스 페인트시 처리하기

        /// </summary>

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

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

        private void pictureBox_Paint(object sender, PaintEventArgs e)

        {

            using(var pen = new Pen(Color.FromArgb(20, Color.Black)))

            {

                for(int i = 0; i < 1000; i += 30)

                {

                    e.Graphics.DrawLine(pen, 0, i, 1000, i);

                }

            }

        }

 

        #endregion

 

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

 

        #region 비트맵 처리하기 - ProcessBitmap(filePath)

 

        /// <summary>

        /// 비트맵 처리하기

        /// </summary>

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

        private void ProcessBitmap(string filePath)

        {

            Bitmap sourceBitmap = Image.FromFile(filePath) as Bitmap;

 

            this.sourcePictureBox.SourceBitmap = sourceBitmap.Clone() as Bitmap;

 

            StripInfo stripInfo = new StripInfo(sourceBitmap, STRIP_COUNT);

 

            int stripX1 = 2;

            int stripX2 = 6;

 

            double angle = SkewCalculator.GetRotateAngle(stripInfo, stripX1, stripX2);

 

            angle = (angle * 180 / Math.PI);

 

            Text = "스캔 이미지 왜곡 자동 보정하기 : 각도 = " + angle.ToString("0.00");

 

            Bitmap targetBitmap = BitmapHelper.RotateBitmap(sourceBitmap, angle);

 

            this.targetPictureBox.SourceBitmap = targetBitmap;

        }

 

        #endregion

    }

}

 

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

Posted by 사용자 icodebroker

댓글을 달아 주세요