2011-01-13 6 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/1018038_hpnarpq.jpg

それは(これは画像の断片のみである)は、以下の結果を生成:

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

なぜですか?

答えて

3

データにアクセスするときは、BitmapData.Strideを考慮する必要があります。

alt text

EDIT:

ここでは、私はビットマップにDirectXの表面をコピーするために使用するソリューションです。アイデアは同じですが、少し修正する必要があります。

チェックこのアウト: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)); 
     } 

//// 

EDIT#2で一度に画像の1本の走査線をコピーしますすべて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とtbridgeのThanx私はこれを行う方法を管理しました。

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

をし、すべてです

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

にライン

this.Width = width; 

BitmapDataAccessorにそれを渡して変更します。

まず我々はBitmap2Byteにパディングを計算する必要があります。高尚な男。

関連する問題