裁剪 CIImage

5

我有一个类,它接受一个UIImage,用以下方式初始化CIImage

workingImage = CIImage.init(image: baseImage!)

然后使用该图像从中剪切出9个相邻的正方形,形成一个3x3的模式 - 在循环中执行:

for x in 0..<3
    {
        for y in 0..<3
        {

            croppingRect = CGRect(x: CGFloat(Double(x) * sideLength + startPointX),
                                  y: CGFloat(Double(y) * sideLength + startPointY),
                                  width: CGFloat(sideLength),
                                  height: CGFloat(sideLength))
            let tmpImg = (workingImage?.cropping(to: croppingRect))!
        }
    }

这些 tmpImgs 被插入到一个表中并稍后使用,但这不是重点。

这段代码可以在 iOS 9 上运行,在 iOS 10 模拟器上也可以运行,但无法在实际的 iOS 10 设备上运行。产生的图像要么全为空的,要么其中一个只有一半是正确的,其余部分仍然是空的。

这不是在 iOS 10 中应该如何处理的吗?


显然,一切都取决于sideLengthstartPointXstartPointY的正确和有意义的值。但是你没有给出任何线索,说明你是如何得出这些关键数字的。(我还应该提到,“在模拟器上可以工作但在设备上不行”的现象通常是由于线程问题引起的。) - matt
1
抱歉 - 还有一件事。你为什么要通过CIImage呢?我有点担心这是因为你认为这是裁剪UIImage的方法 - 但它不是。如果你没有特别使用CIImage的需求,即如果你不打算应用CIFilter,请根本不要使用它! - matt
这段代码没有裁剪workingImage。裁剪后的图像被分配给tmpImg,但由于变量从未被使用,因此立即释放。因此,workingImage保持不变。这是真正的代码吗? - Codo
@matt,实际上,我认为这确实是裁剪图像的方法。我不知道其他的方法,因为我在互联网上找到的大多数与裁剪有关的东西要么根本不起作用,要么使用CIImage。如果您能指点我正确的方向,我将不胜感激。 - VID44R
@Codo,在循环中将tmpImg添加到图像数组中,我没有包含它,因为我认为在这种情况下它并不重要。正如我所说,这种方法在IOS9设备上是_正确的_,这让我更加困惑。 - VID44R
@VID44R 好的,我已经向您展示了如何正确(且可靠地)完成它。 - matt
2个回答

6
问题的关键在于通过CIImage不是裁剪UIImage的正确方式。首先,从CIImage返回UIImage是一件复杂的事情。其次,整个往返过程是不必要的。
如何裁剪:
要裁剪图像,请创建所需裁剪大小的图像图形上下文,并在UIImage上调用draw(at:)将其绘制到与图形上下文相关的所需点,以使图像的所需部分落入上下文中。现在提取生成的新图像并关闭上下文。
为了演示,我将裁剪到您尝试裁剪的三分之一之一,即右下方的三分之一。
let sz = baseImage.size
UIGraphicsBeginImageContextWithOptions(
    CGSize(width:sz.width/3.0, height:sz.height/3.0), 
    false, 0)
baseImage.draw(at:CGPoint(x: -sz.width/3.0*2.0, y: -sz.height/3.0*2.0))
let tmpImg = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()

原始图片 (baseImage):

输入图像描述

裁剪后的图片 (tmpImg):

输入图像描述

其他部分完全相同。


1

Core Image的坐标系统与UIKit不匹配,因此需要镜像矩形。

所以在您的特定情况下,您需要:

var ciRect = croppingRect
ciRect.origin.y = workingImage!.extent.height - ciRect.origin.y - ciRect.height
let tmpImg = workingImage!.cropped(to: ciRect)

这绝对适用于iOS 10及以上版本。
在更一般的情况下,我们将创建一个UIImage扩展,涵盖两种可能的坐标系,这比使用draw(at:)要快得多:
extension UIImage {
    /// Return a new image cropped to a rectangle.
    /// - parameter rect:
    /// The rectangle to crop.
    open func cropped(to rect: CGRect) -> UIImage {
        // a UIImage is either initialized using a CGImage, a CIImage, or nothing
        if let cgImage = self.cgImage {
            // CGImage.cropping(to:) is magnitudes faster than UIImage.draw(at:)
            if let cgCroppedImage = cgImage.cropping(to: rect) {
                return UIImage(cgImage: cgCroppedImage)
            } else {
                return UIImage()
            }
        }
        if let ciImage = self.ciImage {
            // Core Image's coordinate system mismatch with UIKit, so rect needs to be mirrored.
            var ciRect = rect
            ciRect.origin.y = ciImage.extent.height - ciRect.origin.y - ciRect.height
            let ciCroppedImage = ciImage.cropped(to: ciRect)
            return UIImage(ciImage: ciCroppedImage)
        }
        return self
    }
}

我已经为此制作了一个Pod,因此源代码在https://github.com/Coeur/ImageEffects/blob/master/SwiftImageEffects/ImageEffects%2Bextensions.swift


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接