Xamarin Forms - изменение размера изображения камеры

Кто-то помог мне получить этот код для фотографирования с помощью камеры xamarin forms labs:

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;
                });

Он сохраняет изображение, но фактический размер сделанного телефона огромен, есть ли способ изменить его размер.


person user3841879    schedule 12.08.2014    source источник
comment
мое предложение сработало?   -  person Sten Petrov    schedule 14.08.2014


Ответы (4)


ОБНОВЛЕНИЕ: исходный ответ бесполезен, см. Обновленный ответ ниже. Проблема заключалась в том, что библиотека PCL работала очень медленно и потребляла слишком много памяти.

ОРИГИНАЛЬНЫЙ ОТВЕТ (не использовать):

Я нашел библиотеку ввода-вывода изображений, ImageTools-PCL, которую я разветвил на github и урезал то, что не компилировалось в Xamarin, минимизируя изменения, и результат вроде бы работает.

Чтобы использовать его, загрузите связанный репозиторий, скомпилируйте его с помощью Xamarin и добавьте библиотеки DLL из папки Build в свой проект Forms.

Чтобы изменить размер изображения, вы можете сделать это (должно соответствовать контексту вашего вопроса)

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));

ОБНОВЛЕННЫЙ ОТВЕТ:

Получите Xamarin.XLabs из nuget, узнайте об использовании Resolver, создайте интерфейс IImageService с помощью метода Resize.

Реализация для 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);
                }
            }
        }
    }
}

Реализация сервиса для 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);
        }
    }
}
}
person Sten Petrov    schedule 12.08.2014
comment
Большое спасибо. Мне относительно легко удалось получить эту рабочую кроссплатформенную версию благодаря вашей вилке ImageTools-PCL. - person Timothy Lee Russell; 12.01.2015
comment
Выглядит очень многообещающе! Просто интересно, почему бы не опубликовать отдельный NuGet для Xamarin? :) - person rudyryk; 20.02.2015
comment
@rudyryk см. обновленный ответ, поэтому я не публиковал его на Git или тем более на nuget - person Sten Petrov; 20.02.2015
comment
@StenPetrov Понятно. Наконец, я использовал решение, подобное предложенному вами в обновленном ответе, но ImageTools в какой-то момент все еще выглядят интересными. Я думаю, мы можем использовать собственные методы для кодирования / декодирования и методы манипуляции ImageTools, такие как фильтрация и т. Д. Что вы думаете? - person rudyryk; 20.02.2015
comment
ImageTools-PCL был действительно медленным и раздутым, я бы придерживался собственных методов. Я использую тот же подход для обрезки и поворота изображений, в iOS также есть фильтрация (не уверен в Android) - person Sten Petrov; 20.02.2015
comment
Может кто-нибудь объяснить, какие параметры использовать для ResizeImage? SourceFile - это путь к месту на диске, где находится фотография? Я представляю, что мне следует вызвать этот метод, когда я только что сделал фото? Пожалуйста, дай мне знать! - person user2915962; 25.03.2015
comment
Этот алгоритм производит только грубое изменение размера. Вы можете увидеть это особенно на изображениях с текстом, где вы видите пиксели. Было бы неплохо, если бы существовал алгоритм с более плавным масштабированием ... - person testing; 25.06.2020

Ответ @Sten может встретить out-of-memory на некоторых устройствах Android. Вот мое решение для реализации функции ResizeImage, которая соответствует "Загрузка Эффективно большие растровые изображения » документ:

public void ResizeImage (string sourceFile, string targetFile, int reqWidth, int reqHeight)
{ 
    if (!File.Exists (targetFile) && File.Exists (sourceFile)) {   
        var downImg = decodeSampledBitmapFromFile (sourceFile, reqWidth, reqHeight);
        using (var outStream = File.Create (targetFile)) {
            if (targetFile.ToLower ().EndsWith ("png"))
                downImg.Compress (Bitmap.CompressFormat.Png, 100, outStream);
            else
                downImg.Compress (Bitmap.CompressFormat.Jpeg, 95, outStream);
        }
        downImg.Recycle();
    }
}

