Неправильная ориентация изображения iPhone при изменении размера с помощью SkiaSharp

Я использую метод SKBitmap.Resize () в SkiaSharp в проекте Xamarin.Forms для изменения размера изображений для отображения. Проблема, с которой я сталкиваюсь, заключается в том, что при фотографировании на iOS, когда фотография делается в портретной ориентации, изображение отображается правой стороной вверх. При фотографировании на Android и импорте из фотогалереи на устройстве Android и iOS ориентация сохраняется, а при съемке фотографии в iOS - нет. Если я не изменяю размер изображения с помощью SkiaSharp (просто отображаю изображение без изменения размера), тогда изображение отображается с правильной ориентацией. Однако это не решение, поскольку необходимо изменить размер изображений. Ниже мой код -

private byte[] GetResizedImageData(string imageName)
    {
        float resizeFactor = 0.5f;
        var filePath = PathUtil.GetImagePath(imageName);
        var ogBitmap = SKBitmap.Decode(filePath);

        float fWidth = ogBitmap.Width * resizeFactor;
        int width = (int) Math.Round(fWidth);

        float fHeight = ogBitmap.Height * resizeFactor;
        int height = (int) Math.Round(fHeight);

        if (height >= 4096 || width >= 4096)
        {
            width = width * (int)resizeFactor;
            height = height * (int)resizeFactor;
        }

        var scaledBitmap = ogBitmap.Resize(new SKImageInfo( width, height), SKBitmapResizeMethod.Box);
        var image = SKImage.FromBitmap(scaledBitmap);
        var data = image.Encode(SKEncodedImageFormat.Jpeg, 100);

        return data.ToArray();
    }

PathUtil.GetImagePath () - это просто помощник для получения зависящих от платформы путей для хранения фотографий.


person marcus    schedule 25.05.2017    source источник
comment
Вы всегда должны читать исходную ориентацию на изображении (EXIF), поскольку портреты iPhone обычно помечаются как UIImageOrientation.Right. На Android все становится действительно беспорядочным, поскольку разные производители иногда устанавливают датчики камеры на 90 градусов по часовой стрелке или против часовой стрелки (Samsumg и LG известны тем, что поворачивают на 90 градусов. , Я даже использовал пару китайских устройств Android, где датчики установлены на 180 градусов, в большинстве случаев это связано с физическими ограничениями производства и упаковки, моралью истории, прочитайте поворот изображения перед применением каких-либо преобразований ...; - )   -  person SushiHangover    schedule 25.05.2017
comment
Вы можете получить ориентацию Exif через SKCodec.Origin и из SKCodecOrigin определить соответствующее преобразование, которое вам нужно применить.   -  person SushiHangover    schedule 25.05.2017
comment
Итак, схватив это, я не могу изменить SKCodec.Origin, поскольку он только для чтения, и я вывожу изображение в виде байтового массива в Xamarin.Forms Image.ImageSource, где я не могу обрабатывать вращение / преобразование или. Думаю, мне придется добавить код платформы для обработки изменения ориентации растрового изображения.   -  person marcus    schedule 14.06.2017


Ответы (4)


