728x90
반응형
728x170
▶ PixelData.cs
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace TestProject
{
/// <summary>
/// 픽셀 데이터
/// </summary>
public class PixelData
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Public
#region Field
/// <summary>
/// 비트맵 크기
/// </summary>
private Size bitmapDataSize;
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region Field
/// <summary>
/// 픽셀 카운트
/// </summary>
protected readonly int pixelCount;
/// <summary>
/// 픽셀 배열
/// </summary>
protected readonly int[] pixelArray;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 비트맵 데이터 크기 - BitmapDataSize
/// <summary>
/// 비트맵 데이터 크기
/// </summary>
public Size BitmapDataSize
{
get
{
return this.bitmapDataSize;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Indexer
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 인덱서 - this[point]
/// <summary>
/// 인덱서
/// </summary>
/// <param name="point">좌표</param>
/// <returns>픽셀 값</returns>
public int this[Point point]
{
get
{
int index = (point.Y * this.bitmapDataSize.Width) + point.X;
return this.pixelArray[index];
}
protected set
{
int index = (point.Y * this.bitmapDataSize.Width) + point.X;
this.pixelArray[index] = value;
}
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 생성자 - PixelData(bitmapData)
/// <summary>
/// 생성자
/// </summary>
/// <param name="bitmapData">비트맵 데이터</param>
protected PixelData(BitmapData bitmapData)
{
this.bitmapDataSize = new Size(bitmapData.Width, bitmapData.Height);
this.pixelCount = this.bitmapDataSize.Width * this.bitmapDataSize.Height;
this.pixelArray = new int[this.pixelCount];
Marshal.Copy(bitmapData.Scan0, this.pixelArray, 0, this.pixelCount);
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 생성하기 - Create(bitmap)
/// <summary>
/// 생성하기
/// </summary>
/// <param name="bitmap">비트맵</param>
/// <returns>픽셀 데이터</returns>
public static PixelData Create(Bitmap bitmap)
{
BitmapData bitmapData = GetBitmapData(bitmap, true);
PixelData pixelData = new PixelData(bitmapData);
bitmap.UnlockBits(bitmapData);
return pixelData;
}
#endregion
#region 생성하기 - Create(filePath)
/// <summary>
/// 생성하기
/// </summary>
/// <param name="filePath">파일 경로</param>
/// <returns>픽셀 데이터</returns>
public static PixelData Create(string filePath)
{
using(Bitmap bitmap = new Bitmap(filePath))
{
BitmapData bitmapData = GetBitmapData(bitmap, true);
PixelData pixelData = new PixelData(bitmapData);
bitmap.UnlockBits(bitmapData);
return pixelData;
}
}
#endregion
#region 포함 여부 구하기 - Contains(point)
/// <summary>
/// 포함 여부 구하기
/// </summary>
/// <param name="point">좌표</param>
/// <returns>포함 여부</returns>
public bool Contains(Point point)
{
return ((point.X < 0) || (point.X >= this.bitmapDataSize.Width) || (point.Y < 0) || (point.Y >= this.bitmapDataSize.Height)) ? false : true;
}
#endregion
#region 색상 여부 구하기 - IsColor(point, predicate)
/// <summary>
/// 색상 여부 구하기
/// </summary>
/// <param name="point">좌표</param>
/// <param name="predicate">판정 함수</param>
/// <returns>색상 여부</returns>
public bool IsColor(Point point, Predicate<int> predicate)
{
int pixel = this[point];
return predicate(pixel);
}
#endregion
#region 투명색 여부 구하기 - IsTransparentColor(argb)
/// <summary>
/// 투명색 여부 구하기
/// </summary>
/// <param name="argb">ARGB 값</param>
/// <returns>투명색 여부</returns>
public bool IsTransparentColor(int argb)
{
Color color = Color.FromArgb(argb);
return (color.A == 0);
}
#endregion
#region 투명색 여부 구하기 - IsTransparentColor(point)
/// <summary>
/// 투명색 여부 구하기
/// </summary>
/// <param name="point">좌표</param>
/// <returns>투명색 여부</returns>
public bool IsTransparentColor(Point point)
{
if(!Contains(point))
{
return true;
}
return IsColor(point, IsTransparentColor);
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Protected
#region 비트맵 데이터 구하기 - GetBitmapData(bitmap, readOnly)
/// <summary>
/// 비트맵 데이터 구하기
/// </summary>
/// <param name="bitmap">비트맵</param>
/// <param name="readOnly">읽기 전용 여부</param>
/// <returns>비트맵 데이터</returns>
protected static BitmapData GetBitmapData(Bitmap bitmap, bool readOnly)
{
Rectangle rectangle = new Rectangle(new Point(0, 0), bitmap.Size);
return bitmap.LockBits(rectangle, (readOnly) ? ImageLockMode.ReadOnly : ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
}
#endregion
}
}
728x90
▶ PixelData2.cs
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace TestProject
{
/// <summary>
/// 픽셀 데이터 2
/// </summary>
public class PixelData2 : PixelData
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 비트맵
/// </summary>
private readonly Bitmap bitmap;
/// <summary>
/// 비트맵 데이터
/// </summary>
private readonly BitmapData bitmapData;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - PixelData2(bitmap, bitmapData)
/// <summary>
/// 생성자
/// </summary>
/// <param name="bitmap">비트맵</param>
/// <param name="bitmapData">비트맵 데이터</param>
public PixelData2(Bitmap bitmap, BitmapData bitmapData) : base(bitmapData)
{
this.bitmap = bitmap;
this.bitmapData = bitmapData;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 생성하기 - Create(bitmap)
/// <summary>
/// 생성하기
/// </summary>
/// <param name="bitmap">비트맵</param>
/// <returns>픽셀 데이터 2</returns>
public static new PixelData2 Create(Bitmap bitmap)
{
BitmapData bitmapData = GetBitmapData(bitmap, false);
return new PixelData2(bitmap, bitmapData);
}
#endregion
////////////////////////////////////////////////////////////////////////////////////////// Instance
//////////////////////////////////////////////////////////////////////////////// Public
#region 저장하기 - Save(targetPath)
/// <summary>
/// 저장하기
/// </summary>
/// <param name="targetPath">타겟 경로</param>
public void Save(string targetPath)
{
Marshal.Copy(pixelArray, 0, this.bitmapData.Scan0, pixelCount);
this.bitmap.UnlockBits(this.bitmapData);
this.bitmap.Save(targetPath);
}
#endregion
#region 비트맵 구하기 - GetBitmap()
/// <summary>
/// 비트맵 구하기
/// </summary>
/// <returns>비트맵</returns>
public Bitmap GetBitmap()
{
Marshal.Copy(pixelArray, 0, this.bitmapData.Scan0, pixelCount);
this.bitmap.UnlockBits(this.bitmapData);
return this.bitmap;
}
#endregion
#region 색상 설정하기 - SetColor(point, argb)
/// <summary>
/// 색상 설정하기
/// </summary>
/// <param name="point">좌표</param>
/// <param name="argb">ARGB 값</param>
public void SetColor(Point point, int argb)
{
base[point] = argb;
}
#endregion
}
}
300x250
▶ BoundaryTracing.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
namespace TestProject
{
/// <summary>
/// 경계선 추적
/// </summary>
public static class BoundaryTracing
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 생성하기 - Create(pixelData)
/// <summary>
/// 생성하기
/// </summary>
/// <param name="pixelData">픽셀 데이터</param>
/// <returns>좌표 리스트의 리스트</returns>
public static List<List<Point>> Create(PixelData pixelData)
{
Size bitmapDataSize = pixelData.BitmapDataSize;
HashSet<Point> pointHashSet = new HashSet<Point>();
List<Point> pointList = null;
List<List<Point>> pointListList = new List<List<Point>>();
bool inside = false;
int width = bitmapDataSize.Width;
Tuple<Func<Point, Point>, int>[] neighborhoodTupleArray = new Tuple<Func<Point, Point>, int>[]
{
new Tuple<Func<Point, Point>, int>(point => new Point(point.X - 1, point.Y ), 7),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X - 1, point.Y - 1), 7),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X , point.Y - 1), 1),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X + 1, point.Y - 1), 1),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X + 1, point.Y ), 3),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X + 1, point.Y + 1), 3),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X , point.Y + 1), 5),
new Tuple<Func<Point, Point>, int>(point => new Point(point.X - 1, point.Y + 1), 5)
};
for(int y = 0; y < bitmapDataSize.Height; ++y)
{
for(int x = 0; x < bitmapDataSize.Width; ++x)
{
Point point = new Point(x, y);
if(pointHashSet.Contains(point) && !inside)
{
inside = true;
continue;
}
bool isTransparent = pixelData.IsTransparentColor(point);
if(!isTransparent && inside)
{
continue;
}
if(isTransparent && inside)
{
inside = false;
continue;
}
if(!isTransparent && !inside)
{
pointListList.Add(pointList = new List<Point>());
pointHashSet.Add(point); pointList.Add(point);
int neighborCount = 1;
Point startPoint = point;
int counter1 = 0;
int counter2 = 0;
while(true)
{
Point checkPoint = neighborhoodTupleArray[neighborCount - 1].Item1(point);
int newNeighborCount = neighborhoodTupleArray[neighborCount - 1].Item2;
if(!pixelData.IsTransparentColor(checkPoint))
{
if(checkPoint == startPoint)
{
counter1++;
if(newNeighborCount == 1 || counter1 >= 3)
{
inside = true;
break;
}
}
neighborCount = newNeighborCount;
point = checkPoint;
counter2 = 0;
pointHashSet.Add(point);
pointList.Add(point);
}
else
{
neighborCount = 1 + (neighborCount % 8);
if(counter2 > 8)
{
counter2 = 0;
pointList = null;
break;
}
else
{
counter2++;
}
}
}
}
}
}
return pointListList;
}
#endregion
#region 좌표 리스트 구하기 - GetPointList(pointListList)
/// <summary>
/// 좌표 리스트 구하기
/// </summary>
/// <param name="pointListList">좌표 리스트 리스트</param>
/// <returns>좌표 리스트</returns>
public static List<Point> GetPointList(List<List<Point>> pointListList)
{
pointListList.Sort((x, y) => x.Count.CompareTo(y.Count));
return pointListList.Last();
}
#endregion
}
}
▶ MainForm.cs
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace TestProject
{
/// <summary>
/// 메인 폼
/// </summary>
public partial class MainForm : Form
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainForm()
/// <summary>
/// 생성자
/// </summary>
public MainForm()
{
InitializeComponent();
Bitmap sourceBitmap = new Bitmap(400, 400, PixelFormat.Format32bppArgb);
Graphics sourceGraphics = Graphics.FromImage(sourceBitmap);
sourceGraphics.DrawEllipse(new Pen(Brushes.LightGray, 20), 50, 50, 300, 300);
sourceGraphics.Dispose();
this.pictureBox1.Image = sourceBitmap;
PixelData2 pixelData = PixelData2.Create(sourceBitmap.Clone() as Bitmap);
List<List<Point>> pointListList = BoundaryTracing.Create(pixelData);
foreach(List<Point> pointList in pointListList)
{
foreach(Point point in pointList)
{
pixelData.SetColor(point, Color.Red.ToArgb());
}
}
this.pictureBox2.Image = pixelData.GetBitmap();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] TextBox 클래스 : ShortcustsEnabled 속성을 사용해 COPY/PASTE/CUT 방지하기 (0) | 2018.08.15 |
---|---|
[C#/WINFORM] Control 클래스 : WndProc 메소드를 사용해 COPY/PASTE/CUT 방지하기 (0) | 2018.08.15 |
[C#/WINFORM] 색상 맵 사용하기 (0) | 2018.04.14 |
[C#/WINFORM] 별 그리기 (0) | 2018.04.14 |
[C#/WINFORM] Pen 클래스 : 1 픽셀 너비 펜 구하기 (0) | 2018.04.13 |
[C#/WINFORM] DirectShow를 사용해 동영상 재생하기 (0) | 2018.04.02 |
[C#/WINFORM] 설치 프린터 조회하기 (0) | 2018.03.22 |
[C#/WINFORM] 윈폼(WinForm)에서 콘솔(Console) 사용하기 (0) | 2018.03.22 |
[C#/WINFORM] BitBlt 함수를 사용해 비트맵 복사하기 (0) | 2018.03.15 |
[C#/WINFORM] PropertyGrid 클래스 : 이미지 목록을 사용해 항목 값 선택하기 (0) | 2018.03.04 |
댓글을 달아 주세요