Why did I failed to convert IntPtr to byte[]? - c#

I'm working on a project with making DLL to do model's inference from C#.
During the inference process, we do the pre-processing, inference, and post-processing to complete the work.
At the beginning, I write all these process in one DLL, and it works fine.
But for the concept of "modularity", my boss ask me to split these process into three different DLLs.
So, the ideal dataflow should be like this:
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)
But during the process of passing cv::Mat* to C# and pass the byte[] converted from Intptr,
the input data(cv::Mat) is not the same as it showed from the original cv2.Mat(Intptr). And here's the code I write for doing these(it's simplified...):
//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);
}
}
}
To sum up, so far I've done the conversion from IntPtr to byte[] successfully:
dataflow: IntPtr >> Mat >> Bitmap >> byte[]
But it'll take too much time for the conversion(in my opinion...), so I want to make it more simple
I also tried another way of conversion but it still failed:
dataflow: IntPtr >> Mat >> byte[]
with this code
//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...)
I don't know where did I do wrong...
Any help or advice is greatly appreciated!!

Related

C# Linux Framebuffer Unsafe byte[] to CairoSharp ImageSurface

I am trying to create an image surface in c# CairoSharp using these two constructors:
public ImageSurface(byte[] data, Format format, int width, int height, int stride); public ImageSurface(IntPtr data, Format format, int width, int height, int stride);
I am trying to get the array of the linux framebuffer from a memorymappedfile:
var file = MemoryMappedFile.CreateFromFile("/dev/fb0", FileMode.Open, null, (3840 * 2160 * (32 / 8)));
I know I have to use an unsafe context to get it but am unsure the proper syntax to get the sequential pointer from the memeoryMapped object.
The constructors for the ImageSurface will not work with the MemoryMappedFile directly. You will have to Read bytes from the MemoryMappedFile and use those bytes to create the ImageSurface.
I never used C# on Linux before so I don't really know if all those objects are available but maybe like this?
private static void test()
{
Bitmap bmp = (Bitmap)Image.FromFile("some image");
BitmapData imgData = null;
try
{
imgData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.ReadWrite,
bmp.PixelFormat
);
int finalLength = imgData.Stride * imgData.Height;
byte[] buf = new byte[finalLength];
IntPtr ptr = imgData.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, buf, 0, finalLength);
bmp.UnlockBits(imgData);
// Pointer to first byte lives inside of fixed context.
unsafe
{
fixed (byte* p = buf)
{
// your cairo code...
}
}
// Alternative...
var ptPinned = System.Runtime.InteropServices.GCHandle.Alloc(
buf, System.Runtime.InteropServices.GCHandleType.Pinned);
IntPtr ptCairo = ptPinned.AddrOfPinnedObject();
ptPinned.Free();
}
finally
{
if (imgData != null) {
bmp.UnlockBits(imgData);
}
}
}
In any case I am certain that you have to pass the pointer of an already allocated buffer. In the test above I just loaded the image pixels of a bitmap into a byte array. In order to get the pointer you Marshal it or use fixed. That is all on Windows though.

How to transfer an image from C++ to Unity3D using OpenCV?

