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

问题描述 投票:0回答:7

我需要帮助编写一个 Objective-C 函数,该函数将接受 UIImages/PNG 数组,并返回/保存按垂直顺序缝合在一起的所有图像的一个高图像。我是新手,所以请慢慢来,放轻松:)

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

objective-c ios xcode image
7个回答
22
投票

下面是 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
}

注意:下面的原始 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 }
    

9
投票
通常的方法是创建一个位图图像上下文,将图像绘制到所需的位置,然后从图像上下文中获取图像。

您可以使用 UIKit 来完成此操作,这更容易一些,但不是线程安全的,因此需要在主线程中运行并会阻塞 UI。

为此有大量示例代码,但如果您想正确理解它,您应该查看 UIGraphicsContextBeginImageContext、UIGraphicsGetCurrentContext、UIGraphicsGetImageFromCurrentImageContext 和 UIImageDrawInRect。不要忘记 UIGraphicsPopCurrentContext。

您也可以使用 Core Graphics 来完成此操作,据我所知,可以安全地在后台线程上使用(我还没有遇到过崩溃)。效率大致相同,因为 UIKit 只是在底层使用 CG。 其关键字是 CGBitmapContextCreate、CGContextDrawImage、CGBitmapContextCreateImage、CGContextTranslateCTM、CGContextScaleCTM 和 CGContextRelease(核心图形没有 ARC)。缩放和平移是因为 CG 原点在右下角,Y 向上增加。

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


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

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

2
投票
尝试这段代码,我尝试将两个图像拼接在一起并在 ImageView 中显示它们

UIImage *bottomImage = [UIImage imageNamed:@"bottom.png"]; //first image UIImage *image = [UIImage imageNamed:@"top.png"]; //foreground image CGSize newSize = CGSizeMake(209, 260); //size of image view UIGraphicsBeginImageContext( newSize ); // drawing 1st image [bottomImage drawInRect:CGRectMake(0,0,newSize.width/2,newSize.height/2)]; // drawing the 2nd image after the 1st [image drawInRect:CGRectMake(0,newSize.height/2,newSize.width/2,newSize.height/2)] ; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); join.image = newImage;

join 是图像视图的名称,您可以将图像视为单个图像。


2
投票
上面的解决方案很有帮助,但对我来说有一个严重的缺陷。问题是,如果图像尺寸不同,则生成的拼接图像的各部分之间可能存在较大的空间。我想出的解决方案将所有图像组合在一起,这样无论单个图像大小如何,它看起来都更像单个图像。

对于 Swift 3.x

static func combineImages(images:[UIImage]) -> UIImage { var maxHeight:CGFloat = 0.0 var maxWidth:CGFloat = 0.0 for image in images { maxHeight += image.size.height if image.size.width > maxWidth { maxWidth = image.size.width } } let finalSize = CGSize(width: maxWidth, height: maxHeight) UIGraphicsBeginImageContext(finalSize) var runningHeight: CGFloat = 0.0 for image in images { image.draw(in: CGRect(x: 0.0, y: runningHeight, width: image.size.width, height: image.size.height)) runningHeight += image.size.height } let finalImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return finalImage! }
    

0
投票

适用于 Swift 5+

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).index(of: 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.draw(in: rect) } stitchedImages = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() } return stitchedImages }
使用

var selectedImages: [UIImage] = [] let images = stitchImages(images: selectedImages, isVertical: true) imageView.image = images
    
© www.soinside.com 2019 - 2024. All rights reserved.