Xamarin Forms – Resize Camera Picture

xamarin.formsxamarin.forms.labs

Someone helped me get this code for taking a picture using xamarin forms labs camera:

picker = DependencyService.Get<IMediaPicker> ();  
                task = picker.TakePhotoAsync (new CameraMediaStorageOptions {
                    DefaultCamera = CameraDevice.Rear, 
                    MaxPixelDimension = 800,

                });

                img.BackgroundColor = Color.Gray;

                Device.StartTimer (TimeSpan.FromMilliseconds (250), () => {
                    if (task != null) {
                        if (task.Status == TaskStatus.RanToCompletion) {
                            Device.BeginInvokeOnMainThread (async () => {
                                //img.Source = ImageSource.FromStream (() => task.Result.Source);
                                var fileAccess = Resolver.Resolve<IFileAccess> ();
                                string imageName = "img_user_" + User.CurrentUser().id + "_" + DateTime.Now.ToString ("yy_MM_dd_HH_mm_ss") + ".jpg";
                                fileName = imageName;

                                fileAccess.WriteStream (imageName, task.Result.Source);
                                fileLocation = fileAccess.FullPath(imageName);

                                FileStream fileStream = new FileStream(fileAccess.FullPath(imageName), FileMode.Open, System.IO.FileAccess.Read);
                                imageUrl = (string)test[0]["url"];
                                img.Source = imageUrl;
                            }); 
                        }

                            return  task.Status != TaskStatus.Canceled
                            && task.Status != TaskStatus.Faulted
                            && task.Status != TaskStatus.RanToCompletion;
                    }
                    return true;
                });

It saves the image, but the actual size of the phone picture taken is huge, is there a way to resize it.

Best Solution

UPDATE: The original answer is not useful, see below for updated answer. The issue was the PCL library was very slow and consumed too much memory.

ORIGINAL ANSWER (do not use):

I found an image I/O library, ImageTools-PCL, which I forked on github and trimmed down what wouldn't compile in Xamarin, keeping the modifications to minimum and the result seems to work.

To use it download the linked repository, compile it with Xamarin and add the DLLs from Build folder to your Forms project.

To resize an image you can do this (should fit the context of your question)

var decoder = new   ImageTools.IO.Jpeg.JpegDecoder ();
ImageTools.ExtendedImage inImage = new ImageTools.ExtendedImage ();

decoder.Decode (inImage, task.Result.Source); 

var outImage = ImageTools.ExtendedImage.Resize (inImage, 1024, new ImageTools.Filtering.BilinearResizer ());

var encoder = new ImageTools.IO.Jpeg.JpegEncoder ();
encoder.Encode (outImage, fileAccess.CreateStream (imageName));


ImageSource imgSource = ImageSource.FromFile (fileAccess.FullPath (imageName));

UPDATED ANSWER:

Get Xamarin.XLabs from nuget, learn about using Resolver, create an IImageService interface with Resize method.

Implementation for iOS:

public class ImageServiceIOS: IImageService{
   public void ResizeImage(string sourceFile, string targetFile, float maxWidth, float maxHeight)
    {  
        if (File.Exists(sourceFile) && !File.Exists(targetFile))
        {
            using (UIImage sourceImage = UIImage.FromFile(sourceFile))
            {  
                var sourceSize = sourceImage.Size;
                var maxResizeFactor = Math.Min(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height);

                if (!Directory.Exists(Path.GetDirectoryName(targetFile)))
                    Directory.CreateDirectory(Path.GetDirectoryName(targetFile));

                if (maxResizeFactor > 0.9)
                {
                    File.Copy(sourceFile, targetFile);
                }
                else
                { 
                    var width = maxResizeFactor * sourceSize.Width;
                    var height = maxResizeFactor * sourceSize.Height;

                    UIGraphics.BeginImageContextWithOptions(new CGSize((float)width, (float)height), true, 1.0f);  
                    //  UIGraphics.GetCurrentContext().RotateCTM(90 / Math.PI);
                    sourceImage.Draw(new CGRect(0, 0, (float)width, (float)height)); 

                    var resultImage = UIGraphics.GetImageFromCurrentImageContext();
                    UIGraphics.EndImageContext();


                    if (targetFile.ToLower().EndsWith("png"))
                        resultImage.AsPNG().Save(targetFile, true);
                    else
                        resultImage.AsJPEG().Save(targetFile, true);
                }
            }
        }
    }
}

Implementation of the service for Android:

public class ImageServiceDroid: IImageService{
public void ResizeImage(string sourceFile, string targetFile, float maxWidth, float maxHeight)
{ 
    if (!File.Exists(targetFile) && File.Exists(sourceFile))
    {   
        // First decode with inJustDecodeBounds=true to check dimensions
        var options = new BitmapFactory.Options()
        {
            InJustDecodeBounds = false,
            InPurgeable = true,
        };

        using (var image = BitmapFactory.DecodeFile(sourceFile, options))
        {  
            if (image != null)
            {
                var sourceSize = new Size((int)image.GetBitmapInfo().Height, (int)image.GetBitmapInfo().Width);

                var maxResizeFactor = Math.Min(maxWidth / sourceSize.Width, maxHeight / sourceSize.Height);

                string targetDir = System.IO.Path.GetDirectoryName(targetFile);
                if (!Directory.Exists(targetDir))
                    Directory.CreateDirectory(targetDir);

                if (maxResizeFactor > 0.9)
                { 
                    File.Copy(sourceFile, targetFile);
                }
                else
                { 
                    var width = (int)(maxResizeFactor * sourceSize.Width);
                    var height = (int)(maxResizeFactor * sourceSize.Height);

                    using (var bitmapScaled = Bitmap.CreateScaledBitmap(image, height, width, true))
                    {
                        using (Stream outStream = File.Create(targetFile))
                        {
                            if (targetFile.ToLower().EndsWith("png"))
                                bitmapScaled.Compress(Bitmap.CompressFormat.Png, 100, outStream);
                            else
                                bitmapScaled.Compress(Bitmap.CompressFormat.Jpeg, 95, outStream);
                        }
                        bitmapScaled.Recycle();
                    }
                }

                image.Recycle();
            }
            else
                Log.E("Image scaling failed: " + sourceFile);
        }
    }
}
}
Related Question