【问题标题】:iPhone image orientation wrong when resizing with SkiaSharp使用 SkiaSharp 调整大小时 iPhone 图像方向错误
【发布时间】:2017-10-26 04:29:29
【问题描述】:

我在 Xamarin.Forms 项目的 SkiaSharp 中使用 SKBitmap.Resize() 方法来调整图像大小以进行显示。我遇到的问题是在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() 只是获取照片存储位置的特定于平台的路径的助手。

【问题讨论】:

  • 您应该始终阅读图像上的本机方向 (EXIF),因为 iPhone 的肖像通常被标记为 UIImageOrientation.Right,在 Android 上,事情变得非常混乱,因为不同的制造商有时会将他们的相机传感器安装为顺时针 90 度或逆时针(三星和 LG 以 90 度旋转而臭名昭著,我什至使用了几个中国的 Android 设备,传感器安装了 180 度,大多数情况下这是由于物理制造和包装限制,故事的寓意,阅读图像在应用任何转换之前旋转... ;-)
  • 您可以通过SKCodec.Origin 获取Exif 方向,并从SKCodecOrigin 确定您需要应用的适当转换。
  • 所以在抓住它之后,我似乎无法更改 SKCodec.Origin,因为它是只读的,我将图像作为字节数组输出到 Xamarin.Forms Image.ImageSource 我可以' t 似乎也可以处理旋转/转换。我想我必须添加特定于平台的代码来处理位图方向的变化。

标签: image image-processing xamarin xamarin.forms skiasharp


【解决方案1】:

对于那些有同样问题的人,我做了以下工作,并很乐意接受有关改进的意见。

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

【讨论】:

  • 赞成关于如何从流(或字节[])中获取来源的信息:SKCodec.Create,谢谢
【解决方案2】:

SkiaSharp 实际上并没有为我提供操作和更改图像方向的方法。最后,当我使用特定于平台的代码捕获并保存图像时,我最终改变了方向。

【讨论】:

    【解决方案3】:

    调整大小和处理方向的可行解决方案

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

    【讨论】:

      【解决方案4】:

      谢谢@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;
              }
          }
      

      【讨论】:

      猜你喜欢
      • 2018-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-31
      相关资源
      最近更新 更多