public static Bitmap decodeSampledBitmapFromFile (string path, int reqWidth, int reqHeight)
{
    // First decode with inJustDecodeBounds=true to check dimensions
    var options = new BitmapFactory.Options ();
    options.InJustDecodeBounds = true;
    BitmapFactory.DecodeFile (path, options);

    // Calculate inSampleSize
    options.InSampleSize = calculateInSampleSize (options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.InJustDecodeBounds = false;
    return BitmapFactory.DecodeFile (path, options);
}

public static int calculateInSampleSize (BitmapFactory.Options options, int reqWidth, int reqHeight)
{
    // Raw height and width of image
    int height = options.OutHeight;
    int width = options.OutWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {
        int halfHeight = height / 2;
        int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
               && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}
person Jon    schedule 08.10.2015
comment
Я вижу проблему с этим решением. Из-за inSampleSize вы не можете точно указать определенный размер изображения, который вы хотели бы иметь. Это степень двойки, и она приближается, но не к указанным размерам. Это может быть более эффективным с точки зрения памяти, но это не помогает мне в достижении моей цели. - person testing; 19.06.2020

Вы можете сделать это изначально для каждой платформы и использовать интерфейс. Вот пример для IOS

В вашем проекте PCL вам нужно добавить интерфейс

public interface IImageResizer
{
    byte[] ResizeImage (byte[] imageData, double width, double height);
}

Затем, чтобы изменить размер изображения в коде, вы можете загрузить реализацию этого интерфейса в IOS с помощью DependencyService и запустить метод ResizeImage.

var resizer = DependencyService.Get<IImageResizer>();
var resizedBytes = resizer.ResizeImage (originalImageByteArray, 400, 400);
Stream stream = new MemoryStream(resizedBytes);
image.Source = ImageSource.FromStream(stream);

Реализация IOS, добавьте этот класс в свой проект IOS.

[assembly: Xamarin.Forms.Dependency (typeof (ImageResizer_iOS))]
namespace YourNamespace
{
    public class ImageResizer_iOS : IImageResizer
    {

        public byte[] ResizeImage (byte[] imageData, double maxWidth, double maxHeight)
        {
            UIImage originalImage = ImageFromByteArray (imageData);


            double width = 300, height = 300;

            double maxAspect = (double)maxWidth / (double)maxHeight;
            double aspect = (double)originalImage.Size.Width/(double)originalImage.Size.Height;

            if (maxAspect > aspect && originalImage.Size.Width > maxWidth) {
                //Width is the bigger dimension relative to max bounds
                width = maxWidth;
                height = maxWidth / aspect;
            }else if (maxAspect <= aspect && originalImage.Size.Height > maxHeight){
                //Height is the bigger dimension
                height = maxHeight;
                width = maxHeight * aspect;
            }

            return originalImage.Scale(new SizeF((float)width,(float)height)).AsJPEG ().ToArray ();
        }


        public static MonoTouch.UIKit.UIImage ImageFromByteArray(byte[] data)
        {
            if (data == null) {
                return null;
            }

            MonoTouch.UIKit.UIImage image;
            try {
                image = new MonoTouch.UIKit.UIImage(MonoTouch.Foundation.NSData.FromArray(data));
            } catch (Exception e) {
                Console.WriteLine ("Image load failed: " + e.Message);
                return null;
            }
            return image;
        }
    }
}
person Steve    schedule 09.10.2014

Обновление из Xamarin Media Plugin позволяет изменять размер изображения https://github.com/jamesmontemagno/MediaPlugin ... за исключением этого, и вам нужен более общий вариант изменения размера (скажем, изображение поступает из веб-вызова, а не устройства, тогда посмотрите: https://github.com/InquisitorJax/Wibci.Xamarin.Images

person InquisitorJax    schedule 10.11.2016