takuya-takeuchi / DlibDotNet

Dlib .NET wrapper written in C++ and C# for Windows, MacOS, Linux and iOS
MIT License
493 stars 135 forks source link

Can find - DlibDotNet.Extensions/Extensions/BitmapExtensions.cs #97

Closed Lucashglund closed 5 years ago

Lucashglund commented 5 years ago

Summary of your issue

Hi there!

I need to convert my System.Drawing.Bitmap to your Array2D<> and back again to my System.Drawing.Bitmap.

I have tried to use the DlibDotNet Extension - BitmapExtensions by I can't find it. It only seems like I can find the "DlibDotNet.Extensions.EnumerableExtensions"..

I am using Visual Studio 2017, and I have added DlibDotNet from "NuGet Packages", and include it "using DlibDotNet;".

I manage to use other functions in your library, just seems like I have a problem with the Extensions...

What am I missing? Please help me :)

takuya-takeuchi commented 5 years ago

Ops. I'm very sorry to trouble you. DlibDotNet.Extension is not included in nuget packge. It is experimental. Because .net core can not handle WritableBitmap. It means that it depends on non-nuget library. But .NET core 3.0 will use WritableBitmap class. So I can create nuget package of DlibDotNet.Extension.

Now, you can clone DlibDotNet and build DlibDotNet.Extension. Don't be afraid. It could be easy.

Lucashglund commented 5 years ago

Thanks for the answer! I have one more question, I did manage to solve the problem and now I have some problem with the conversion.

To make you understand better why I need to make this conversion is because I have a video stream which is of type bitmap, I want to convert each frame to a Array2D<What ever you prefer, for best performance and available in your library> so I can use your face detection and landmark detection and finally convert it back to my bitmap.

When I am converting from bitmap to array2D I get some exceptions from ArrayToBitmap "throw new NotSupportedException(); cause I used UInt8/BYTE" and when I change type the conversion To array give me another one based on type.

Anyhow, I guess I should use RGBpixel somehow. Could you help me out with an example of how I could make such a conversion? Something like: From System.Image.Bitmap -> ToArray2D -> Do some stuff -> BackToBitmap

Finally - What do you think about the time complexity of such implementation? I am in need of having rather good time complexity because of real-time face tracking. I hope this isn't too much to ask for! Anyhow Thanks for an awesome library and code! Great work! @takuya-takeuchi

takuya-takeuchi commented 5 years ago

You can use Matrix class. Matrix class support creating from byte array. Almost DlibDotNet API support Matrix.

You can use the following code.

var bitmap = GetBitmap();
var array = ToManaged(bitmap);
var matrix = new Matrix<byte>(array, bitmap.Height, bitmap,Width);
..
..
array = matrix.ToArray();
// back to array to bitmap like ToManaged

private static byte[] ToManaged(Bitmap bitmap)
{
    var format = bitmap.PixelFormat;
    var width = bitmap.Width;
    var height = bitmap.Height;
    var array = new byte[width * height * 1]; // bitmap is treated as grayscale

    BitmapData bitmapData = null;
    try
    {
        bitmapData = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, width, height),
            ImageLockMode.WriteOnly,
            format);

        var stride = bitmapData.Stride;
        var scan0 = bitmapData.Scan0;
        for (var y = 0; y < height; y++)
        {
                Marshal.Copy(new IntPtr(scan0 + y * stride), array, y * width * 1, width * 1)
        }
    }
    finally
    {
        if (bitmapData != null)
            bitmap.UnlockBits(bitmapData);
    }

    return array;
}
Lucashglund commented 5 years ago

Thanks!! I still have problems with "var scan0 = bitmap.Scan0;" I did try to cast it to an int, but ended up with exceptions under runtime. If I don't cast it I have this error "Cannot convert from 'System.IntPtr to 'int' because of "new IntPtr(scan0 + y * stride)" in Marshal.copy()

takuya-takeuchi commented 5 years ago

Sorry late reply. I should have noted that the above code is not tested :(

y is int. But address value of scan0 may be in range of int64. Could you try cast to int64?

var tmp = ((long) scan0 + y) * stride;
Marshal.Copy(new IntPtr(tmp), array, y * width * 1, width * 1)
Lucashglund commented 5 years ago

Don't worry! Thanks anyway, you helped me more than enough already!

I think I manage to solve it in another way, but I am not 100% sure. "Marshal.Copy(IntPtr.Add(scan0, ystride), array, y width 1, width 1);" seems to work for me, but what is the difference between creating a new IntPtr and Adding Scan0 with an offset of y*stride the way I am doing it?

Just want to make sure that I am doing it right so I don't lose some date because I use add instead. @takuya-takeuchi

takuya-takeuchi commented 5 years ago

Sorry, the above code is wrong.

var tmp = (long) scan0 + y * stride;
Marshal.Copy(new IntPtr(tmp), array, y * width * 1, width * 1)

what is the difference between creating a new IntPtr and Adding Scan0 with an offset of ystride the way I am doing it?

no diffrence!! My approach is old. I didn't think what you told right away. You way is elegant!!

Lucashglund commented 5 years ago

Cool!

If I may ask another question, when I am trying to start with the face detection from your example, "DlibDotNet.Dlib.PyramidUp(matrix); And var dets = detector.Operator(matrix); " Ill receive an Exception "System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'" which seems to come from my conversion back toBitmap function "Marshal.Copy(array, 0, scan0, array.Length);".

I have tried to change the "ImageLockMode" but that doesn't seem to help.. Want me to post my code of the ToBitmap function so u can see what i have done? @takuya-takeuchi

takuya-takeuchi commented 5 years ago

I guess matrix won't be currupt even though using of Marshal.Copy is wrong. Because, If corrupt memory when using Marshal.Copy, CLR throw exception right now.

So I doubt it comes from bug of Matrix constructor(ArrayBase). However, PyramidUp and detector.Operator are well tested. So it may be depends on content of passed image.

Could you show me sample code and problem image if you can?

valerysntx commented 5 years ago

Ops. I'm very sorry to trouble you. DlibDotNet.Extension is not included in nuget packge. It is experimental. Because .net core can not handle WritableBitmap. It means that it depends on non-nuget library. But .NET core 3.0 will use WritableBitmap class. So I can create nuget package of DlibDotNet.Extension.

Now, you can clone DlibDotNet and build DlibDotNet.Extension. Don't be afraid. It could be easy.

18 way to retarget "DlibDotNet.Extentions" to netstandard2.0 / w.tests coverage

valerysntx commented 5 years ago

I have another question.

@Lucashglund why did You not opening another issue here, how did we manage to understand the context of your problem, if need to gather info reading comments from totally unrelated issue names? @takuya-takeuchi could be closed

takuya-takeuchi commented 5 years ago

@Lucashglund Could you open new issue? Welcome to your issue report and future request!! @valerysntx thank you for your advice.