Home > front end >  How to properly work with ImageData from Camera in Xamarin?
How to properly work with ImageData from Camera in Xamarin?

Time:03-27

I know there are a lot of articles about using the camera out there but sadly nothing helped me out.

My project seemed to be simple: Take a photo (don't save it), crop a part and create a new image from it (don't save it) and use this for further calculations.

My written code already worked fine at the emulator but fails at my Galaxy phone.

I used the CameraView of the Xamarin.CommunityToolkit:

xmlns:xct="clr-namespace:Xamarin.CommunityToolkit.UI.Views;assembly=Xamarin.CommunityToolkit"

<xct:CameraView x:Name="xctCameraView"
                Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"                
                CaptureMode="Photo"
                MediaCaptured="MediaCaptured"                             
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand" 
                IsVisible="True" /> 

In the MediaCaptured event I want to work with the captured photo:

private void MediaCaptured(object sender, MediaCapturedEventArgs e)
{
    var imageData = e.ImageData;

    var cropRectSize = overlayView.GetOverlayRectSize();
    var resultImage = GetCroppedImage(imageData, cropRectSize);
    
    if (resultImage != null)
    {
        ...

        var image = SKImage.FromPixels(resultImage.PeekPixels());
        var encoded = image.Encode();
        var stream = encoded.AsStream();

        imgView.Source = ImageSource.FromStream(() => stream);
    } 
}

Here is the void where the error on my Galaxy phone appeared (originally I used "using" with all disposable objects below with the same result):

private SKBitmap GetCroppedImage(byte[] imageData, float[] cropRectSize)
{
    if (cropRectSize.Length != 2)
            return null;

    var cropRectWidth = cropRectSize[0];
    var cropRectHeight = cropRectSize[1];

    var memStream = new MemoryStream(imageData);

    var bitmap = SKBitmap.Decode(memStream); // <= here it failes (*)

    var imageWidthMid = bitmap.Width / 2;
    var imageHeightMid = bitmap.Height / 2;

    var cropRect = new Xamarin.Forms.Rectangle(imageWidthMid - cropRectWidth / 2,
        imageHeightMid - cropRectHeight / 2,
        cropRectWidth, cropRectHeight);

    SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
                                  (int)cropRect.Height);

    SKRect dest = new SKRect(0, 0, (float)cropRect.Width, (float)cropRect.Height);
    SKRect source = new SKRect((float)cropRect.Left, (float)cropRect.Top,
                               (float)cropRect.Right, (float)cropRect.Bottom);

    var canvas = new SKCanvas(croppedBitmap);
    canvas.DrawBitmap(bitmap, source, dest);

    return croppedBitmap;
}

(*) The catched error is as follows:

Value cannot be null.
Parameter name: buffer
at SkiaSharp.SKManagedStream.OnReadManagedStream(System.IntPtr buffer, System.IntPtr size) [0x0000d] in <...>:0 
at SkiaSharp.SKManagedStream.OnRead(System.IntPtr size) [0x00006] in <...>:0 
at SkiaSharp.SKAbstractManagedStream.ReadInternal (System.IntPtr s, System.Void* context, System.Void* buffer, System.IntPtr size) [0x00015] in <...>:0
at (wrapper native-to-managed) SkiaSharp.SKAbstractManagedStream.ReadInternal(intptr,void*,void*,intptr)
at (wrapper native-to-managed) SkiaSharp.SkiaApi.sk_codec_new_from_stream(intptr,SkiaSharp.SKCodecResult*)
at SkiaSharp.SKCodec.Create (SkiaSharp.SKStream stream, SkiaSharp.SKCodecResult& result) [0x0003b] in <...>:0
at SkiaSharp.SKCodec.Create (System.IO.Stream stream, SkiaSharp.SKCodecResult& result) [0x00006] in <...>:0
at SkiaSharp.SKCodec.Create (System.IO.Stream stream) [0x00000] in <...>:0
at SkiaSharp.SKBitmap.Decode (System.IO.Stream stream) [0x0000e] in <...>:0
at GetCroppedImage(System.Byte[] imageData, System.Single[] cropRectSize [0x00049]) in <...>:0

Any help would be very appreciated!

Thanks in advance! Sebastian.

CodePudding user response:

Looks like a typo. In your MediaCaptured method you have:

var imageData = e.ImageData;

var cropRectSize = overlayView.GetOverlayRectSize();
var resultImage = GetCroppedImage(_imageData, cropRectSize);

You never use imageData, but rather use _imageData, which is probably the cause of the null reference.

CodePudding user response:

It is a SkyiaSharp issue and seems to appear on Samsung phones:

https://github.com/mono/SkiaSharp/issues/1551

There is a workaround out there. After changing my GetCroppedImage void to

private SKBitmap GetCroppedImage(Stream stream, float[] cropRectSize)
{
    if (cropRectSize.Length != 2)
        return null;

    var cropRectWidth = cropRectSize[0];
    var cropRectHeight = cropRectSize[1];

    using (var inputStream = new SKManagedStream(stream))
    {
        using (var inputData = SKData.Create(inputStream))
        {
            var bitmap = SKBitmap.Decode(inputData);

            var imageWidthMid = bitmap.Width / 2;
            var imageHeightMid = bitmap.Height / 2;

            var cropRect = new Xamarin.Forms.Rectangle(imageWidthMid - cropRectWidth / 2,
                imageHeightMid - cropRectHeight / 2,
                cropRectWidth, cropRectHeight);

            SKBitmap croppedBitmap = new SKBitmap((int)cropRect.Width,
                                          (int)cropRect.Height);

            SKRect dest = new SKRect(0, 0, (float)cropRect.Width, (float)cropRect.Height);
            SKRect source = new SKRect((float)cropRect.Left, (float)cropRect.Top,
                                       (float)cropRect.Right, (float)cropRect.Bottom);

            var canvas = new SKCanvas(croppedBitmap);
            canvas.DrawBitmap(bitmap, source, dest);

            return croppedBitmap;
        }
    } 
}

It is perfectly working now!

Regards. Sebastian.

  • Related