2016-04-29 35 views
3

爲了測試目的,我收到了一個.DLL文件,該.dll文件包含稍後將用於處理來自硬件的實時圖像的功能。從.DLL獲取位圖,轉換爲字節[]和圖像

對於這個簡單的.dll,我可以打開一個圖像(加載到內存中),獲取寬度和高度,並獲取需要轉換爲圖像的像素。加載,獲取寬度和獲取高度是好的,但獲取像素並將其轉換爲位圖或圖像是一個問題。凡在ApiFunc,這可以發現

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; 
    } 

:我收到

C++示例源

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(); 
} 

所以,現在我已經試過到目前爲止,我已經使用字節[嘗試]作爲返回參數,但是會拋出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); 
      } 
    } 

答案來源:

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

只是爲了確保我有一個有效的圖像有,我保存在Photoshop的圖像,使用此選項進行測試: photoshoppie

難看的測試圖片:

rotated example image

結果: awesome result

美麗的,不是嗎? :)

也嘗試使用for循環,並運行,直到它崩潰。跑到計數= 1441777和另一個圖像計數= 1527793(相同的尺寸)。

 int count = 0; 
     for (int i = 0; i < width * height * 4; i++) 
     { 
      count++; 
      bitmapImageArray[i] = Marshal.ReadByte(buffer, i); 
     } 

回答

2

從錯誤的結果看來,水平/垂直是實際切換的結果。您的圖像是通過按列組織列的像素獲取的,而不是按行的行(如通常那樣)。

這是由您收到的示例源確認的:雖然外部變量是y,內部變量是x,但令人困惑的是,外部循環進入w(寬度),內部循環進入h(高度) 。

它也似乎R和B組件切換(我沒有解釋,在這裏,但相信我)。

因此,在已經獲得使用

字節[] bitmapImageArray =新的字節[大小]的陣列; Marshal.Copy(buffer,bitmapImageArray,0,size);

您必須重新組織它。分配相同大小的另一個緩衝區bitmapImageArray2,循環遍歷所有像素(逐行或逐列,如您所願,但正確命名變量不同於示例:x轉至w,y轉至h),以及它寫入到目標數組這樣的:

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]; 

注:您的size值似乎是正確的。

+0

謝謝!我正要發佈一個與他們做的循環相同的答案,但像這樣構建一個位圖似乎有點密集。先從341毫秒到11毫秒,然後按照我的解決方案 – CularBytes

0

好的,雖然這確實能解決問題,但我仍然不滿意,當然我很樂意得到一些結果,但在我看來,這需要很長時間。從實例化BitMap到保存之前需要241ms。雖然這可能看起來很小,但檢索像這樣需要產生流暢視頻的圖像有點不對。

 for (int y = 0; y < width; y++) 
     { 
      for (int x = 0; x < height; x++) 
      { 
       int red = imageData[offset + 3 * x] ; 
       int green = imageData[offset + 3 * x + 1]; 
       int blue = imageData[offset + 3 * x + 2]; 
       Color color = Color.FromArgb(red, green, blue); 
       bitmap.SetPixel(y, x, color); 
      } 
      offset += 3 * height; 
     } 

@ Dim的回答很有幫助,先做了這個轉換,然後像以前一樣處理,大大提高了速度。

所以結果:

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; 
    } 

現在我必須以某種方式提高從.DLL獲取圖像的速度,但是那是當然的了作用域這個問題。