■ 스캔 이미지 왜곡 자동 보정하기
------------------------------------------------------------------------------------------------------------------------
▶ 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 } }
|
------------------------------------------------------------------------------------------------------------------------
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] ScrollableControl 클래스 : 접을 수 있는 패널 사용하기 (0) | 2020.01.05 |
---|---|
[C#/WINFORM] UserControl 클래스 : 푯말 레이블 사용하기 (0) | 2019.12.28 |
[C#/WINFORM] UserControl 클래스 : 체크 버튼 사용하기 (0) | 2019.12.28 |
[C#/WINFORM] Label 클래스 : 외곽선 텍스트 사용하기 (0) | 2019.12.27 |
[C#/WINFORM] WebBrowser 클래스 : DocumentText 속성을 사용해 HTML 문자열 설정하기 (0) | 2019.12.26 |
[C#/WINFORM] 스캔 이미지 왜곡 자동 보정하기 (0) | 2019.12.24 |
[C#/WINFORM] Bitmap 클래스 : 비트맵 회전하기 (0) | 2019.12.23 |
[C#/WINFORM] Form 클래스 : 마우스 위치에 따라 폼 불투명도 변경하기 (0) | 2019.11.29 |
[C#/WINFORM] TreeView 클래스 : 드래그 & 드롭 사용하기 (0) | 2019.10.03 |
[C#/WINFORM] TreeView 클래스 : 첫번째 종말 노드 구하기 (0) | 2019.09.15 |
[C#/WINFORM] TreeView 클래스 : 특정 레벨까지 노드 확장하기 (0) | 2019.09.15 |
댓글을 달아 주세요