2016-04-29 11 views
3

Ich habe eine .DLL zu Testzwecken erhalten. Diese .dll enthält die Funktionalität, die später zur Verarbeitung von Livebildern von der Hardware verwendet wird.Bitmap von .DLL abrufen, in Byte [] konvertieren und zu Image

Für diese einfache .dll kann ich ein Bild öffnen (in Arbeitsspeicher laden), die Breite und Höhe abrufen und die Pixel abrufen, die in ein Bild konvertiert werden müssen. Laden, Breite bekommen und Höhe bekommen ist in Ordnung, aber die Pixel zu bekommen und diese in eine Bitmap oder ein Bild zu konvertieren ist ein Problem.

C++ Beispiel Quelle, die ich erhielt:

ApiFunc->OpenImageFile(this->OpenPictureDialog1->FileName.c_str()); 
    ApiFunc->AllocMemory(); 

    w = ApiFunc->GetImageWidth(); 
    h = ApiFunc->GetImageHeight(); 
    Image1->Width = w; 
    Image1->Height = h; 

    unsigned char* ptr = ApiFunc->GetImagePixels(); 
    COLORREF pixel; 
    int r,g,b; 
    for(int y=0; y<w; y++) 
    { 
     for(int x=0; x<h; x++) 
     { 
      r = (int)*(ptr+3*x); 
      g = (int)*(ptr+3*x+1); 
      b = (int)*(ptr+3*x+2); 

      Image1->Canvas->Pixels[y][x] = RGB(r,g,b); 
     } 
     ptr += 3*h; 
    } 

Wo in ApiFunc, diese zu finden sind:

void __fastcall TAPIFunc::LoadDll(HINSTANCE m_hMain) 
{ 
    //some others above 
    GET_IMAGE_PIXELS = (func_GET_IMAGE_PIXELS )GetProcAddress(m_hMain, "GET_IMAGE_PIXELS"); 
    //some others below 
} 
unsigned char* __fastcall TAPIFunc::GetImagePixels(void) 
{ 
    return GET_IMAGE_PIXELS(); 
} 

So, jetzt, was ich bisher versucht, ich habe versucht, byte [mit ] als Rückgabeparameter, aber das wirft eine MarshalDirectiveException.

[DllImport("ImageTest.dll")] 
    public static extern IntPtr GET_IMAGE_PIXELS(); 

    private void OpenImage(string filename) 
    { 
     OPEN_IMAGE_FILE(filename); 
     ALLOC_MEMORY(); 
     int width = GET_IMAGE_WIDTH(); //=800 
     int height = GET_IMAGE_HEIGHT(); //=600 
     IntPtr buffer = GET_IMAGE_PIXELS(); 
     int size = width * height * 3;//not sure what the size must be, I think this is one of the issues, just following logic of one answer below. 

     //but source: https://stackoverflow.com/a/16300450/2901207 
     byte[] bitmapImageArray = new byte[size]; 
     Marshal.Copy(buffer, bitmapImageArray, 0, size); 

     Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb); 
     BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); 
     IntPtr pNative = bmData.Scan0; 
     Marshal.Copy(imageData, 0, pNative,size); 
     bitmap.UnlockBits(bmData); 
     bitmap.Save(Environment.CurrentDirectory + @"\result.bmp"); 
      using (var ms = new MemoryStream(bitmapImageArray)) 
      { 
       //both throw exception: Parameter is not valid 
       Bitmap bmp = new Bitmap(ms); 
       Image bitmapImage = Image.FromStream(ms); 
      } 
    } 

Antwort Quellen:

answer 1, using bitmap.LockBits method answers 2 and 3, using memoryStream

Nur um sicherzustellen, dass ich ein gültiges Bild mit zu testen, habe ich ein Bild in Photoshop gespeichert, die mit dieser Option: photoshoppie

hässliches Testbild:

rotated example image

Ergebnis: awesome result

Schön ist es nicht? :)

Auch versucht mit einer for-Schleife, und laufen, bis es abstürzt. lief bis count = 1441777 und auf einer anderen Bildanzahl = 1527793 (gleiche Abmessungen).

Antwort

2

