为什么过滤裁剪的图像比过滤尺寸调整的图像慢4倍(两者都具有相同的尺寸)

Pon*_*ono 5 core-graphics core-image swift

我一直在努力解决这个问题而没有运气.我有一个非常简单的Swift命令行应用程序,它接受一个参数 - 图像路径加载.它使用SepiaTone过滤器裁剪图像并过滤该图像片段.

它工作得很好.它将图像裁剪为200x200并使用SepiaTone进行过滤.现在这是我面临的问题 - 我的MacBook Air 整个过程需要600毫秒.现在,当我将输入图像调整(而不是裁剪)到相同尺寸(200x200)时,需要150ms.

这是为什么?在这两种情况下,我都会过滤尺寸为200x200的图像.我正在使用这个特定的图像进行测试(5966x3978).

更新:

在处理裁剪图像时,这段特殊代码需要4倍的时间:

var ciImage:CIImage = CIImage(cgImage: cgImage)
Run Code Online (Sandbox Code Playgroud)

更新结束

裁剪代码(200x200):

// parse args and get image path
let args:Array = CommandLine.arguments
let inputFile:String = args[CommandLine.argc - 1]
let inputURL:URL = URL(fileURLWithPath: inputFile)

// load the image from path into NSImage
// and convert NSImage into CGImage
guard
    let nsImage = NSImage(contentsOf: inputURL),
    var cgImage = nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil)
else {
    exit(EXIT_FAILURE)
}

// CROP THE IMAGE TO 200x200
// THIS IS THE ONLY BLOCK OF CODE THAT IS DIFFERENT
// IN THOSE TWO EXAMPLES
let rect = CGRect(x: 0, y: 0, width: 200, height: 200)

if let croppedImage = cgImage.cropping(to: rect) {
    cgImage = croppedImage
} else {
    exit(EXIT_FAILURE)
}

// END CROPPING



// convert CGImage to CIImage
var ciImage:CIImage = CIImage(cgImage: cgImage)

// initiate SepiaTone
guard
    let sepiaFilter = CIFilter(name: "CISepiaTone")
else {
    exit(EXIT_FAILURE)
}

sepiaFilter.setValue(ciImage, forKey: kCIInputImageKey)
sepiaFilter.setValue(0.5, forKey: kCIInputIntensityKey)

guard
    let result = sepiaFilter.outputImage
else {
    exit(EXIT_FAILURE)
}

let context:CIContext = CIContext()

// perform filtering in a GPU context
guard
    let output = context.createCGImage(sepiaFilter.outputImage!, from: ciImage.extent)
else {
    exit(EXIT_FAILURE)
}
Run Code Online (Sandbox Code Playgroud)

调整大小的代码(200x200):

// parse args and get image path
let args:Array = CommandLine.arguments
let inputFile:String = args[CommandLine.argc - 1]
let inputURL:URL = URL(fileURLWithPath: inputFile)

// load the image from path into NSImage
// and convert NSImage into CGImage
guard
    let nsImage = NSImage(contentsOf: inputURL),
    var cgImage = nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil)
else {
    exit(EXIT_FAILURE)
}

// RESIZE THE IMAGE TO 200x200
// THIS IS THE ONLY BLOCK OF CODE THAT IS DIFFERENT
// IN THOSE TWO EXAMPLES
guard let CGcontext = CGContext(data: nil,
                              width: 200,
                              height: 200,
                              bitsPerComponent: cgImage.bitsPerComponent,
                              bytesPerRow: cgImage.bytesPerRow,
                              space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
                              bitmapInfo: cgImage.bitmapInfo.rawValue)
else {
    exit(EXIT_FAILURE)
}

CGcontext.draw(cgImage, in: CGRect(x: 0, y: 0, width: 200, height: 200))

if let resizeOutput = CGcontext.makeImage() {
    cgImage = resizeOutput
}

// END RESIZING



// convert CGImage to CIImage
var ciImage:CIImage = CIImage(cgImage: cgImage)

// initiate SepiaTone
guard
    let sepiaFilter = CIFilter(name: "CISepiaTone")
else {
    exit(EXIT_FAILURE)
}

sepiaFilter.setValue(ciImage, forKey: kCIInputImageKey)
sepiaFilter.setValue(0.5, forKey: kCIInputIntensityKey)

guard
    let result = sepiaFilter.outputImage
else {
    exit(EXIT_FAILURE)
}

let context:CIContext = CIContext()

// perform filtering in a GPU context
guard
    let output = context.createCGImage(sepiaFilter.outputImage!, from: ciImage.extent)
else {
    exit(EXIT_FAILURE)
}
Run Code Online (Sandbox Code Playgroud)

小智 1

看起来你正在做两件截然不同的事情。在“慢”版本中,您正在裁剪(如获取原始图像的一个小 CGRect),而在“快”版本中,您正在调整大小(如将原始图像缩小为 CGRect)。

您可以通过添加两个 UIImageView 并在每个 ciImage 声明后添加以下行来证明这一点:

    slowImage.image = UIImage(ciImage: ciImage)
    fastImage.image = UIImage(ciImage: ciImage)
Run Code Online (Sandbox Code Playgroud)

这是两个模拟器屏幕截图,“慢”图像位于“快”图像上方。第一个是您的代码,其中“慢”CGRect 原点是 (0,0),第二个是将其调整为 (2000,2000):

原点是 (0,0) 原点是 (0,0) 原点是 (2000,2000) 原点是 (2000,2000)

知道了这一点,我就能想出一些发生在时间上的事情。

我提供了有关裁剪功能的 Apple 文档的链接。它解释了它在幕后进行一些 CGRect 计算,但没有解释它如何从全尺寸 CG 图像中提取像素位 - 我认为这才是真正变慢的地方。

但最终,看起来时机是由于做了两件完全不同的事情。

CGRect.裁剪(到:)