728x90
반응형
728x170
▶ 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
반응형
그리드형(광고전용)
'C# > WinForm' 카테고리의 다른 글
[C#/WINFORM] Bitmap 클래스 : 비트맵 회전하기 (0) | 2021.01.08 |
---|---|
[C#/WINFORM] Bitmap 클래스 : 비트맵 자르기(Shear) (0) | 2021.01.08 |
[C#/WINFORM] Bitmap 클래스 : 컴파스 가장자리 탐지 필터(Compass Edge Detection Filter) 사용하기 (0) | 2021.01.04 |
[C#/WINFORM] Bitmap 클래스 : 스테인 글라스 필터(Stained Glass Filter) 사용하기 (0) | 2021.01.04 |
[C#/WINFORM] Bitmap 클래스 : 경계 추출 필터(Boundary Extraction Filter) 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 오일 페인트 필터(Oil Paint Filter) 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 이미지 추상 색상 필터 사용하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 바이트 배열 구하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 정사각형 크기 비트맵에 복사하기 (0) | 2021.01.03 |
[C#/WINFORM] Bitmap 클래스 : 비트맵 일부 오버레이 표시하기 (0) | 2020.12.27 |
댓글을 달아 주세요