Es scheint aus dem falschen Ergebnis, dass die horizontale/vertikale tatsächlich geschaltet wird. Ihr Bild wird mit Pixeln erhalten, die spaltenweise anstelle von Reihen nach Reihen (wie üblich) organisiert sind.

Dies wird durch die Beispielquelle bestätigt: Die äußere Schleife geht nach w (Breite) und die innere Schleife nach h (Höhe), obwohl die äußere Variable y und die innere Variable x ist, was verwirrend ist .

Es scheint auch, dass die R-und B-Komponenten geschaltet sind (Ich habe hier nicht die Erklärung, aber vertrau mir).

Daher wird, nachdem das Array erhalten hat unter Verwendung von

byte [] bitmapImageArray = new byte [size]; Marshal.Copy (Puffer, BitmapImageArray, 0, Größe);

Sie müssen es reorganisieren.einen anderen Puffer bitmapImageArray2 der gleichen Größe zuweisen, Schleife über alle Pixel (Zeile für Zeile oder Spalte für Spalte, wie Sie bevorzugen, aber mit der richtigen Benennung der Variablen im Gegensatz zum Beispiel: x geht zu w und y geht zu h), und schreiben sie es an das Ziel-Array wie folgt aus:

bitmapImageArray2[(y * w + x) * 3] = bitmapImageArray[(x * h + y) * 3 + 2]; 
bitmapImageArray2[(y * w + x) * 3 + 1] = bitmapImageArray[(x * h + y) * 3 + 1]; 
bitmapImageArray2[(y * w + x) * 3 + 2] = bitmapImageArray[(x * h + y) * 3]; 

Hinweis: Ihr Wert für size korrekt zu sein scheint.

+0

Vielen Dank! Ich wollte gerade eine Antwort posten, die die gleiche Schleife macht wie sie, aber eine solche Bitmap zu erstellen, scheint ein bisschen intensiv zu sein. von 341ms bis 11ms, indem ich das zuerst mache und dann dem Rest meiner Lösung folge – CularBytes

0

Ok, während dies das Problem löst, bin ich immer noch nicht zufrieden, natürlich bin ich glücklich, ein Ergebnis zu bekommen, aber es dauert zu lange, meiner Meinung nach. Es dauert vom Instantiieren der BitMap bis zum Speichern: 241ms. Während das vielleicht klein aussieht, ist das Abrufen von Bildern wie diesem, die ein flüssiges Video produzieren müssen, ein bisschen falsch.

@ Dims Antwort half, indem diese Umwandlung zuerst und dann wie zuvor die Geschwindigkeit dramatisch erhöht.

So das Ergebnis:

private void OpenImage(string filename) 
    { 
     OPEN_IMAGE_FILE(filename); 
     ALLOC_MEMORY(); 
     int width = GET_IMAGE_WIDTH(); 
     int height = GET_IMAGE_HEIGHT(); 
     IntPtr buffer = GET_IMAGE_PIXELS(); 
     int size = width * height * 3; 
     byte[] bitmapImageArray = new byte[size]; 
     Marshal.Copy(buffer, bitmapImageArray, 0, size); 
     Bitmap bitmap3 = ConvertImage3(bitmapImageArray, height, width); 
    } 

    public Bitmap ConvertImage3(byte[] imageData, int height, int width) 
    { 
     int size = width * height * 3; 
     byte[] bitmapImageArray2 = new byte[size]; 
     for (int y = 0; y < height; y++) 
     { 
      for (int x = 0; x < width; x++) 
      { 
       bitmapImageArray2[(y * width + x) * 3] = imageData[(x * height + y) * 3 + 2]; 
       bitmapImageArray2[(y * width + x) * 3 + 1] = imageData[(x * height + y) * 3 + 1]; 
       bitmapImageArray2[(y * width + x) * 3 + 2] = imageData[(x * height + y) * 3]; 
      } 
     } 

     Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb); 
     BitmapData bmData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat); 
     IntPtr pNative = bmData.Scan0; 
     Marshal.Copy(bitmapImageArray2, 0, pNative, width * height * 3); 
     bitmap.UnlockBits(bmData); 
     return bitmap; 
    } 

Jetzt muss ich irgendwie die Geschwindigkeit immer das Bild von der .DLL verbessern, aber das ist natürlich für diese Frage des Umfangs aus.