从.DLL获取位图,转换为byte[],再转换为图像

3

我收到了一个用于测试的.dll文件,这个.dll包括了以后将用于处理硬件实时图像的功能。

对于这个简单的.dll文件,我可以打开一张图片(加载到内存中),获取宽度和高度,并获取需要转换成图片的像素。加载、获取宽度和获取高度都没问题,但是获取像素并将其转换为Bitmap或Image是一个问题。

我收到的C++示例源代码:

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

在ApiFunc中,可以找到这个:

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

目前我尝试过的是将byte[]作为返回参数,但这会抛出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://dev59.com/gmbWa4cB1Zd3GeqPZrd9#16300450
        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);
            }
    }

答案来源:

答案1,使用bitmap.LockBits方法 答案2和3,使用memoryStream

为了确保我有一个有效的图像进行测试,我在Photoshop中保存了一张图片,并选择了该选项: photoshoppie

丑陋的测试图像:

rotated example image

结果: awesome result

很美吧? :)

还尝试使用for循环,一直运行到崩溃。在另一张图片上运行到count = 1441777,在相同维度的另一张图片上运行到count = 1527793。

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

2
从错误的结果来看,水平/垂直实际上被交换了。您的图像是通过按列而不是按行(通常情况下)组织像素获得的。
这一点在您收到的示例源代码中得到了证实:外部循环到w(宽度),内部循环到h(高度),尽管外部变量为y,内部变量为x,这很令人困惑。
还有一个问题是R和B分量被交换了(我在这里没有解释,但请相信我)。
因此,在使用以下代码获取数组后:
byte[] bitmapImageArray = new byte[size]; 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值似乎是正确的。

谢谢!我本来要发布一个与他们相同的循环答案,但是构建这样的位图似乎有点繁重。通过首先执行此操作,然后按照我的解决方案的其余部分进行操作,将时间从341毫秒缩短到11毫秒。 - CularBytes

0

好的,虽然这解决了问题,但我仍然不满意,当然我很高兴能得到一些结果,但在我看来,它花费的时间太长了。从实例化BitMap到保存之前需要241毫秒。虽然这看起来很小,但检索像这样需要生成流畅视频的图像有点不对。

        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获取图像的速度,但这当然超出了这个问题的范围。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接