Load Image from pointer in CUDA with export of dll

442 views Asked by At

I am new to this kind of stuff. I'm trying to create a function which is equivalent to the histogram function of an image. I am using windows forms application to show the histogram (and load the image) and CUDA/c++ to make the histogram. I am mentioning from the beginning that I am NOT using openCV, glut, OpenGL or any other third library. Carrying on... I am trying to pass a bitmap to an unmanaged c++ DLL. The problem here is that I don't now how to reference that bitmap in the c++ code. (And even how to get the RGB out of it). Snippets of code:

c# :

private void calculateHistogram(object sender, EventArgs e)
{
    Bitmap bmp = (Bitmap)pictureBox1.Image;
    unsafe {
        int** matrixAcumulated;

        var date = bmp.LockBits(new Rectangle(0, 0, pictureBox1.Width, pictureBox1.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);

        matrixAcumulated=NativeMethods.GenerateHistogram(date.Scan0, pictureBox1.Width);

        bmp.UnlockBits(date);

        // Write the string to a file.
        System.Console.WriteLine(matrixAcumulated[0][0]);
    }
}

Dll import :

using System;
using System.Runtime.InteropServices;

namespace HistogramProcessingCs
{
    class NativeMethods
    {
        [DllImport("HistogramProcessingCpp.dll", CallingConvention = CallingConvention.StdCall)]
        public static extern unsafe int** GenerateHistogram(IntPtr bmp, int dimensionImage);
    }
}

c++ :

extern "C" __declspec(dllexport)  int** __stdcall GenerateHistogram(unsigned char *bmp, int dimensionImage)
{
    //How to refere the bitmap from the bmp pointer?
    //dimensionImage is Width = Height
}
1

There are 1 answers

2
realvictorprm On BEST ANSWER

Okay I worked out something after 2 hours of googling, stackoverflowing and microsoft documentationing!

Because you're using CUDA I think you only want fast and nice solutions, that's why I tried to find a way with which you can modify data without copying it many times only because of C# and C++ connection.

That's what I've done so far.

Some things are important. I used an integer pointer, that's a bit messy with the current code. You can of course use instead a char pointer which makes much more sense (haven't tested this). Another thing is the System.Drawing.Imaging.PixelFormat. It's really important to know which you have choosen. In my example I've choosen PixelFormat.Format32bppRgb. The byte order is (so far I learned) just how the name is.

Example:

32bppRgb stands for red, green and blue which consume each 8 bits. However because it's this format and not 24bppRgb it consumes a whole integer(8 bits aren't used). In this case the first 8 bits arent used (left to right thinking) so to set a pixel to red it works like this. (Sorry formating didn't worked as expected...)

| 8 | 8 | 8 | 8 | consuming bits

|empty | red | green | blue | color

| 00 | FF | 00 | 00 | color code for red

So the code for red is this => 0x00 FF 00 00 and as decimal it's 16711680. That's where the number in the C++ comes from.

C++ Code:

Header file "NativeLibrary.h":

namespace NativeLibrary
{
    extern "C" __declspec(dllexport) void __stdcall PassBitmap(int* number, int size);
}

Cpp file "NativeLibrary.cpp":

#include <NativeLibrary.h>

void NativeLibrary::PassBitmap(int* number, int size) {
    for (int i = 0; i < size; i++) {
        number[i] = 16711680;
    }
}

C# Code:

using System.Drawing;
using System.Runtime.InteropServices;

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void PassBitmap(IntPtr bmp, int size);

public System.Drawing.Bitmap bitmap = null;


public void GenerateAndModifyBitmap()
{
    //The pixel format is essential!
    System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(100, 100, System.Drawing.Imaging.PixelFormat.Format32bppRgb);

    //Just which region we want to lock of the bitmap
    System.Drawing.Rectangle rect = new System.Drawing.Rectangle(new System.Drawing.Point(), bmp.Size);

    //We want to read and write to the data, pixel format stays the same (anything else wouldn't make much sense)
    System.Drawing.Imaging.BitmapData data = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmp.PixelFormat);

    //This is a pointer to the data in memory. Can be manipulated directly!
    IntPtr ptr = data.Scan0;

    // This code is specific to a bitmap with 32 bits per pixels.
    // Ignore current calculations. They are still work in progress xD
    int size = bmp.Height;
    size *= Math.Abs(data.Stride);
    size /= 4;
    //Call native function with our pointer to the data and of course how many ints we have
    PassBitmap(ptr, size);
    //Work is finished. Give our data back to the manager
    bmp.UnlockBits(data);
    bitmap = bmp;
}

This code would generate a bitmap which is completely red.