이번 포스팅은 제가 C#에서 영상처리 구현을 공부하면서 알게 된 Bitmap 클래스의 LockBits 메서드에 대해 작성하려 합니다. 이때까지 픽셀 단위 작업을 하며 GetPixel, SetPixel 메서드를 사용하며 작업했는데 속도면에서 LockBits 메서드가 더 빠르다는 것을 알았고 더욱 사용하게 되었습니다.
GetPixel, SetPixel에 대해 간단히 설명하고 LockBits 메서드에 대해 예제와 함께 정리하겠습니다.
| GetPixel 및 SetPixel 메서드
- GetPixel과 SetPixel을 사용하면 이미지 데이터에 대한 직접적인 포인터 액세스를 하지 않아도 작업할 수 있으므로 코드가 더 안정적일 수 있습니다.
- 하지만 GetPixel과 SetPixel 메서드는 비트맵 이미지의 픽셀 값을 조작할 때마다 메서드 호출이 발생하므로 이미지의 전체 픽셀을 다루게 되면 느리게 동작합니다.
| LockBits 메서드란?
- LockBits 메서드는 비트맵 이미지의 픽셀 데이터에 접근하는 데 사용합니다. 비트맵의 픽셀 데이터에 대한 포인터를 얻어와서 해당 데이터를 직접 조작할 수 있습니다.
- 픽셀 데이터에 대한 포인터 작업을 하므로 대량의 이미지 데이터 처리 작업에 대한 속도가 빨라지며 효율적으로 처리할 수 있게 됩니다. 간단한 예시를 통해 LockBits 메서드 사용 방법에 대해 알아보겠습니다.
예시) 이미지를 반전시키는 작업 수행 코드
// 비트맵 이미지 로드
using (Bitmap bmp = new Bitmap("input.jpg"))
{
// 이미지를 잠금
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat);
// 픽셀 데이터에 접근
unsafe
{
byte* ptr = (byte*)bmpData.Scan0;
int bytesPerPixel = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
// 각 픽셀을 조작
for (int i = 0; i < bytesPerPixel; i++)
{
ptr[i] = 255 - ptr[i]; // 픽셀 반전
}
ptr += bytesPerPixel; // 다음 픽셀로 이동
}
ptr += bmpData.Stride - (bmp.Width * bytesPerPixel); // Stride 처리
}
}
// 이미지 잠금 해제
bmp.UnlockBits(bmpData);
// 변경된 이미지 저장
bmp.Save("output.jpg");
}
- BitmapData 구조로 선언하여 bitmap.LoctBits로 이미지를 잠그고 포인터를 반환합니다.
- LockBits 메서드의 기본 형식은 다음과 같습니다.
public BitmapData LockBits(Rectangle rect, ImageLockMode flags, PixelFormat format)
- rect : 이미지에서 잠길 영역을 정의하는 사각형
- flags : 잠금 모드, ImageLockMode.ReadWrite 또는 ImageLockMode.ReadOnly 선택
- format : 픽셀 형식을 나타내며, 주로 PixelFormat.Format24bppRgb 또는 PixelFormat.Format32bppArgb 사용
- 그리고 ptr 포인터를 얻고 픽셀 데이터에 접근하여 각 픽셀을 반전시킵니다. 반전시킨 후 ptr 포인터를 다음 픽셀로 이동하고 bmpData.Stride를 사용하여 각 행의 바이트 수를 처리합니다.
- Stride는 이미지 행 간 간격을 나타내며 각 행은 메모리에서 연속적으로 저장되지 않을 수 있으며 각 행의 데이터 길이를 나타냅니다.
- 마지막으로 Unlockbits를 통해 이미지 잠금을 해제하고 이미지를 저장합니다.
| 마치며
- Lockbits 메서드는 C#에서 이미지 처리를 위한 도구라는 것을 알게 되었고 고속처리를 위해서 꼭 필요한 기능이라는 것을 알게 되었습니다. 하지만 메모리 누수나 예외 처리에 조금 더 신경을 써야 할 것 같습니다.
다음 포스팅으로 찾아오겠습니다.
감사합니다 :)
'C#' 카테고리의 다른 글
[C#] Marshal.Copy 이해 (0) | 2023.09.26 |
---|---|
[C#] Bitmap.Clone() vs new Bitmap() 비교 (0) | 2023.09.26 |
[C#] unsafe / fixed 키워드 (0) | 2023.09.25 |
[C#] record(레코드) 정의와 사용 (0) | 2023.09.24 |
[C#] delegate(대리자) 마스터하기 (1) (0) | 2023.07.17 |