Divide image in array of images with swift

前端 未结 3 1323
迷失自我
迷失自我 2021-01-03 02:33

I\'m trying to divide an image to create 16 out of it (in a matrix). I\'m using swift 2.1. Here\'s the code:

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


        
相关标签:
3条回答
  • 2021-01-03 03:09

    The fundamental issue is the difference between how UIImage and CGImage interpret their size. UIImage uses "points" and CGImage uses pixels. And the conversion factor is the scale.

    For example, if a UIImage has a scale of 3, every "point" in any given direction the UIImage, there are three pixels in that direction in the underlying CGImage. So for a UIImage that has a scale of 3 and a size of 100x100 points, the underlying CGImage has a size of 300x300 pixels.

    To return a simple array of images sliced by n x n (e.g. if n is three, there will be nine images in the array), you can do something like the following in Swift 3:

    /// 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
    }
    

    Or, in 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
    }
    

    This makes sure that the resulting images are in the correct scale (this is why the above strides through the image in "points" and the multiplies to get the correct pixels in the CGImage). This also, if the dimensions, measured in "points") are not evenly divisible by n, it will making up the difference in the last image for that row or column, respectively. E.g. when you make three tiles for an image with a height of 736 points, the first two will be 245 points, but the last one will be 246 points).

    There is one exception that this does not (entirely) handle gracefully. Namely, if the UIImage has an imageOrientation of something other than .up, the order in which the images is retrieved corresponds to that orientation, not the upper left corner of the image as you view it.

    0 讨论(0)
  • 2021-01-03 03:17

    I've used this to slice an image into a matrix. The matrix is represented as a 1D array.

    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
        }
    
    0 讨论(0)
  • 2021-01-03 03:21

    you can split your image im two parts vertically and horizontally and sub split the result as needed:

    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({$0.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
        }
    }
    

    Splitting the image by columns and rows:

    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
    
    0 讨论(0)
提交回复
热议问题