I have a trouble in transferring an image from C++ to Unity3D(C#). I used OpenCV to read a Mat type image and get the data point with img.data. When I build the dll and call the corresponding function, I get error with all data in the bytes array is 0, so I want to know how to transfer an image from C++ to C#. Thank you.
Sorry for late reply,
code shown as the follows:
C++ code:
DLL_API BYTE* _stdcall MatToByte()
{
Mat srcImg = imread("F:\\3121\\image\\image_0\\000000.png");
nHeight = srcImg.rows;
nWidth = srcImg.cols;
nBytes = nHeight * nWidth * nFlag / 8;
if (pImg)
delete[] pImg;
pImg = new BYTE[nBytes];
memcpy(pImg, srcImg.data, nBytes);
return pImg;
}
C# code
[DllImport("Display")]
public static extern IntPtr MatToByte();
byte[] Bytes = new byte[nBytes];
IntPtr tmp = MatToByte();
Marshal.Copy(tmp, Bytes, 0, nBytes);
Texture2D texture = new Texture2D(width, height);
texture.LoadImage(Bytes);
left_plane.GetComponent<Renderer>().material.mainTexture = texture;

How do I send an image from C# to a DLL accepting OpenCV Mat properly?

I have a C++ class for which I created a C DLL to be used in our C# solution. Now I need to send images to the DLL, but I don't know the proper way of doing this. This is the C++ function signature:
std::vector<Prediction> Classify(const cv::Mat& img, int N = 2);
And this is how I went about it. Currently I tried to create this wrapper method in the DLL:
#ifdef CDLL2_EXPORTS
#define CDLL2_API __declspec(dllexport)
#else
#define CDLL2_API __declspec(dllimport)
#endif
#include "../classification.h"
extern "C"
{
CDLL2_API void Classify_image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results = 2);
//...
}
Code in the dll:
CDLL2_API void Classify_image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results)
{
auto classifier = reinterpret_cast<Classifier*>(GetHandle());
cv::Mat img = cv::Mat(height, width, CV_32FC3, (void*)img_pointer);
std::vector<Prediction> result = classifier->Classify(img, top_n_results);
//misc code...
*length_of_out_result = ss.str().length();
}
and in the C# code I wrote :
[DllImport(#"CDll2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void Classify_image(byte[] img, uint height, uint width, byte[] out_result, out int out_result_length, int top_n_results = 2);
private string Classify(Bitmap img, int top_n_results)
{
byte[] result = new byte[200];
int len;
var img_byte = (byte[])(new ImageConverter()).ConvertTo(img, typeof(byte[]));
Classify_image(img_byte, (uint)img.Height, (uint)img.Width,res, out len, top_n_results);
return ASCIIEncoding.ASCII.GetString(result);
}
but whenever I try to run the code, I get Access violation error:
An unhandled exception of type 'System.AccessViolationException'
occurred in Classification Using dotNet.exe
Additional information: Attempted to read or write protected memory.
This is often an indication that other memory is corrupt.
and exception error says:
{"Attempted to read or write protected memory. This is often an
indication that other memory is corrupt."}
Deeper investigation into the code made it clear that, I get the exception error in this function:
void Classifier::Preprocess(const cv::Mat& img, std::vector<cv::Mat>* input_channels)
{
/* Convert the input image to the input image format of the network. */
cv::Mat sample;
if (img.channels() == 3 && num_channels_ == 1)
cv::cvtColor(img, sample, cv::COLOR_BGR2GRAY);
else if (img.channels() == 4 && num_channels_ == 1)
cv::cvtColor(img, sample, cv::COLOR_BGRA2GRAY);
else if (img.channels() == 4 && num_channels_ == 3)
cv::cvtColor(img, sample, cv::COLOR_BGRA2BGR);
else if (img.channels() == 1 && num_channels_ == 3)
cv::cvtColor(img, sample, cv::COLOR_GRAY2BGR);
else
sample = img;
//resize image according to the input
cv::Mat sample_resized;
if (sample.size() != input_geometry_)
cv::resize(sample, sample_resized, input_geometry_);
else
sample_resized = sample;
cv::Mat sample_float;
if (num_channels_ == 3)
sample_resized.convertTo(sample_float, CV_32FC3);
else
sample_resized.convertTo(sample_float, CV_32FC1);
cv::Mat sample_normalized;
cv::subtract(sample_float, mean_, sample_normalized);
/* This operation will write the separate BGR planes directly to the
* input layer of the network because it is wrapped by the cv::Mat
* objects in input_channels. */
cv::split(sample_normalized, *input_channels);
CHECK(reinterpret_cast<float*>(input_channels->at(0).data)
== net_->input_blobs()[0]->cpu_data())
<< "Input channels are not wrapping the input layer of the network.";
}
The access violation occurs when it is attempted to resize the image meaning running this snippet:
//resize image according to the input
cv::Mat sample_resized;
if (sample.size() != input_geometry_)
cv::resize(sample, sample_resized, input_geometry_);
Further investigation and debugging (here) made the culprit visible!
This approach proved to be plain wrong, or at the very least buggy. Using this code, the image on the C++ side seemed to have been initialized properly, the number of channels, the height, and width all seemed fine.
But the moment you attempt to use the image, either by resizing it or even showing it using imshow(), it would crash the application and be giving an access violation exception, the very same error that happened when resizing and is posted in the question.
Looking at this answer, I changed the C# code responsible for handing the image to the dll. the new code is as follows :
//Dll import
[DllImport(#"CDll2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void Classify_Image(IntPtr img, uint height, uint width, byte[] out_result, out int out_result_length, int top_n_results = 2);
//...
//main code
Bitmap img = new Bitmap(txtImagePath.Text);
BitmapData bmpData = img.LockBits(new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
result = Classify_UsingImage(bmpData, 1);
img.UnlockBits(bmpData); //Remember to unlock!!!
and the C++ code in the DLL:
CDLL2_API void Classify_Image(unsigned char* img_pointer, unsigned int height, unsigned int width, char* out_result, int* length_of_out_result, int top_n_results)
{
auto classifier = reinterpret_cast<Classifier*>(GetHandle());
cv::Mat img = cv::Mat(height, width, CV_8UC3, (void*)img_pointer, Mat::AUTO_STEP);
std::vector<Prediction> result = classifier->Classify(img, top_n_results);
//...
*length_of_out_result = ss.str().length();
}
And doing so rectified all access violations I was getting previously.
Although I can now easily send images from C# to the DLL, I have some issue with the current implementation of mine. I don't know how to send the OpenCV type from C# to the needed function, currently I'm using a hardcoded image type as you can see, and this begs the question, what should I do when my input image is grayscale or even a png with 4 channels?
After Trying many different approaches, I guess it would be beneficial to other people who seek to the the same thing, to know this.
To cut a very long story short (see this question), The best way that I could find is this (as #EdChum says it) :
I'd pass the file as memory to your openCV dll, this should be able
to call imdecode which will sniff the file type, additionally you can
pass the flag
And also explained here to send a pointer to the DLL and there use imdecode to decode the image. This solved a lot of issues other approaches introduced. And will also save you alot of headaches.
Here is the code of intrest:
This is how my functions in the DLL and C# should have looked :
#ifdef CDLL2_EXPORTS
#define CDLL2_API __declspec(dllexport)
#else
#define CDLL2_API __declspec(dllimport)
#endif
#include "classification.h"
extern "C"
{
CDLL2_API void Classify_Image(unsigned char* img_pointer, long data_len, char* out_result, int* length_of_out_result, int top_n_results = 2);
//...
}
The actual method :
CDLL2_API void Classify_Image(unsigned char* img_pointer, long data_len,
char* out_result, int* length_of_out_result, int top_n_results)
{
auto classifier = reinterpret_cast<Classifier*>(GetHandle());
vector<unsigned char> inputImageBytes(img_pointer, img_pointer + data_len);
cv::Mat img = imdecode(inputImageBytes, CV_LOAD_IMAGE_COLOR);
cv::imshow("img just recieved from c#", img);
std::vector<Prediction> result = classifier->Classify(img, top_n_results);
//...
*length_of_out_result = ss.str().length();
}
Here is the C# Dll Import:
[DllImport(#"CDll2.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void Classify_Image(byte[] img, long data_len, byte[] out_result, out int out_result_length, int top_n_results = 2);
and This is the actual method sending the image back to the DLL:
private string Classify_UsingImage(Bitmap image, int top_n_results)
{
byte[] result = new byte[200];
int len;
Bitmap img;
if (chkResizeImageCShap.Checked)
img = ResizeImage(image, int.Parse(txtWidth.Text), (int.Parse(txtHeight.Text)));
else
img = image;
ImageFormat fmt = new ImageFormat(image.RawFormat.Guid);
var imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(codec => codec.FormatID == image.RawFormat.Guid);
//this is for situations, where the image is not read from disk, and is stored in the memort(e.g. image comes from a camera or snapshot)
if (imageCodecInfo == null)
{
fmt = ImageFormat.Jpeg;
}
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms,fmt);
byte[] image_byte_array = ms.ToArray();
Classify_Image(image_byte_array, ms.Length, result, out len, top_n_results);
}
return ASCIIEncoding.ASCII.GetString(result);
}

PictureBox.Handle Intptr to Image or Bitmap

I need help with HikVision IPCam Video streaming. I searched for 2 hole weeks without luck.
My problem is the IPCamm DLL stream the image into a picturebox using PictureBox.Handle. Its working perfectly fine:
[DllImport("HCNetSDK.dll")]
public static extern int NET_DVR_RealPlay_V30(int lUserID, ref NET_DVR_CLIENTINFO lpClientInfo, RealDataCallBack_V30 fRealDataCallBack_V30, IntPtr pUser, bool bBlocked);
this.realDataCallBack = new RealDataCallBack_V30(RealDataCallback);
this.clientInfo.hPlayWnd = PictureBox.Handle;
this.clientInfo.lChannel = channel;
this.clientInfo.lLinkMode = 0;
this.playHandle = NET_DVR_RealPlay_V30(this.userID, ref this.clientInfo, realDataCallBack, IntPtr.Zero, true);
My Issue is that I need to process the image but I couldn't have any way to capture the image as Bitmap or Image and then display it As I like.
I tried Bitmap.FromHbitmap(PictureBox.Handle), Tried some MemoryMarshel solutions with no luck.
My Only way to get it now is by getting the data from call back functions which is with lower quality, lower frame-count, ...
This snippet draws the data from the handle into a bitmap and then sets the image of the picturebox. The CopyFromScreen line might not be necessary on older systems.
PictureBox.Image = CaptureControl(PictureBox.Handle, PictureBox.Width, PictureBox.Height);
// PictureBox.Image now contains the data that was drawn to it
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
public Bitmap CaptureControl(IntPtr handle, int width, int height)
{
Bitmap controlBmp;
using (Graphics g1 = Graphics.FromHwnd(handle))
{
controlBmp = new Bitmap(width, height, g1);
using (Graphics g2 = Graphics.FromImage(controlBmp))
{
g2.CopyFromScreen(this.Location.X + PictureBox.Left, this.Location.Y + PictureBox.Top, 0, 0, PictureBox.Size);
IntPtr dc1 = g1.GetHdc();
IntPtr dc2 = g2.GetHdc();
BitBlt(dc2, 0, 0, width, height, handle, 0, 0, 13369376);
g1.ReleaseHdc(dc1);
g2.ReleaseHdc(dc2);
}
}
return controlBmp;
}
You need to set hPlayWnd as zero. Set callback function to work on decoded data. I try to understand Hikvision SDK, a few difficult...
lpPreviewInfo.hPlayWnd = IntPtr.Zero;//预览窗口 live view window
m_ptrRealHandle = RealPlayWnd.Handle;
RealData = new CHCNetSDK.REALDATACALLBACK(RealDataCallBack);//预览实时流回调函数 real-time stream callback function
m_lRealHandle = CHCNetSDK.NET_DVR_RealPlay_V40(m_lUserID, ref lpPreviewInfo, RealData, pUser);
public void RealDataCallBack(Int32 lRealHandle, UInt32 dwDataType, IntPtr pBuffer, UInt32 dwBufSize, IntPtr pUser)
//解码回调函数
private void DecCallbackFUN(int nPort, IntPtr pBuf, int nSize, ref PlayCtrl.FRAME_INFO pFrameInfo, int nReserved1, int nReserved2)
{
// 将pBuf解码后视频输入写入文件中(解码后YUV数据量极大,尤其是高清码流,不建议在回调函数中处理)
if (pFrameInfo.nType == 3) //#define T_YV12 3
{
// FileStream fs = null;
// BinaryWriter bw = null;
// try
// {
// fs = new FileStream("DecodedVideo.yuv", FileMode.Append);
// bw = new BinaryWriter(fs);
// byte[] byteBuf = new byte[nSize];
// Marshal.Copy(pBuf, byteBuf, 0, nSize);
// bw.Write(byteBuf);
// bw.Flush();
// }
// catch (System.Exception ex)
// {
// MessageBox.Show(ex.ToString());
// }
// finally
// {
// bw.Close();
// fs.Close();
// }
}
}
See the source code

How to create a BitmapImage from a pixel byte array (live video display)

I need to display live images on a WPF control. I'm looking for the fastest way to do this using WPF.
I'm capturing images from a camera using its dll API (AVT).
The image is writen by the dll and the camera rises a callback with an IntPtr to a Image struct called tFrame (described below). The pixel data is stored at the ImageBuffer propertie with is an InPtr to a byte array.
I know how to create a Bitmap from the pixel byte array, but not a BitmapImage. So it is possible to create a Bitmap and then create a BitmapImagem from it.
Here there is a way to create a BitmapImage from a Bitmap on the memory. But I want to create the BitmapImage directly from the data source (tFrame). How can I do that?
I know that BitmapImage have a CopyPixels method, but it laks of a SetPixels.
public struct tFrame
{
public IntPtr AncillaryBuffer;
public uint AncillaryBufferSize;
public uint AncillarySize;
public tBayerPattern BayerPattern;
public uint BitDepth;
public tFrameCtx Context;
public tImageFormat Format;
public uint FrameCount;
public uint Height;
public IntPtr ImageBuffer;
public uint ImageBufferSize;
public uint ImageSize;
public uint RegionX;
public uint RegionY;
public tErr Status;
public uint TimestampHi;
public uint TimestampLo;
public uint Width;
}
Here is how I create a Bitmap from a pixel byte array. This was used at the WinForm version of the software.
private void CreateBitmap(tFrame frame)
{
//This sample is for a 8bpp captured image
PixelFormat pxFormat = PixelFormat.Format8bppIndexed;
//STRIDE
//[https://stackoverflow.com/questions/1983781/why-does-bitmapsource-create-throw-an-argumentexception/1983886#1983886][3]
//float bitsPerPixel = System.Drawing.Image.GetPixelFormatSize(format);
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
//Number of bits used to store the image data per line (only the valid data)
int validBitsPerLine = ((int)frame.Width) * bitsPerPixel;
//4 bytes for every int32 (32 bits)
int stride = ((validBitsPerLine + 31) / 32) * 4;
Bitmap bmp = new Bitmap((int)frame.Width, (int)frame.Height, stride, pxFormat, frame.ImageBuffer);
}
EDIT 1:
Thanks to dr.mo, now I'm able to display 60 FPS 1024x1024 images with ~3% CPU usage!
What I'm doing is:
//# UI Thread
public WriteableBitmap wbm = new WriteableBitmap(1024, 1024, (double)96, (double)96, System.Windows.Media.PixelFormats.Gray8, null);
this.wbBackBuffer = this.wbm.BackBuffer;
//This can be called by a timer in the UI thread or at the grab Thread for every image, the CPU usage is almost the same.
void UpdateDisplayImage()
{
wbm.Lock();
wbm.AddDirtyRect(new Int32Rect(0, 0, wbm.PixelWidth, wbm.PixelHeight));
wbm.Unlock();
}
//# Grab Thread
//Update the backbuffer with new camera image data.
UpdateBackBuffer(...);
/// <summary>
/// [http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx]
/// </summary>
public void UpdateBackBuffer(IntPtr pData, int w, int h, int ch)
{
//Can not acess wbm from outside UI thread
//CopyMemory(wbm.BackBuffer, pData, (uint)(w * h * ch));
//I dont know if it is safe to write to it buffer like this:
CopyMemory(this.wbBackBuffer, pData, (uint)(w * h * ch));
}
This should do the trick. it's super fast.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System.ComponentModel;
public class MakeBitmapSource
{
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory")]
public static extern void CopyMemory(IntPtr Destination, IntPtr Source, uint Length);
public static BitmapSource FromNativePointer(IntPtr pData, int w, int h, int ch)
{
PixelFormat format = PixelFormats.Default;
if (ch == 1) format = PixelFormats.Gray8; //grey scale image 0-255
if (ch == 3) format = PixelFormats.Bgr24; //RGB
if (ch == 4) format = PixelFormats.Bgr32; //RGB + alpha
WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
CopyMemory(wbm.BackBuffer, pData, (uint)(w * h * ch));
wbm.Lock();
wbm.AddDirtyRect(new Int32Rect(0, 0, wbm.PixelWidth, wbm.PixelHeight));
wbm.Unlock();
return wbm;
}
public static BitmapSource FromArray(byte[] data, int w, int h, int ch)
{
PixelFormat format = PixelFormats.Default;
if (ch == 1) format = PixelFormats.Gray8; //grey scale image 0-255
if (ch == 3) format = PixelFormats.Bgr24; //RGB
if (ch == 4) format = PixelFormats.Bgr32; //RGB + alpha
WriteableBitmap wbm = new WriteableBitmap(w, h, 96, 96, format, null);
wbm.WritePixels(new Int32Rect(0, 0, w, h), data, ch * w, 0);
return wbm;
}
}

Categories

Resources