首页 > 解决方案 > 为什么我无法将 IntPtr 转换为 byte[]?

问题描述

我正在做一个项目,使 DLL 从 C# 中进行模型推断。

在推理过程中,我们做预处理、推理和后处理来完成工作。一开始,我将所有这些进程都写在一个 DLL 中,并且运行良好。但是对于“模块化”的概念,我的老板要求我将这些进程拆分为三个不同的 DLL。

所以,理想的数据流应该是这样的:

                        C#          ||        DLL    (return cv::Mat*)
pre-processing  ||  image->byte[] -----> Mat (...) -> new Mat(result)
                                                            ↓
============================================================↓==============
                        Intptr   <---------------------------
==========================↓================================================
inference       ||      byte[]    -----> Mat (...) -> new Mat(result)
                                                            ↓
============================================================↓==============
                        Intptr   <---------------------------
==========================↓===============================================
post-processing ||      byte[]    -----> Mat (...) -> new Mat(result)

但是在将 cv::Mat* 传递给 C# 并传递从 Intptr 转换的 byte[] 的过程中,输入数据 (cv::Mat)原始 cv2.Mat(Intptr) 显示的数据不同。这是我为此编写的代码(已简化...):

//C++(DLL)
//pre-processing DLL ---------------------------------------------------
//dll_pre.h
extern "C" LIB_API cv::Mat* img_padding(unsigned char* img_pointer, int img_H, int img_W, int* len);

//dll_pre.cpp
LIB_API cv::Mat* img_padding(unsigned char* img_pointer, int img_H, int img_W)
{
    cv::Mat input_pic = cv::Mat(img_H, img_W, CV_8UC(pic_ch), img_pointer);

    //do something...

    *len = input_pic.total() * input_pic.elemSize();

    return new cv::Mat(input_pic);
}
// ---------------------------------------------------------------------

//inference DLL --------------------------------------------------------
//dll_inf.h
extern "C" LIB_API void dll_inference(unsigned char* img_pointer, int img_H, int img_W);

//dll_inf.cpp'
LIB_API void dll_inference(unsigned char* img_pointer, int img_H, int img_W)
{
    cv::Mat input_pic = cv::Mat(img_H, img_W, CV_8UC(pic_ch), img_pointer);

    cv::imwrite("dll_pic.png", input_pic)

    //do something...
}
// ---------------------------------------------------------------------
//C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing; //Bitmap
using System.Drawing.Imaging;
using System.IO; //Memory Stream
using System.Runtime.InteropServices; //Marshal
using System.Diagnostics;//Timer
using OpenCvSharp;


namespace Test_DLL_Console
{
    class Program
    {
        [DllImport(@"dll_pre.dll")]
        private static extern IntPtr img_padding(byte[] img, int img_H, int img_W, out int rt_len);
        [DllImport(@"dll_inf.dll")]
        private static extern void dll_inference(byte[] img, int img_H, int img_W);

        public static byte[] GetRGBValues(Bitmap bmp, out int bmp_w)
        {
            // Lock the bitmap's bits. 
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            System.Drawing.Imaging.BitmapData bmpData =
             bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);

            // Get the address of the first line.
            IntPtr ptr = bmpData.Scan0;

            // Declare an array to hold the bytes of the bitmap.
            int bytes = bmpData.Stride * bmp.Height;
            byte[] rgbValues = new byte[bytes];

            Console.WriteLine($"bytes->{bytes}");
            // Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); bmp.UnlockBits(bmpData);
            bmp_w = bmpData.Stride;
            return rgbValues;
        }

        static void Main()
        {
            Bitmap image = new Bitmap("test_pic.bmp");
            int img_W = 0;
            int rt_len = 0;
            byte[] dst = GetRGBValues(image, out img_W);
            IntPtr pre_res = img_padding(dst, image.Height, img_W, out rt_len);
            //Mat pad_res = new Mat(pre_res); //This is the way I get return picture from DLL in C#
            ////pad_res is different from dll_pic.png I saved from dll_inf
            byte[] btarr = new byte[rt_len];
            Marshal.Copy(pre_res, btarr, 0, rt_len);
            dll_inference(btarr, image.Height, img_W);
        }
    }
}

总结一下,到目前为止,我已经成功地完成了从 IntPtr 到 byte[] 的转换:

数据流:IntPtr >> Mat >> Bitmap >> byte[]

但是转换需要太多时间(在我看来......),所以我想让它更简单

我还尝试了另一种转换方式,但仍然失败:

数据流:IntPtr >> Mat >> byte[]

使用此代码

//C#
byte[] btarr = new byte[rt_len];
Mat X = new Mat(pre_res);
X.GetArray(image.Height, img_W, btarr);
dll_inference(btarr, image.Height, img_W);
//(But the result is also different from the way it should be...)

我不知道我在哪里做错了......非常感谢任何帮助或建议!

标签: c#c++opencvdll

解决方案


推荐阅读