Для тех, у кого возникла такая же проблема, я сделал следующее и с радостью приму предложения по улучшениям.

        public static SKBitmap HandleOrientation(SKBitmap bitmap, SKCodecOrigin orientation)
    {
        SKBitmap rotated;
        switch (orientation)
        {
            case SKCodecOrigin.BottomRight:

                using (var surface = new SKCanvas(bitmap))
                {
                    surface.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
                    surface.DrawBitmap(bitmap.Copy(), 0, 0);
                }

                return bitmap;

            case SKCodecOrigin.RightTop:                                                 
                rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                using (var surface = new SKCanvas(rotated))
                {
                    surface.Translate(rotated.Width, 0);
                    surface.RotateDegrees(90);
                    surface.DrawBitmap(bitmap, 0, 0);
                }

                return rotated;

            case SKCodecOrigin.LeftBottom:
                rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                using (var surface = new SKCanvas(rotated))
                {
                    surface.Translate(0, rotated.Height);
                    surface.RotateDegrees(270);
                    surface.DrawBitmap(bitmap, 0, 0);
                }

                return rotated; 

            default:                       
                return bitmap;            
        }

Затем используйте следующее, чтобы получить исходную ориентацию.

            // TODO: Improve this.. I do not know how to "reset" 
            // the inputStream, so new it up twice. :/
            using (var inputStream = new SKManagedStream(imageIn))
            {
                using (var codec = SKCodec.Create(inputStream))
                {
                    orientation = codec.Origin;
                }
            }

....... Потом

SKBitmap orientedWExif = HandleOrientation(resized, orientation);
person ttugates    schedule 10.08.2017
comment
проголосовали за информацию о том, как получить происхождение из потока (или byte []): SKCodec.Create, спасибо - person A.J.Bauer; 03.09.2017

На самом деле SkiaSharp не предоставил мне способ манипулировать и изменять ориентацию изображения. В конце концов, я изменил ориентацию, когда сделал снимок и сохранил изображение, используя специфичный для платформы код.

person marcus    schedule 15.06.2017

Работоспособное решение для изменения размера и ориентации ручки

public class ImageResizer : IImageResizer
{
    private const int Quality = 75;

    public byte[] Resize(byte[] data, int newWidth, int newHeight)
    {
        using (var inputStream = new SKMemoryStream(data))
        {
            using (var codec = SKCodec.Create(inputStream))
            {
                using (var original_old = SKBitmap.Decode(codec))
                {
                    int sourceWidth = original_old.Width;
                    int sourceHeight = original_old.Height;

                    float nPercentW = ((float) newWidth / (float) sourceWidth);
                    float nPercentH = ((float) newHeight / (float) sourceHeight);

                    float nPercent = nPercentH < nPercentW ? nPercentH : nPercentW;

                    int destWidth = (int) (sourceWidth * nPercent);
                    int destHeight = (int) (sourceHeight * nPercent);

                    using (SKBitmap original = original_old.Resize(new SKImageInfo(destWidth, destHeight), SKFilterQuality.Medium))
                    {

                        var useWidth = original.Width;
                        var useHeight = original.Height;
                        Action<SKCanvas> transform = canvas => { };
                        switch (codec.EncodedOrigin)
                        {
                            case SKEncodedOrigin.TopLeft:
                                break;
                            case SKEncodedOrigin.TopRight:
                                // flip along the x-axis
                                transform = canvas => canvas.Scale(-1, 1, useWidth / 2, useHeight / 2);
                                break;
                            case SKEncodedOrigin.BottomRight:
                                transform = canvas => canvas.RotateDegrees(180, useWidth / 2, useHeight / 2);
                                break;
                            case SKEncodedOrigin.BottomLeft:
                                // flip along the y-axis
                                transform = canvas => canvas.Scale(1, -1, useWidth / 2, useHeight / 2);
                                break;
                            case SKEncodedOrigin.LeftTop:
                                useWidth = original.Height;
                                useHeight = original.Width;
                                transform = canvas =>
                                {
                                    // Rotate 90
                                    canvas.RotateDegrees(90, useWidth / 2, useHeight / 2);
                                    canvas.Scale(useHeight * 1.0f / useWidth, -useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2);
                                };
                                break;
                            case SKEncodedOrigin.RightTop:
                                useWidth = original.Height;
                                useHeight = original.Width;
                                transform = canvas =>
                                {
                                    // Rotate 90
                                    canvas.RotateDegrees(90, useWidth / 2, useHeight / 2);
                                    canvas.Scale(useHeight * 1.0f / useWidth, useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2);
                                };
                                break;
                            case SKEncodedOrigin.RightBottom:
                                useWidth = original.Height;
                                useHeight = original.Width;
                                transform = canvas =>
                                {
                                    // Rotate 90
                                    canvas.RotateDegrees(90, useWidth / 2, useHeight / 2);
                                    canvas.Scale(-useHeight * 1.0f / useWidth, useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2);
                                };
                                break;
                            case SKEncodedOrigin.LeftBottom:
                                useWidth = original.Height;
                                useHeight = original.Width;
                                transform = canvas =>
                                {
                                    // Rotate 90
                                    canvas.RotateDegrees(90, useWidth / 2, useHeight / 2);
                                    canvas.Scale(-useHeight * 1.0f / useWidth, -useWidth * 1.0f / useHeight, useWidth / 2, useHeight / 2);
                                };
                                break;
                        }

                        var info = new SKImageInfo(useWidth, useHeight);
                        using (var surface = SKSurface.Create(info))
                        {
                            using (var paint = new SKPaint())
                            {
                                // high quality with antialiasing
                                paint.IsAntialias = true;
                                paint.FilterQuality = SKFilterQuality.High;

                                // rotate according to origin
                                transform.Invoke(surface.Canvas);

                                // draw the bitmap to fill the surface
                                surface.Canvas.DrawBitmap(original, info.Rect, paint);
                                surface.Canvas.Flush();

                                using (SKImage image = surface.Snapshot())
                                {
                                    return image.Encode(SKEncodedImageFormat.Jpeg, Quality).ToArray();
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
person Stanislav Glushak    schedule 17.06.2020

Спасибо @ttugates. Ваш ответ помог мне исправить проблему с поворотом ориентации. Просто хочу обновить свой ответ, так как есть устаревший код.

 using (var inputStream = new SKManagedStream(await file.ReadAllStreamAsync()))
                {
                    using (var codec = SKCodec.Create(inputStream))
                    {
                          orientation = codec.EncodedOrigin;
                    }
                }
  public static SKBitmap HandleOrientation(SKBitmap bitmap, SKEncodedOrigin orientation)
    {
        SKBitmap rotated;
        switch (orientation)
        {
            case SKEncodedOrigin.BottomRight:

                using (var surface = new SKCanvas(bitmap))
                {
                    surface.RotateDegrees(180, bitmap.Width / 2, bitmap.Height / 2);
                    surface.DrawBitmap(bitmap.Copy(), 0, 0);
                }

                return bitmap;

            case SKEncodedOrigin.RightTop:
                rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                using (var surface = new SKCanvas(rotated))
                {
                    surface.Translate(rotated.Width, 0);
                    surface.RotateDegrees(90);
                    surface.DrawBitmap(bitmap, 0, 0);
                }

                return rotated;

            case SKEncodedOrigin.LeftBottom:
                rotated = new SKBitmap(bitmap.Height, bitmap.Width);

                using (var surface = new SKCanvas(rotated))
                {
                    surface.Translate(0, rotated.Height);
                    surface.RotateDegrees(270);
                    surface.DrawBitmap(bitmap, 0, 0);
                }

                return rotated;

            default:
                return bitmap;
        }
    }
person Waaheeda    schedule 13.05.2020
comment
@AmitaiIrron да, я хотел прокомментировать, но у меня не было достаточно очков репутации ... это не позволяло мне комментировать, но позволяло комментировать мой собственный ответ, спасибо stackoverflow.com/users/4022580/amitai-irron - person Waaheeda; 09.09.2020