代码之家  ›  专栏  ›  技术社区  ›  Jojodmo

旋转无内存峰值的UIImage

  •  5
  • Jojodmo  · 技术社区  · 10 年前

    我当前正在使用 CGContextDrawImage ,但无论何时调用它,都会有一个巨大的内存峰值。较新的设备可以处理它,但内存较低的设备(如iPhone 4s)无法处理。图像是用户在 AVCaptureSessionPresetHigh

    我当前正在使用此扩展将图像旋转到 UIImage

    func rotate(orientation: UIImageOrientation) -> UIImage{
        if(orientation == UIImageOrientation.Up){return self}
    
        var transform: CGAffineTransform = CGAffineTransformIdentity
    
        if(orientation == UIImageOrientation.Down || orientation == UIImageOrientation.DownMirrored){
            transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height)
            transform = CGAffineTransformRotate(transform, CGFloat(M_PI))
        }
        else if(orientation == UIImageOrientation.Left || orientation == UIImageOrientation.LeftMirrored){
            transform = CGAffineTransformTranslate(transform, self.size.width, 0)
            transform = CGAffineTransformRotate(transform, CGFloat(M_PI_2))
        }
        else if(orientation == UIImageOrientation.Right || orientation == UIImageOrientation.RightMirrored){
            transform = CGAffineTransformTranslate(transform, 0, self.size.height)
            transform = CGAffineTransformRotate(transform,  CGFloat(-M_PI_2))
        }
    
    
        if(orientation == UIImageOrientation.UpMirrored || orientation == UIImageOrientation.DownMirrored){
            transform = CGAffineTransformTranslate(transform, self.size.width, 0)
            transform = CGAffineTransformScale(transform, -1, 1)
        }
        else if(orientation == UIImageOrientation.LeftMirrored || orientation == UIImageOrientation.RightMirrored){
            transform = CGAffineTransformTranslate(transform, self.size.height, 0)
            transform = CGAffineTransformScale(transform, -1, 1);
        }
    
        let ref: CGContextRef = CGBitmapContextCreate(nil, Int(self.size.width), Int(self.size.height), CGImageGetBitsPerComponent(self.CGImage), 0, CGImageGetColorSpace(self.CGImage), CGImageGetBitmapInfo(self.CGImage))
        CGContextConcatCTM(ref, transform)
    
        if(orientation == UIImageOrientation.Left || orientation == UIImageOrientation.LeftMirrored || orientation == UIImageOrientation.Right || orientation == UIImageOrientation.RightMirrored){
            CGContextDrawImage(ref, CGRectMake(0, 0, self.size.height, self.size.width), self.CGImage)
        }
        else{
            CGContextDrawImage(ref, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage)
        }
    
    
        let cgImg: CGImageRef = CGBitmapContextCreateImage(ref)
        let rotated: UIImage = UIImage(CGImage: cgImg)!
    
        return rotated
    }
    

    这段代码在iPhone 5和6等设备上运行良好,但由于内存不足,几乎总是导致iPhone 4s崩溃。

    我知道我可以上传带有指定方向的EXIF数据的图像,但我觉得在上传之前旋转图像可以防止后续问题。

    iOS如何使用EXIF数据旋转图像进行显示?例如,尽管使用

    UIImage(image.CGImage, scale: 1.0, orientation: UIImageOrientation.Right)
    

    更改EXIF数据时,图像仍然以正确的方向显示在屏幕上,对内存的影响要小得多。是否有一些真正的低级代码可以像上面的代码一样使用GPU来实现这一点?

    我还想避免在上传之前缩小图像。

    是否有任何方法可以减少旋转图像的影响,例如将图像旋转成碎片,或者我必须找到另一种方法?

    1 回复  |  直到 10 年前
        1
  •  2
  •   Jojodmo    9 年前

    我能(大部分)通过打电话解决这个问题 UIGraphicsEndImageContext() 在上述代码的末尾,以及在上传之前不旋转图像(用户可以在上传之前旋转照片,因此在上传之前不对其进行旋转会大大减少内存使用)。

    所以,每当用户旋转照片时,我称之为 rotateExif(orientation) 我创建的函数

    extension UIImage{
        func rotateExif(orientation: UIImageOrientation) -> UIImage{
    
            if(orientation == UIImageOrientation.Up){return self}
    
            let current = self.imageOrientation
            let currentDegrees: Int = (
                current == UIImageOrientation.Down || current == UIImageOrientation.DownMirrored ? 180 : (
                    current == UIImageOrientation.Left || current == UIImageOrientation.LeftMirrored ? 270 : (
                        current == UIImageOrientation.Right || current == UIImageOrientation.RightMirrored ? 90 : 0
                    )
                )
            )
            let changeDegrees: Int = (
                orientation == UIImageOrientation.Down || orientation == UIImageOrientation.DownMirrored ? 180 : (
                    orientation == UIImageOrientation.Left || orientation == UIImageOrientation.LeftMirrored ? 270 : (
                        orientation == UIImageOrientation.Right || orientation == UIImageOrientation.RightMirrored ? 90 : 0
                    )
                )
            )
    
    
            let mirrored: Bool = (
                current == UIImageOrientation.DownMirrored || current == UIImageOrientation.UpMirrored ||
                current == UIImageOrientation.LeftMirrored || current == UIImageOrientation.RightMirrored ||
                orientation == UIImageOrientation.DownMirrored || orientation == UIImageOrientation.UpMirrored ||
                orientation == UIImageOrientation.LeftMirrored || orientation == UIImageOrientation.RightMirrored
            )
    
            let degrees: Int = currentDegrees + changeDegrees
    
            let newOrientation: UIImageOrientation = (
                degrees == 270 || degrees == 630 ? (mirrored ? UIImageOrientation.LeftMirrored : UIImageOrientation.Left) : (
                    degrees == 180 || degrees == 540 ? (mirrored ? UIImageOrientation.DownMirrored : UIImageOrientation.Down) : (
                        degrees == 90 || degrees == 450 ? (mirrored ? UIImageOrientation.RightMirrored : UIImageOrientation.Right) : (
                            mirrored ? UIImageOrientation.UpMirrored : UIImageOrientation.Up
                        )
                    )
                )
            )
    
            return UIImage(CGImage: self.CGImage!, scale: 1.0, orientation: newOrientation)
        }
    }
    

    这将按输入的量旋转图像的EXIF orientation 。例如,如果原始方向是 Right ,并且它被旋转 正确的 ,新的方向将是 Down 。如果原件是 RightMirrored ,并且它被旋转 Left ,新的方向将是 UpMirrored .

    然后,在上传照片之前,通过调用问题中的代码作为扩展来旋转实际像素 UIImage , image.rotate(image.imageOrientation) .