question

AndreaFarina-0549 avatar image
0 Votes"
AndreaFarina-0549 asked JarvanZhang-MSFT commented

Rotation on image from CameraView

How can I rotate the image (is byte[]) captured by CamerView (Xamarin Community Toolkit) since rotation doesn't have a setter?

dotnet-xamarinforms
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

JarvanZhang-MSFT avatar image
0 Votes"
JarvanZhang-MSFT answered ShinichiHezemans-9652 edited

Hello AndreaFarina-0549,​

Welcome to our Microsoft Q&A platform!

since rotation doesn't have a setter

The CamearView class inherits from View class, and View class inherits from VisualElement class. The Rotation property is provided by the VisualElement class, the property has set and get method. You could change the value of the Rotation property to rotate the image captured by the CameraView control directly.

Here is the related doc about the Rotation property:
https://docs.microsoft.com/en-us/dotnet/api/xamarin.forms.visualelement.rotationproperty?view=xamarin-forms

Best Regards,

Jarvan Zhang


If the response is helpful, please click "Accept Answer" and upvote it.

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


· 4
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

That rotation property rotates the CameraView preview, but not the file produced by the shot. That is always turned on its side. Unfortunately the output file has the [] byte of the image, its ImageSource, but apparently no Exif information. In this way it is, at least to me, impossible to rotate the image at will. I tried to convert it to Bitmap but no rotation method seems to work.

0 Votes 0 ·

You can also use SkiaSharp.

 private async Task PhotoCapturedAsync(MediaCapturedEventArgs arg)
 {
 SKBitmap photo = BitmapExtensions.Rotate90(SKBitmap.Decode(arg.ImageData));
 ImageSource imageSource = ImageSource.FromStream(() => SKImage.FromBitmap(photo).Encode().AsStream());
 }

helper class

  static class BitmapExtensions
     {
         public static SKBitmap Rotate90(SKBitmap original)
         {
             SKBitmap rotated = new SKBitmap(original.Height, original.Width);
    
             using (var surface = new SKCanvas(rotated))
             {
                 surface.Translate(rotated.Width, 0);
                 surface.RotateDegrees(90);
                 surface.DrawBitmap(original, 0, 0);
             }
    
             return rotated;
    
         }
    
     }

encode() does take some time.

1 Vote 1 ·

Hi, AndreaFarina-0549. I've reproduced the issue on my side. I am trying to get a soultion and will come back later.

0 Votes 0 ·

The System.Drawing api cannot used in Xamarin, we need to achieve the function on native platform instead. For example, rotate the bitmap and return the byte array on Android.

[assembly: Dependency(typeof(DroidImplementation))]
namespace TestApplication_6.Droid
{
    public class DroidImplementation : ITestInterface
    {
        public byte[] RotateImage(byte[] bytes)
        {
            Bitmap bitmap = BitmapFactory.DecodeByteArray(bytes,0,bytes.Length);

            Matrix matrix = new Matrix();
            matrix.PreRotate(90);
            var rotatedBitmap = Bitmap.CreateBitmap(bitmap,0,0,bitmap.Width,bitmap.Height,matrix,true);

            MemoryStream stream = new MemoryStream();
            rotatedBitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);

            return stream.ToArray();
        }
    }
}

Consume the code in the shared project.

private void CameraView_MediaCaptured(object sender, Xamarin.CommunityToolkit.UI.Views.MediaCapturedEventArgs e)
{
    //get byte array from the imageSource
    StreamImageSource streamImageSource = (StreamImageSource)e.Image;
    var cancellationToken = System.Threading.CancellationToken.None;
    Task<Stream> task = streamImageSource.Stream(cancellationToken);
    Stream stream = task.Result;
    byte[] bytesAvailable = new byte[stream.Length];
    stream.Read(bytesAvailable, 0, bytesAvailable.Length);

    var bytes = DependencyService.Get<ITestInterface>().RotateImage(bytesAvailable);
    img.Source = ImageSource.FromStream(() => { return new MemoryStream(bytes); });
}
0 Votes 0 ·
AndreaFarina-0549 avatar image
0 Votes"
AndreaFarina-0549 answered ShinichiHezemans-9652 commented

Unfortunately I had already tried these solutions. I would like to avoid native platforms. And using SkiaSharp when I convert SKBitmap back to bytes[] has not undergone any rotation. But I need a byte[].

· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

skiasharp works for me when rotating an image made from the xamarin community toolkit cameraview. Just make sure you output the rotated bitmap and not the original.
you can read the stream and convert them to byte[].

0 Votes 0 ·
JarvanZhang-MSFT avatar image
0 Votes"
JarvanZhang-MSFT answered AndreaFarina-0549 commented

And using SkiaSharp when I convert SKBitmap back to bytes[] has not undergone any rotation. But I need a byte[].

@AndreaFarina-0549 Try to get the stream for the ImageSource and then use SKBitmap.Decode(stream) command to generate a SKBitmap iobject.

Here is the related code, you could refer to it:

private void CameraView_MediaCaptured(object sender, Xamarin.CommunityToolkit.UI.Views.MediaCapturedEventArgs e)
{
    StreamImageSource streamImageSource = (StreamImageSource)e.Image;
    System.Threading.CancellationToken cancellationToken = System.Threading.CancellationToken.None;
    Task<Stream> task = streamImageSource.Stream(cancellationToken);
    Stream stream = task.Result;

    SKBitmap photo = RotateBitmap(SKBitmap.Decode(stream));
    img.Source = ImageSource.FromStream(() => SKImage.FromBitmap(photo).Encode().AsStream());
}

SKBitmap RotateBitmap(SKBitmap bitmap)
{
    SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);
    using (var surface = new SKCanvas(rotatedBitmap))
    {
        surface.Translate(rotatedBitmap.Width, 0);
        surface.RotateDegrees(90);
        surface.DrawBitmap(bitmap, 0, 0);
    }
    return rotatedBitmap;
}
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Unfortunately this method returns this error to me:

System.ArgumentNullException: 'Value cannot be null.
Parameter name: buffer'

0 Votes 0 ·

Hi, AndreaFarina-0549. Which parameter is the 'buffer'? Could you please post more details about it and share the related code?

0 Votes 0 ·
AndreaFarina-0549 avatar image
0 Votes"
AndreaFarina-0549 answered JarvanZhang-MSFT edited

135390-immagine-2021-09-27-093940.jpg



· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Please make sure you've captured a photo. Here is the complete sample code, please check:

<Button Text="button" Clicked="Button_Clicked" />
<xct:CameraView x:Name="cameraView" HeightRequest="150" MediaCaptured="CameraView_MediaCaptured"/>
<Image x:Name="img" HeightRequest="50"/>

Page.xaml.cs

public partial class TestPage : ContentPage
{
    public TestPage()
    {
        InitializeComponent();
    }

    private async void Button_Clicked(object sender, EventArgs e)
    {
        cameraView.Shutter();
    }

    private void CameraView_MediaCaptured(object sender, Xamarin.CommunityToolkit.UI.Views.MediaCapturedEventArgs e)
    {
        StreamImageSource streamImageSource = (StreamImageSource)e.Image;
        System.Threading.CancellationToken cancellationToken = System.Threading.CancellationToken.None;
        Task<Stream> task = streamImageSource.Stream(cancellationToken);
        Stream stream = task.Result;
        SKBitmap photo = RotateBitmap(SKBitmap.Decode(stream));
        img.Source = ImageSource.FromStream(() => SKImage.FromBitmap(photo).Encode().AsStream());
    }

    SKBitmap RotateBitmap(SKBitmap bitmap)
    {
        SKBitmap rotatedBitmap = new SKBitmap(bitmap.Height, bitmap.Width);
        using (var surface = new SKCanvas(rotatedBitmap))
        {
            surface.Translate(rotatedBitmap.Width, 0);
            surface.RotateDegrees(90);
            surface.DrawBitmap(bitmap, 0, 0);
        }
        return rotatedBitmap;
    }
}
0 Votes 0 ·
AndreaFarina-0549 avatar image
0 Votes"
AndreaFarina-0549 answered

135503-immagine-2021-09-27-101558.jpg







5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

AndreaFarina-0549 avatar image
0 Votes"
AndreaFarina-0549 answered JarvanZhang-MSFT commented

135495-immagine-2021-09-27-101703.jpg



· 1
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

The code works fine on my side. Someone faced the similar issue with SkBitmap.Create method and has reported the issue to github repo. Here is the case link, this may be a potential issue with SkiaSharp. You could follow the topic to get a solution.

0 Votes 0 ·