垂直拼接/合成多个图像并保存为一个图像(iOS,objective-c)

Cam*_*n E 11 xcode image objective-c ios

我需要帮助编写一个Objective-c函数,它将接收一组UIImages/PNG,然后返回/保存所有拼接在一起的图像的高图像.我是新手,所以请慢点放松一下:)

到目前为止我的想法:绘制一个UIView,然后将子视图添加到每个人的父(图像),然后???

cle*_*ght 21

下面是Swift 3和Swift 2示例,它们将图像垂直或水平拼接在一起.它们使用调用者提供的数组中最大图像的尺寸来确定每个单独图像被拼接到的每个单独帧的常用尺寸.

注意:Swift 3示例保留每个图像的纵横比,而Swift 2示例则不保留.请参阅下面的内容注释.

更新:添加了Swift 3示例

斯威夫特3:

import UIKit
import AVFoundation

func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
    var stitchedImages : UIImage!
    if images.count > 0 {
        var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
        for image in images {
            if image.size.width > maxWidth {
                maxWidth = image.size.width
            }
            if image.size.height > maxHeight {
                maxHeight = image.size.height
            }
        }
        var totalSize : CGSize
        let maxSize = CGSize(width: maxWidth, height: maxHeight)
        if isVertical {
            totalSize = CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(images.count))
        } else {
            totalSize = CGSize(width: maxSize.width  * (CGFloat)(images.count), height:  maxSize.height)
        }
        UIGraphicsBeginImageContext(totalSize)
        for image in images {
            let offset = (CGFloat)(images.index(of: image)!)
            let rect =  AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
                            CGRect(x: 0, y: maxSize.height * offset, width: maxSize.width, height: maxSize.height) :
                            CGRect(x: maxSize.width * offset, y: 0, width: maxSize.width, height: maxSize.height))
            image.draw(in: rect)
        }
        stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
    return stitchedImages
}
Run Code Online (Sandbox Code Playgroud)

注意:下面的原始Swift 2示例保留纵横比(例如,在Swift 2示例中,所有图像都展开以适合表示图像宽度和高度极值的边界框,因此任何非正方形图像可以在其中一个维度上不成比例地拉伸).如果您正在使用Swift 2并希望保留纵横比,请使用AVMakeRect() Swift 3示例中的修改.由于我不再能够访问Swift 2游乐场并且无法测试它以确保没有错误,因此我没有更新Swift 2示例.

Swift 2 :(不保留纵横比.修复了上面的Swift 3示例)

import UIKit
import AVFoundation

func stitchImages(images: [UIImage], isVertical: Bool) -> UIImage {
    var stitchedImages : UIImage!
    if images.count > 0 {
        var maxWidth = CGFloat(0), maxHeight = CGFloat(0)
        for image in images {
            if image.size.width > maxWidth {
                maxWidth = image.size.width
            }
            if image.size.height > maxHeight {
                maxHeight = image.size.height
            }
        }
        var totalSize : CGSize, maxSize = CGSizeMake(maxWidth, maxHeight)
        if isVertical {
            totalSize = CGSizeMake(maxSize.width, maxSize.height * (CGFloat)(images.count))
        } else {
            totalSize = CGSizeMake(maxSize.width  * (CGFloat)(images.count), maxSize.height)
        }
        UIGraphicsBeginImageContext(totalSize)
        for image in images {
            var rect : CGRect, offset = (CGFloat)((images as NSArray).indexOfObject(image))
            if isVertical {
                rect = CGRectMake(0, maxSize.height * offset, maxSize.width, maxSize.height)
            } else {
                rect = CGRectMake(maxSize.width * offset, 0 , maxSize.width, maxSize.height)
            }
            image.drawInRect(rect)
        }
        stitchedImages = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
    }
    return stitchedImages
}
Run Code Online (Sandbox Code Playgroud)

  • 只是想知道你是否可以为Swift 3更新这个? (2认同)

Gor*_*ove 9

通常的方法是创建位图图像上下文,在所需位置绘制图像,然后从图像上下文中获取图像.

您可以使用UIKit执行此操作,这有点简单,但不是线程安全的,因此需要在主线程中运行并阻止UI.

这里有很多示例代码,但是如果你想正确理解它,你应该看看UIGraphicsContextBeginImageContext,UIGraphicsGetCurrentContext,UIGraphicsGetImageFromCurrentImageContext和UIImageDrawInRect.不要忘记UIGraphicsPopCurrentContext.

你也可以使用Core Graphics,这是AFAIK,可以安全地在后台线程上使用(我还没有崩溃).效率大致相同,因为UIKit只是在引擎盖下使用CG.关键词是CGBitmapContextCreate,CGContextDrawImage,CGBitmapContextCreateImage,CGContextTranslateCTM,CGContextScaleCTM和CGContextRelease(核心图形没有ARC).缩放和翻译是因为CG的右下角有原点而Y向上增加.

还有第三种方法,即使用CG作为上下文,但通过使用CALayer保存自己所有的坐标疼痛,将CGImage(UIImage.CGImage)设置为内容,然后将图层渲染到上下文.这仍然是线程安全的,让层可以处理所有的转换.关键字是 - renderInContext:


Wha*_*hat 6

我知道我在这里有点晚了但希望这可以帮助别人.如果您尝试从阵列中创建一个大图像,则可以使用此方法

- (UIImage *)mergeImagesFromArray: (NSArray *)imageArray {

    if ([imageArray count] == 0) return nil;

    UIImage *exampleImage = [imageArray firstObject];
    CGSize imageSize = exampleImage.size;
    CGSize finalSize = CGSizeMake(imageSize.width, imageSize.height * [imageArray count]);

    UIGraphicsBeginImageContext(finalSize);

    for (UIImage *image in imageArray) {
        [image drawInRect: CGRectMake(0, imageSize.height * [imageArray indexOfObject: image],
                                      imageSize.width, imageSize.height)];
    }

    UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return finalImage;
}
Run Code Online (Sandbox Code Playgroud)


小智 5

斯威夫特 4

extension Array where Element: UIImage {
    func stitchImages(isVertical: Bool) -> UIImage {

        let maxWidth = self.compactMap { $0.size.width }.max()
        let maxHeight = self.compactMap { $0.size.height }.max()

        let maxSize = CGSize(width: maxWidth ?? 0, height: maxHeight ?? 0)
        let totalSize = isVertical ?
            CGSize(width: maxSize.width, height: maxSize.height * (CGFloat)(self.count))
            : CGSize(width: maxSize.width  * (CGFloat)(self.count), height:  maxSize.height)
        let renderer = UIGraphicsImageRenderer(size: totalSize)

        return renderer.image { (context) in
            for (index, image) in self.enumerated() {
                let rect = AVMakeRect(aspectRatio: image.size, insideRect: isVertical ?
                    CGRect(x: 0, y: maxSize.height * CGFloat(index), width: maxSize.width, height: maxSize.height) :
                    CGRect(x: maxSize.width * CGFloat(index), y: 0, width: maxSize.width, height: maxSize.height))
                image.draw(in: rect)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)