用 swift 分割图像数组中的图像

Divide image in array of images with swift

我正在尝试划分图像以从中创建 16 个图像(在矩阵中)。我正在使用 swift 2.1。这是代码:

let cellSize = Int(originalImage.size.height) / 4

for i in 0...4 {
        for p in 0...4 {
            let tmpImgRef: CGImage = originalImage.CGImage!

            let rect: CGImage = CGImageCreateWithImageInRect(tmpImgRef, CGRectMake(CGFloat(i * cellSize), CGFloat(p * cellSize), CGFloat(cellSize), CGFloat(cellSize)))!

            gameCells.append(cell)

        }
    }

这可行,但 returns 的图像只是原始图像的一部分。我一直在搜索,我知道那是因为当我创建 CGImage 时它的大小与 UIImage 不同,但我不知道如何修复它。如果我可以使用 CGImage 的高度而不是 UIImage 来创建变量 cellSize,我想我会修复它,但我无法获得 CGImage 的高度。

感谢您的帮助!

我用它把图像切成矩阵。矩阵表示为一维数组。

func snapshotImage(image: UIImage, rect: CGRect) -> UIImage {
        var imageRect: CGRect! = rect

        if image.scale > 1.0 {
            imageRect = CGRect(origin: CGPoint(x: rect.origin.x * image.scale, y: rect.origin.y * image.scale), size: CGSize(width: rect.size.width * image.scale, height: rect.size.height * image.scale))
        }


        let imageRef: CGImage = image.cgImage!.cropping(to: imageRect)!
        let result: UIImage = UIImage(cgImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
        return result
    }

    func sliceImage(image: UIImage, size: CGSize) -> [UIImage] {
        var slices: [UIImage] = [UIImage]()
        var rect = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)


        var y: Float = 0.0
        let width: Int = Int(image.size.width / size.width)
        let height: Int = Int(image.size.height / size.height)

        for _ in 0...height {
            var x: Float = 0.0

            for _ in 0...width {
                rect.origin.x = CGFloat(x);
                slices.append(self.snapshotImage(image: image, rect: rect))

                x += Float(size.width)
            }

            y += Float(size.height)
            rect.origin.y = CGFloat(y)
        }

        return slices
    }

您可以将图像纵向和横向分成两部分,然后根据需要细分结果:

extension UIImage {
    var topHalf: UIImage? {
        guard let cgImage = cgImage, let image = cgImage.cropping(to: CGRect(origin: .zero, size: CGSize(width: size.width, height: size.height/2))) else { return nil }
        return UIImage(cgImage: image, scale: scale, orientation: imageOrientation)
    }
    var bottomHalf: UIImage? {
        guard let cgImage = cgImage, let image = cgImage.cropping(to: CGRect(origin: CGPoint(x: 0,  y: CGFloat(Int(size.height)-Int(size.height/2))), size: CGSize(width: size.width, height: CGFloat(Int(size.height) - Int(size.height/2))))) else { return nil }
        return UIImage(cgImage: image, scale: scale, orientation: imageOrientation)
    }
    var leftHalf: UIImage? {
        guard let cgImage = cgImage, let image = cgImage.cropping(to: CGRect(origin: .zero, size: CGSize(width: size.width/2, height: size.height))) else { return nil }
        return UIImage(cgImage: image, scale: scale, orientation: imageOrientation)
    }
    var rightHalf: UIImage? {
        guard let cgImage = cgImage, let image = cgImage.cropping(to: CGRect(origin: CGPoint(x: CGFloat(Int(size.width)-Int((size.width/2))), y: 0), size: CGSize(width: CGFloat(Int(size.width)-Int((size.width/2))), height: size.height)))
            else { return nil }
        return UIImage(cgImage: image, scale: scale, orientation: imageOrientation)
    }
    var splitedInFourParts: [UIImage] {
        guard case let topHalf = topHalf,
              case let bottomHalf = bottomHalf,
            let topLeft = topHalf?.leftHalf,
            let topRight = topHalf?.rightHalf,
            let bottomLeft = bottomHalf?.leftHalf,
            let bottomRight = bottomHalf?.rightHalf else{ return [] }
        return [topLeft, topRight, bottomLeft, bottomRight]
    }
    var splitedInSixteenParts: [UIImage] {
        var array = splitedInFourParts.flatMap({[=10=].splitedInFourParts})
        // if you need it in reading order you need to swap some image positions
        swap(&array[2], &array[4])
        swap(&array[3], &array[5])
        swap(&array[10], &array[12])
        swap(&array[11], &array[13])
        return array
    }
}

按列和行拆分图像:

