2011-01-13 2 views
1
struct BitmapDataAccessor 
{ 
    private readonly byte[] data; 
    private readonly int[] rowStarts; 
    public readonly int Height; 
    public readonly int Width; 

    public BitmapDataAccessor(byte[] data, int width, int height) 
    { 
     this.data = data; 
     this.Height = height; 
     this.Width = width; 
     rowStarts = new int[height]; 
     for (int y = 0; y < Height; y++) 
      rowStarts[y] = y * width; 
    } 

    public byte this[int x, int y, int color] // Maybe use an enum with Red = 0, Green = 1, and Blue = 2 members? 
    { 
     get { return data[(rowStarts[y] + x) * 3 + color]; } 
     set { data[(rowStarts[y] + x) * 3 + color] = value; } 
    } 

    public byte[] Data 
    { 
     get { return data; } 
    } 
} 

    public static byte[, ,] Bitmap2Byte(Bitmap obraz) 
    { 
     int h = obraz.Height; 
     int w = obraz.Width; 

     byte[, ,] wynik = new byte[w, h, 3]; 

     BitmapData bd = obraz.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); 

     int bytes = Math.Abs(bd.Stride) * h; 
     byte[] rgbValues = new byte[bytes]; 
     IntPtr ptr = bd.Scan0; 
     System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); 

     BitmapDataAccessor bda = new BitmapDataAccessor(rgbValues, w, h); 

     for (int i = 0; i < h; i++) 
     { 
      for (int j = 0; j < w; j++) 
      { 
       wynik[j, i, 0] = bda[j, i, 2]; 
       wynik[j, i, 1] = bda[j, i, 1]; 
       wynik[j, i, 2] = bda[j, i, 0]; 
      } 
     } 

     obraz.UnlockBits(bd); 
     return wynik; 
    } 

    public static Bitmap Byte2Bitmap(byte[, ,] tablica) 
    { 
     if (tablica.GetLength(2) != 3) 
     { 
      throw new NieprawidlowyWymiarTablicyException(); 
     } 

     int w = tablica.GetLength(0); 
     int h = tablica.GetLength(1); 

     Bitmap obraz = new Bitmap(w, h, PixelFormat.Format24bppRgb); 

     for (int i = 0; i < w; i++) 
     { 
      for (int j = 0; j < h; j++) 
      { 
       Color kol = Color.FromArgb(tablica[i, j, 0], tablica[i, j, 1], tablica[i, j, 2]); 
       obraz.SetPixel(i, j, kol); 
      } 
     } 

     return obraz; 
    } 

지금, 나는 할 경우 :왜 내 사진을 사용하여 구부러진 도난 된 사진이 있습니까?

private void btnLoad_Click(object sender, EventArgs e) 
    { 
     if (dgOpenFile.ShowDialog() == DialogResult.OK) 
     { 
      try 
      { 
       Bitmap img = new Bitmap(dgOpenFile.FileName); 

       byte[, ,] tab = Grafika.Bitmap2Byte(img); 

       picture.Image = Grafika.Byte2Bitmap(tab); 
       picture.Size = img.Size; 


      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.Message); 
      } 
     } 
    } 

올바르게 처리 사진의 대부분은하지 butsome. 작동하지 않는 사진 예 :

http://ifotos.pl/img/example_hpnarhp.jpg

왜 즉 : 결과 다음

http://ifotos.pl/img/1018038_hpnarpq.jpg

그것은 생산 (이 사진의 조각입니다)?

답변

3

데이터에 액세스 할 때 BitmapData.Stride을 고려해야합니다.

alt text

편집 : 여기

내가 비트 맵에 다이렉트 표면을 복사하는 데 사용하는 솔루션입니다. 아이디어는 동일하지만 약간 수정해야합니다.

확인이 아웃 : Stride/Pitch Tutorial

나는 (KERNEL32.DLL하는 P/호출) RtlMoveMemory를 호출

//// Snippet 

     int pitch; 
     int bytesPerPixel = 4; 
     Rectangle lockRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height); 

     // Lock the bitmap 
     GraphicsStream surfacedata = surface.LockRectangle(LockFlags.ReadOnly, out pitch); 
     BitmapData bitmapdata = bitmap.LockBits(lockRectangle, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb); 

     // Copy surface to bitmap 
     for (int scanline = 0; scanline < bitmap.Height; ++scanline) 
     { 
      byte* dest = (byte*)bitmapdata.Scan0 + (scanline * bitmap.Width * bytesPerPixel); 
      byte* source = (byte*)surfacedata.InternalData + (scanline * pitch); 

      RtlMoveMemory(new IntPtr(dest), new IntPtr(source), (bitmap.Width * bytesPerPixel)); 
     } 

//// 

편집 # 2 한 번에 이미지 중 하나의 주사선을 복사 모두 DirectX를 목표로하지만 컨셉은 동일합니다.

+0

좋습니다. 알아요. 그런데 왜 모든 이미지에 그런 일이 일어나지 않습니까? 그것은 오직 어떤 것에 만 일어납니다 ... –

+0

때로는 'Stride'는 '0'이 될 수 있습니다. 상향식 이미지의 경우 실제로는 음수가 될 수도 있습니다. – tbridge

+0

그러나 여전히 나는 그것이 작동하도록 코드를 바꿀 수있는 방법을 모르겠다 ... 그리고 나는 보폭이 이것과 아무 상관이 없다고 생각한다. –

1

비트 맵에 할당 된 메모리는 32 비트 경계에 정렬되어야하며 크기 때문에 일부 이미지에 패딩이 발생할 수 있습니다. 여기에 24 비트 픽셀이 있기 때문에 일부 선폭은 32 비트로 끝나고 나머지는 그렇지 않습니다. 당신은 그것을 위해 계정 다음 사용되는 패딩을 해결하기 위해 다음과 같은 공식을 사용하기 위해 필요 :

int padding = bd.Stride - (((w * 24) + 7)/8); 

당신은 대신에 변환 전체를 통해가는 것보다 getPixel와 (X, Y)를 사용하여 바이트 배열을로드 할 수 있습니다 픽셀 읽기를 시작하기 전에

+0

'GetPixel'은 너무 느리기 때문에 사용하고 싶지 않습니다. –

+0

@Miko Kronn : 프로파일 링 데이터가 얼마나 더 느린지 이해하는 데 관심이 있습니다. – Lazarus

0

@Lazarus와 Thbridge의 고맙습니다. 어떻게해야할까요?

int padding = bd.Stride - (((w * 24) + 7)/8); 

및 전부

this.Width = width + (4-padding)%4; 

에 선

this.Width = width; 

BitmapDataAccessor에 전달하고 수정

먼저 우리는 Bitmap2Byte에 패딩을 계산해야합니다. 고맙습니다.

관련 문제