从 .DLL 获取位图,转换为 byte[] 和图像
Get bitmap from .DLL, convert to byte[] and to image
我收到了一个用于测试目的的 .DLL,这个 .dll 包含稍后用于处理来自硬件的实时图像的功能。
对于这个简单的.dll,我可以打开一个图像(加载到内存中),获取宽度和高度,并获取需要转换为图像的像素。加载、获取宽度和获取高度都很好,但是获取像素并将其转换为位图或图像是个问题。
我收到的 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[] 作为 return 参数,但是会抛出 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:
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 中保存了一张图像,并使用了以下选项:
丑陋的测试图像:
结果:
很美吧? :)
还尝试使用 for 循环,运行 直到它崩溃。 运行 直到计数 = 1441777 并且在另一张图像上计数 = 1527793(相同尺寸)。
int count = 0;
for (int i = 0; i < width * height * 4; i++)
{
count++;
bitmapImageArray[i] = Marshal.ReadByte(buffer, i);
}
从错误的结果看来,horizontal/vertical确实被切换了。您的图像是通过像素按列组织的,而不是按行组织的(通常是这样)。
您收到的示例源证实了这一点:外层循环转到 w(宽度),内层循环转到 h(高度),尽管外部变量是 y,内部变量是 x,即令人困惑。
R 和 B 组件似乎也被调换了(我在这里没有解释,但请相信我)。
因此,在使用
获得数组后
byte[] bitmapImageArray = new byte[size];
Marshal.Copy(缓冲区, bitmapImageArray, 0, 大小);
你必须重组它。分配另一个相同大小的缓冲区 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
值似乎是正确的。
好的,虽然这确实解决了问题,但我仍然不满意,当然我很高兴得到一些结果,但我认为这需要很长时间。从实例化 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 获取图像的速度,但这当然超出了这个问题的范围。
我收到了一个用于测试目的的 .DLL,这个 .dll 包含稍后用于处理来自硬件的实时图像的功能。
对于这个简单的.dll,我可以打开一个图像(加载到内存中),获取宽度和高度,并获取需要转换为图像的像素。加载、获取宽度和获取高度都很好,但是获取像素并将其转换为位图或图像是个问题。
我收到的 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[] 作为 return 参数,但是会抛出 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:
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 中保存了一张图像,并使用了以下选项:
丑陋的测试图像:
结果:
很美吧? :)
还尝试使用 for 循环,运行 直到它崩溃。 运行 直到计数 = 1441777 并且在另一张图像上计数 = 1527793(相同尺寸)。
int count = 0;
for (int i = 0; i < width * height * 4; i++)
{
count++;
bitmapImageArray[i] = Marshal.ReadByte(buffer, i);
}
从错误的结果看来,horizontal/vertical确实被切换了。您的图像是通过像素按列组织的,而不是按行组织的(通常是这样)。
您收到的示例源证实了这一点:外层循环转到 w(宽度),内层循环转到 h(高度),尽管外部变量是 y,内部变量是 x,即令人困惑。
R 和 B 组件似乎也被调换了(我在这里没有解释,但请相信我)。
因此,在使用
获得数组后byte[] bitmapImageArray = new byte[size]; Marshal.Copy(缓冲区, bitmapImageArray, 0, 大小);
你必须重组它。分配另一个相同大小的缓冲区 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
值似乎是正确的。
好的,虽然这确实解决了问题,但我仍然不满意,当然我很高兴得到一些结果,但我认为这需要很长时间。从实例化 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 获取图像的速度,但这当然超出了这个问题的范围。