extension UIImage {
    func matrix(_ rows: Int, _ columns: Int) -> [UIImage] {
        let y = (size.height / CGFloat(rows)).rounded()
        let x = (size.width / CGFloat(columns)).rounded()
        var images: [UIImage] = []
        images.reserveCapacity(rows * columns)
        guard let cgImage = cgImage else { return [] }
        (0..<rows).forEach { row in
            (0..<columns).forEach { column in
                var width = Int(x)
                var height = Int(y)
                if row == rows-1 && size.height.truncatingRemainder(dividingBy: CGFloat(rows)) != 0 {
                    height = Int(size.height - size.height / CGFloat(rows) * (CGFloat(rows)-1))
                }
                if column == columns-1 && size.width.truncatingRemainder(dividingBy: CGFloat(columns)) != 0 {
                    width = Int(size.width - (size.width / CGFloat(columns) * (CGFloat(columns)-1)))
                }
                if let image = cgImage.cropping(to: CGRect(origin: CGPoint(x: column * Int(x), y:  row * Int(x)), size: CGSize(width: width, height: height))) {
                    images.append(UIImage(cgImage: image, scale: scale, orientation: imageOrientation))
                }
            }
        }
        return images
    }
}

let myPicture = UIImage(data: try! Data(contentsOf: URL(string:"https://i.stack.imgur.com/Xs4RX.jpg")!))!

let images = myPicture.matrix(4, 4)

images.count   // 16

根本问题是 UIImageCGImage 如何解释他们的 size 之间的差异。 UIImage 使用 "points" 而 CGImage 使用像素。而换算系数就是scale

例如,如果 UIImagescale 为 3,则 UIImage 在任何给定方向上的每个 "point",在该方向上有三个像素底层 CGImage。因此,对于 scale 为 3 且 size 为 100x100 点的 UIImage,基础 CGImage 的大小为 300x300 像素。

到 return 由 n x n 切片的简单图像数组(例如,如果 n 是三,数组中会有九张图片),你可以在Swift3中做类似下面的事情:

/// Slice image into array of tiles
///
/// - Parameters:
///   - image: The original image.
///   - howMany: How many rows/columns to slice the image up into.
///
/// - Returns: An array of images.
///
/// - Note: The order of the images that are returned will correspond
///         to the `imageOrientation` of the image. If the image's
///         `imageOrientation` is not `.up`, take care interpreting 
///         the order in which the tiled images are returned.

func slice(image: UIImage, into howMany: Int) -> [UIImage] {
    let width: CGFloat
    let height: CGFloat

    switch image.imageOrientation {
    case .left, .leftMirrored, .right, .rightMirrored:
        width = image.size.height
        height = image.size.width
    default:
        width = image.size.width
        height = image.size.height
    }

    let tileWidth = Int(width / CGFloat(howMany))
    let tileHeight = Int(height / CGFloat(howMany))

    let scale = Int(image.scale)
    var images = [UIImage]()

    let cgImage = image.cgImage!

    var adjustedHeight = tileHeight

    var y = 0
    for row in 0 ..< howMany {
        if row == (howMany - 1) {
            adjustedHeight = Int(height) - y
        }
        var adjustedWidth = tileWidth
        var x = 0
        for column in 0 ..< howMany {
            if column == (howMany - 1) {
                adjustedWidth = Int(width) - x
            }
            let origin = CGPoint(x: x * scale, y: y * scale)
            let size = CGSize(width: adjustedWidth * scale, height: adjustedHeight * scale)
            let tileCgImage = cgImage.cropping(to: CGRect(origin: origin, size: size))!
            images.append(UIImage(cgImage: tileCgImage, scale: image.scale, orientation: image.imageOrientation))
            x += tileWidth
        }
        y += tileHeight
    }
    return images
}

或者,在 Swift 2.3 中:

func slice(image image: UIImage, into howMany: Int) -> [UIImage] {
    let width: CGFloat
    let height: CGFloat

    switch image.imageOrientation {
    case .Left, .LeftMirrored, .Right, .RightMirrored:
        width = image.size.height
        height = image.size.width
    default:
        width = image.size.width
        height = image.size.height
    }

    let tileWidth = Int(width / CGFloat(howMany))
    let tileHeight = Int(height / CGFloat(howMany))

    let scale = Int(image.scale)
    var images = [UIImage]()
    let cgImage = image.CGImage!

    var adjustedHeight = tileHeight

    var y = 0
    for row in 0 ..< howMany {
        if row == (howMany - 1) {
            adjustedHeight = Int(height) - y
        }
        var adjustedWidth = tileWidth
        var x = 0
        for column in 0 ..< howMany {
            if column == (howMany - 1) {
                adjustedWidth = Int(width) - x
            }
            let origin = CGPoint(x: x * scale, y: y * scale)
            let size = CGSize(width: adjustedWidth * scale, height: adjustedHeight * scale)
            let tileCgImage = CGImageCreateWithImageInRect(cgImage, CGRect(origin: origin, size: size))!
            images.append(UIImage(CGImage: tileCgImage, scale: image.scale, orientation: image.imageOrientation))
            x += tileWidth
        }
        y += tileHeight
    }
    return images
}

这确保生成的图像在正确的 scale 中(这就是为什么上面跨过 "points" 中的图像并乘以得到 [=13 中的正确像素的原因=]).同样,如果在 "points") 中测量的尺寸不能被 n 整除,它将分别弥补最后一张图像中该行或该列的差异。例如。当你为一个高度为 736 点的图像制作三个图块时,前两个将是 245 点,但最后一个将是 246 点)。

有一个例外,这并没有(完全)妥善处理。也就是说,如果 UIImageimageOrientation 不是 .up,则检索图像的顺序对应于该方向,而不是您查看时图像的左上角它。