Stitch/Composite несколько изображений по вертикали и сохранить как одно изображение (iOS, objective-c)
Мне нужна помощь, записывающая функцию objective-c, которая возьмет в массив UIImages/PNG, и вернет/сохранит одно высокое изображение всех изображений, сшитых вместе по вертикали. Я новичок в этом, поэтому, пожалуйста, пройдите медленнее и успокойтесь:)
Мои идеи до сих пор:
Нарисуйте UIView, затем addSubviews для каждого родителя (из изображений)
и затем
Ответы
Ответ 1
Обычным способом является создание контекста растрового изображения, рисование изображений в нужном месте, а затем получение изображения из контекста изображения.
Вы можете сделать это с помощью UIKit, который несколько проще, но не является потокобезопасным, поэтому его нужно будет запустить в основном потоке и заблокировать пользовательский интерфейс.
Для этого есть множество примеров кода, но если вы хотите правильно его понять, вы должны посмотреть на 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:
Ответ 2
Ниже приведены примеры Swift 3 и Swift 2, которые строчат изображения по вертикали или по горизонтали. Они используют размеры самого большого изображения в массиве, предоставленные вызывающим, для определения общего размера, используемого для каждого отдельного кадра, в которое сшивается каждое отдельное изображение.
Примечание. Пример Swift 3 сохраняет все пропорции изображения, а пример Swift 2 - нет. См. Примечание ниже в отношении этого.
UPDATE: добавлен пример Swift 3
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
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
}
Ответ 3
Я знаю, что я немного опоздал, но, надеюсь, это может помочь кому-то. Если вы пытаетесь создать одно большое изображение из массива, вы можете использовать этот метод
- (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;
}
Ответ 4
Попробуйте этот кусок кода, я попробовал сшить два изображения вместе, n отобразил их в 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 - это имя изображения, и вы можете видеть изображения как одно изображение.
Ответ 5
Вышеприведенные решения были полезны, но имели серьезный недостаток для меня. Проблема в том, что если изображения имеют разные размеры, получившееся сшитое изображение будет иметь потенциально большие промежутки между частями. Решение, которое я придумал, объединяет все изображения прямо друг под другом, так что это похоже на одно изображение независимо от размеров отдельных изображений.
Для 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!
}
Ответ 6
Swift 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)
}
}
}
}