ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [C# - Bitmap] BitMap 포인터 접근하기
    C# 2021. 3. 10. 17:07

     

     

     

    이번 글은 Bitmap Pixel에 대한 짧은 글입니다.

     

    이미지 처리를 하다 보면 이미지의 각 픽셀의 정보가 필요할 때가 있습니다.

     

    이때 각 픽셀의 값을 가져오기 위해선 GetPixel(x, y)를 사용할 수 도 있지만 큰 이미지를 처리한다면 많은 시간이 소요됩니다.

     

    포인터를 이용하여 비트맵의 각 픽셀에 빠르게 접근하는 방법을 살펴보도록 하겠습니다.

     

     

    1. unsafe

     

    우선 C#에서 포인터를 사용하려면 unsafe를 이용해야 합니다. 

     

    다음과 같이 함수 앞에 unsafe 선언을 한 후 해당 프로젝트에서 unsafe코드를 사용할 수 있도록 수정해 주면 됩니다.

     

     public unsafe void PixelColor(Bitmap pImage) 
     {
     	// 이곳에서 포인터를 사용하세요.
     }

     

     

    unsafe를 선언 후 프로젝트 속성 -> 빌드에서 안전하지 않은 코드 허용을 체크해 주도록 합니다.

     

     

    2. BitmapData

     

    public unsafe void PixelColor(Bitmap pImage) 
    {
        BitmapData pBitmapData = pImage.LockBits(new Rectangle(0, 0, pImage.Width, pImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        
        byte* ptr0 = (byte*)pBitmapData.Scan0;
        
        int iStride = pBitmapData.Stride;
        int iHeight = pImage.Height;
        int iWidth = pImage.Width;
        
        
        
        pImage.UnlockBits(pBitmapData);
    }

    비트맵의 LockBits를 통해 이제 BitmapData를 가져오도록 합니다. 

     

    LockBits 동안은 다른 곳에서 변수에 접근할 수 없으니 주의하셔야 합니다.

     

     

    첫 번째 인자는 Rectangle 영역만큼 이미지에서 가져올 BitmapData입니다.

    두 번째 인자는 이미지 모드로 읽기 모드, 쓰기 모드 등 둘 다 선택할 수 있습니다.

    마지막으로 세 번째 인자는 픽셀 포맷으로 이미지가 RGBA, RGB, GrayScale의 구분에 따라 32, 24, 8 비트로 구분할 수 있습니다.

     

    BitmapData Scan0를 통해 포인터 주소를 구합니다.

     

    Scan0는 이미지의 0, 0의 픽셀 값입니다.

     

    여기서 중요한 것은 Stride인데 이미지 데이터의 너비 폭을 나타냅니다.

     

    비트맵 이미지의 Pixel은 각각 BGR에 해당하는 바이트 값을 가지고 있습니다. 그렇기 때문에 Stride의 이미지의 픽셀 포맷에 따라 달라지게 됩니다.

     

    이미지가 RGBA, RGB, GrayScale 일 경우 Width * 4, Width * 3. Width *1 이 되지만 Stride 가 계산보다 조금 큰 경우가 있는데 나머지 공간은 여백이기 때문에 계산할 때 주의하셔야 합니다.

     

     

    이제 나머지 함수를 채워 보도록 하겠습니다.

     

    public unsafe void PixelColor(Bitmap pImage) 
    {
        BitmapData pBitmapData = pImage.LockBits(new Rectangle(0, 0, pImage.Width, pImage.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
        
        byte* ptr0 = (byte*)pBitmapData.Scan0;
        
        int iStride = pBitmapData.Stride;
        int iHeight = pImage.Height;
        int iWidth = pImage.Width;
        
        for (int h = 0; h < iHeight; h++)
        {
            for (int w = 0; w < iWidth; w+=3)
            {
                byte blue = *(ptr0 + (h * iStride) + w);
                byte green = *(ptr0 + (h * iStride) + w + 1);
                byte red = *(ptr0 + (h * iStride) + w + 2);
            }
        }
        
        pImage.UnlockBits(pBitmapData);
    }

     

    W += 3 은 픽셀 포맷이 24비트여서 다른 포맷을 사용하는 분은 포맷에 맞춰 수정해주시면 됩니다.

     

    h * iStride 각 행의 첫 번째 픽셀의 주소 값이며 w를 더해 BGR에 해당하는 byte 값을 가져올 수 있습니다.  

     

     

    마지막으로 LockBits를 UnlockBits로 변경해 이미지를 사용할 수 있도록 변경합니다.

    댓글

Designed by Tistory.