簡體   English   中英

縮放圖像:加速如何才能成為最慢的方法?

[英]Scaling Images: how can the accelerate be the slowest method?

我正在測試幾種重新縮放UIImage的方法。

我測試了這里發布的所有這些方法並測量了調整圖像大小所需的時間。

1)UIGraphicsBeginImageContextWithOptions&UIImage -drawInRect:

let image = UIImage(contentsOfFile: self.URL.path!)

let size = CGSizeApplyAffineTransform(image.size, CGAffineTransformMakeScale(0.5, 0.5))
let hasAlpha = false
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen

UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
image.drawInRect(CGRect(origin: CGPointZero, size: size))

let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

2)CGBitmapContextCreate和CGContextDrawImage

let cgImage = UIImage(contentsOfFile: self.URL.path!).CGImage

let width = CGImageGetWidth(cgImage) / 2
let height = CGImageGetHeight(cgImage) / 2
let bitsPerComponent = CGImageGetBitsPerComponent(cgImage)
let bytesPerRow = CGImageGetBytesPerRow(cgImage)
let colorSpace = CGImageGetColorSpace(cgImage)
let bitmapInfo = CGImageGetBitmapInfo(cgImage)

let context = CGBitmapContextCreate(nil, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo.rawValue)

CGContextSetInterpolationQuality(context, kCGInterpolationHigh)

CGContextDrawImage(context, CGRect(origin: CGPointZero, size: CGSize(width: CGFloat(width), height: CGFloat(height))), cgImage)

let scaledImage = CGBitmapContextCreateImage(context).flatMap { UIImage(CGImage: $0) }

3)CGImageSourceCreateThumbnailAtIndex

import ImageIO

if let imageSource = CGImageSourceCreateWithURL(self.URL, nil) {
    let options: [NSString: NSObject] = [
        kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
        kCGImageSourceCreateThumbnailFromImageAlways: true
    ]

    let scaledImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options).flatMap { UIImage(CGImage: $0) }
}

4)Lanczos重新采樣核心圖像

let image = CIImage(contentsOfURL: self.URL)

let filter = CIFilter(name: "CILanczosScaleTransform")!
filter.setValue(image, forKey: "inputImage")
filter.setValue(0.5, forKey: "inputScale")
filter.setValue(1.0, forKey: "inputAspectRatio")
let outputImage = filter.valueForKey("outputImage") as! CIImage

let context = CIContext(options: [kCIContextUseSoftwareRenderer: false])
let scaledImage = UIImage(CGImage: self.context.createCGImage(outputImage, fromRect: outputImage.extent()))

5)加速中的vImage

let cgImage = UIImage(contentsOfFile: self.URL.path!).CGImage

// create a source buffer
var format = vImage_CGImageFormat(bitsPerComponent: 8, bitsPerPixel: 32, colorSpace: nil, 
    bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.First.rawValue), 
    version: 0, decode: nil, renderingIntent: CGColorRenderingIntent.RenderingIntentDefault)
var sourceBuffer = vImage_Buffer()
defer {
    sourceBuffer.data.dealloc(Int(sourceBuffer.height) * Int(sourceBuffer.height) * 4)
}

var error = vImageBuffer_InitWithCGImage(&sourceBuffer, &format, nil, cgImage, numericCast(kvImageNoFlags))
guard error == kvImageNoError else { return nil }

// create a destination buffer
let scale = UIScreen.mainScreen().scale
let destWidth = Int(image.size.width * 0.5 * scale)
let destHeight = Int(image.size.height * 0.5 * scale)
let bytesPerPixel = CGImageGetBitsPerPixel(image.CGImage) / 8
let destBytesPerRow = destWidth * bytesPerPixel
let destData = UnsafeMutablePointer<UInt8>.alloc(destHeight * destBytesPerRow)
defer {
    destData.dealloc(destHeight * destBytesPerRow)
}
var destBuffer = vImage_Buffer(data: destData, height: vImagePixelCount(destHeight), width: vImagePixelCount(destWidth), rowBytes: destBytesPerRow)

// scale the image
error = vImageScale_ARGB8888(&sourceBuffer, &destBuffer, nil, numericCast(kvImageHighQualityResampling))
guard error == kvImageNoError else { return nil }

// create a CGImage from vImage_Buffer
let destCGImage = vImageCreateCGImageFromBuffer(&destBuffer, &format, nil, nil, numericCast(kvImageNoFlags), &error)?.takeRetainedValue()
guard error == kvImageNoError else { return nil }

// create a UIImage
let scaledImage = destCGImage.flatMap { UIImage(CGImage: $0, scale: 0.0, orientation: image.imageOrientation) }

經過幾個小時的測試並測量每個方法將圖像重新縮放到100x100所花費的時間,我的結論與NSHipster完全不同。 首先, vImage in acceleratevImage in accelerate比第一種方法慢200倍,在我看來是其他的表兄弟。 核心圖像方法也很慢。 但我很感興趣的是方法#1可以粉碎方法3,4和5,其中一些在理論上處理GPU上的東西。

例如,方法#3花了2秒鍾將1024x1024圖像的大小調整為100x100。 另一方面#1耗時0.01秒!

我錯過了什么嗎?

有些事情肯定是錯的,或者Apple不會花時間編寫加速和CIImage的東西。

注意 :我正在測量從圖像已加載到變量上的時間到將縮放版本保存到另一個變量的時間。 我沒有考慮從文件中讀取所需的時間。

由於各種原因,加速可能是最慢的方法:

  1. 您顯示的代碼可能會花費大量時間從CGImage中提取數據並制作新圖像。 例如,您沒有使用任何允許CGImage直接使用您的vImage結果而不是復制的功能。 可能還需要進行顏色空間轉換,作為其中一些提取/創建CGImage操作的一部分。 很難從這里說出來。
  2. 其他一些方法可能沒有做任何事情,將工作推遲到以后絕對被迫做的時候。 如果那是在你的結束時間之后,則沒有測量工作。
  3. 一些其他方法具有能夠直接使用圖像內容而無需首先進行復制的優點。
  4. 不同的重采樣方法(例如Bilinear vs. Lanczos)具有不同的成本
  5. GPU在某些方面實際上可以更快,重采樣是它專門優化的任務之一。 另一方面,隨機數據訪問(例如在重新采樣中發生)對矢量單元來說不是一件好事。
  6. 時序方法會影響結果。 加速是多線程的。 如果您使用掛鍾時間,您將得到一個答案。 如果你使用getrusage或采樣器,你會得到另一個。

如果你真的認為Accelerate在這里不合適,請提交一個bug。 我當然會在使用儀器時間檔案時檢查你在基准測試循環中花費了大部分時間在vImageScale上,然而這樣做。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM