Обрезка UIImage

209

У меня есть некоторый код, который изменяет размеры изображения, чтобы я мог получить масштабированный фрагмент центра изображения - я использую это, чтобы взять UIImageи вернуть маленькое квадратное представление изображения, аналогично тому, что видно в представлении альбома приложение Фотографии. (Я знаю, что мог бы использовать UIImageViewи настроить режим кадрирования для достижения тех же результатов, но эти изображения иногда отображаются в UIWebViews).

Я начал замечать некоторые сбои в этом коде, и я немного озадачен. У меня есть две разные теории, и мне интересно, есть ли одна из них на основе.

Теория 1) Я достигаю обрезки, рисуя в контексте закадрового изображения моего целевого размера. Поскольку мне нужна центральная часть изображения, я устанавливаю CGRectаргумент, передаваемый drawInRectдля чего-то большего, чем границы моего контекста изображения. Я надеялся, что это было кошерно, но вместо этого я пытаюсь нарисовать другое воспоминание, которого я не должен касаться?

Теория 2) Я делаю все это в фоновом потоке. Я знаю, что есть части UIKit, которые ограничены основным потоком. Я предполагал / надеялся, что рисование за пределами экрана не было одним из них. Я ошибся?

(О, как я скучаю по NSImage's drawInRect:fromRect:operation:fraction:методу.)

Jablair
источник
Если вы пытаетесь диагностировать сбой, вы должны запустить приложение под отладчиком и записывать, что происходит при сбое. Вы даже не определили, возникает ли исключение или вы получаете EXC_BAD_ACCESS из-за висящего указателя. Как только вы это знаете, вы можете начать делать теории.
Бензадо
Справедливо. Я не сделал rero'd это под отладчиком, хотя у меня есть сообщения EXC_BAD_ACCESS в журнале сбоев. Когда я писал это, я работал, предполагая, что допустил глупую ошибку в своей реализации, и кто-то запрыгнул бы на нее (например, забыл путь отсечения).
Jablair
Что бы ни стоило и хорошие ответы ниже, несмотря на это, есть хороший обзор методов и их производительности на NSHipster: nshipster.com/image-resizing . Пурист во мне хотел использовать CIImage, но прагматик выбрал UIKit / контекст изображения.
Крис Коновер

Ответы:

236

Обновление 2014-05-28: я написал это, когда iOS 3 или около того была новинкой, я уверен, что к настоящему времени есть лучшие способы сделать это, возможно, встроенные. Как уже упоминали многие, этот метод не учитывает ротацию; Прочитайте некоторые дополнительные ответы и распространите любовь, чтобы ответы на этот вопрос были полезны для всех.

Оригинальный ответ:

Я собираюсь скопировать / вставить свой ответ на тот же вопрос в другом месте:

Для этого не существует простого метода класса, но есть функция, которую вы можете использовать для получения желаемых результатов: CGImageCreateWithImageInRect(CGImageRef, CGRect)она поможет вам.

Вот краткий пример его использования:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);
HitScan
источник
13
Когда для параметра imageOrientationустановлено значение, отличное от «вверх», CGImageоно поворачивается относительно UIImageи прямоугольник обрезки будет неправильным. Вы можете вращать прямоугольник обрезки соответствующим образом, но у вновь созданного UIImageбудет imageOrientationвверх, так что обрезанный CGImageбудет вращаться внутри него.
2010 г.
25
Я хочу отметить, что с помощью этого метода на дисплее сетчатки необходимо удвоить ширину и высоту cropRect. Просто то, с чем я столкнулся.
Petrocket
21
чтобы заставить его работать в любой ориентации использования:[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];
Никос Каралис
3
Вы должны добавить поддержку Retina!
Марио Карвалью
1
@NicosKaralis, который только повернет его в правильную ориентацию. Однако область, которую вы обрезаете на изображении, все равно будет неправильной.
Тим Бодейт
90

Чтобы обрезать изображения сетчатки, сохраняя тот же масштаб и ориентацию, используйте следующий метод в категории UIImage (iOS 4.0 и выше):

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
Arne
источник
14
Вы могли бы просто убить условное здесь и всегда несколькими self.scale, нет?
Майкл Миор
2
Вы правы, Майкл, но я думаю, что вышеупомянутое решение немного быстрее на устройствах без сетчатки, так как оно выполняет только одну проверку вместо четырех умножений плюс назначение. На устройствах retina это всего лишь одна логическая проверка больше, чем необходимо, поэтому это вопрос личных предпочтений или соответствия цели.
CGee
2
Правильно ли использовать CGImageRelease(imageRef);с включенной ARC?
CGee
3
@ Даши Да. См. Stackoverflow.com/questions/7800174/…
Клаас
3
На самом деле 4 умножения могут быть быстрее, чем оценка одного условия :) Умножения можно довольно хорошо оптимизировать на современных процессорах, тогда как условия можно только прогнозировать. Но вы правы, это вопрос предпочтений. Я предпочитаю более короткий / простой код из-за повышенной читабельности.
Marsbear
65

Вы можете создать категорию UIImage и использовать ее там, где вам нужно. Основано на ответах HitScans и комментариях ниже.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

Вы можете использовать это так:

UIImage *imageToCrop = <yourImageToCrop>;
CGRect cropRect = <areaYouWantToCrop>;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];
Вилем Курц
источник
3
Не должно быть [[UIScreen mainScreen] scale]просто self.scale? Масштаб изображения может не совпадать с масштабом экрана.
Майкл Миор
Здравствуйте, я попробовал ваш ответ, и он дает мне, No visible @interface for 'UIImage' declares the selector 'crop'хотя я поместил файлы категорий .h и .m в свой проект и импортировал .h в класс, который я использую категории. Любая идея?
Али
Починил это. Я пропустил размещение заголовка метода в файле UIImage + Crop.h.
Али
Я хочу обрезать изображение в круглой форме. Так что мы можем видеть только круговой путь, а другой путь остается прозрачным.
Альфа
Чем вы можете использовать CGImageCreateWithMask или CGImageCreateWithMaskingColors вместо CGImageCreateWithImageInRect
Вилем Курц
54

Версия Swift 3

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{
    
    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

введите описание изображения здесь

с помощью этой функции моста CGRectMake-> CGRect(кредиты на этот ответ ответили @rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

Использование:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

Вывод:

введите описание изображения здесь

Максим Шустин
источник
Не уверен, почему за него проголосовали, я подумал, что это хороший ответ. Единственное, о чем я могу думать, это то, что он не обрабатывает масштабирование, поэтому он не будет работать, если у вас есть изображения @ 2x / @ 3x и имена ваших функций не совпадают.
Уильям Т.
4
Вам нужно до guardпервой строки, если UIImage.CGImageвозвращается nilи зачем использовать, varкогда letработает отлично.
NoodleOfDeath
Это правильно, ОДНАКО, cropping(to:)внимательно прочитайте документы . Результирующая CGImageсодержит сильную ссылку на большее, CGImageиз которого он был обрезан. Так что, если вы хотите только удерживать обрезанное изображение и не нуждаетесь в оригинале, взгляните на другие решения.
jsadler
Это не работает с изображениями, имеющими теги ориентации. Область кадрирования не вращается должным образом.
Макс
1
@ Макс это потому, что у cgImage нет поддержки ориентации. Если UIImage имеет, rightто cgImage должен быть повернут на 90 градусов по часовой стрелке, следовательно, прямоугольник кадрирования тоже должен вращаться
МК Юнг
49

Вот моя реализация обрезки UIImage, которая подчиняется свойству imageOrientation. Все ориентации были тщательно проверены.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}
Сергей Рудченко
источник
7
Предупреждение implicit declaration of function 'rad' is invalid in c99можно удалить, заменив: rad (90), rad (-90), rad (-180) -> M_PI_2, -M_PI_2, -_M_PI
Sound Blaster
Ой, извини. Добавлена ​​вспомогательная функция rad () в исходный фрагмент.
Сергей Рудченко
contains undefined reference for architecture armv7Я скучаю по библиотеке? CoreGraphics импортируется.
Боб Сприн
1
@SergiiRudchenko "Вся ориентация была тщательно проверена." - Это включает в себя зеркальные ориентации?
Тим Бодейт,
@BobSpryn Нет, вы не пропустите библиотеку. Хотя я не могу объяснить, что означает эта ошибка, замена rad (), как предложил SoundBlaster, также исправляет эту ошибку.
Тим Бодейт
39

Heads up: все эти ответы предполагают CGImageобъект изображения с обратной стороной.

image.CGImageможет вернуть ноль, если UIImageон поддерживается a CIImage, что было бы в случае, если вы создали это изображение с помощью CIFilter.

В этом случае вам, возможно, придется нарисовать изображение в новом контексте и вернуть это изображение ( медленно ).

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}
colinta
источник
5
Это именно то, что мне нужно, работает во всех сценариях, поэтому лучше, чем любое другое решение!
Дэвид Томпсон
2
image.size в первой строке должен быть rect.size UIGraphicsBeginImageContextWithOptions (rect.size, false, image.scale);
Бретт
2
Спасибо Бретт, определенно прямо здесь. Я обновил код, чтобы включить это исправление.
Колинта
Почему это -rect.origin.x не просто rect.origin.x?
Агрессор
2
Потому что мы обрезаем урожай; другими словами, аргумент 'rect' говорит: «Я хочу, чтобы левый верхний угол нового изображения начинался, скажем, с [10, 10]». Для этого мы рисуем изображение так, чтобы [10, 10] находилось в начале нового изображения.
Колинта
35

Ни один из ответов здесь не обрабатывает все проблемы масштаба и вращения на 100% правильно. Вот обобщение всего, что было сказано до сих пор, начиная с iOS7 / 8. Он предназначен для включения в качестве метода в категорию на UIImage.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}
волк
источник
1
Почему за этот ответ не проголосовали больше? Я попробовал все приведенные выше ответы и столкнулся с множеством проблем (в частности, преобразование из UIImage в CIImage и потеря правильных данных преобразования). Это единственное, что работает для меня!
Агрессор
1
Спасибо @Aggressor, рад, что это работает для вас. Мой ответ был опубликован только месяц назад, тогда как многие из этих ответов были здесь в течение 5 лет. Я предполагаю, что это объясняет отсутствие сравнимых голосов.
Авольф
Я использую это, чтобы провести фильтры по изображениям (например, как это делает Snapchat), и у меня появляются небольшие проблемы с масштабированием. Есть ли в вашем коде что-то, что вы можете посоветовать мне посмотреть, что может содержать причину этой ошибки масштабирования? Я разместил свое решение, использующее ваш код, здесь: stackoverflow.com/questions/23319497/…
Aggressor
очень полезная функция, спасибо. какие советы, что нужно изменить, чтобы обрезать фокусировку на центральной части изображения?
личное
ЭТО ДОЛЖНО БЫТЬ ОТВЕТОМ.
horseshoe7
17

Быстрая версия awolfответа, которая сработала для меня:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}
Марк Леонард
источник
10
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

Большинство ответов, которые я видел, касается только позиции (0, 0) для (x, y). Хорошо, это один случай, но я бы хотел, чтобы моя операция кадрирования была сосредоточена. Мне потребовалось некоторое время, чтобы понять, какая строка следует за комментарием WTF.

Давайте возьмем случай изображения, снятого с портретной ориентацией:

  1. Высота исходного изображения превышает ширину (пока ничего удивительного!)
  2. Изображение, которое метод CGImageCreateWithImageInRect представляет в своем собственном мире, на самом деле не портрет, а пейзаж (поэтому также, если вы не используете аргумент ориентации в конструкторе imageWithCGImage, он будет отображаться как повернутый на 180).
  3. Итак, вы должны представить, что это пейзаж, позиция (0, 0) - верхний правый угол изображения.

Надеюсь, это имеет смысл! Если этого не произойдет, попробуйте другие значения, и вы увидите, что логика инвертируется, когда дело доходит до выбора правильных x, y, width и height для вашего cropRect.

Иордания
источник
8

swift3

extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}
neoneye
источник
7

Быстрое расширение

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}
Эпический Байт
источник
4

Лучшее решение для обрезки UIImage в Swift , с точки зрения точности, масштабирования пикселей ...:

private func squareCropImageToSideLength(let sourceImage: UIImage,
    let sideLength: CGFloat) -> UIImage {
        // input size comes from image
        let inputSize: CGSize = sourceImage.size

        // round up side length to avoid fractional output size
        let sideLength: CGFloat = ceil(sideLength)

        // output size has sideLength for both dimensions
        let outputSize: CGSize = CGSizeMake(sideLength, sideLength)

        // calculate scale so that smaller dimension fits sideLength
        let scale: CGFloat = max(sideLength / inputSize.width,
            sideLength / inputSize.height)

        // scaling the image with this scale results in this output size
        let scaledInputSize: CGSize = CGSizeMake(inputSize.width * scale,
            inputSize.height * scale)

        // determine point in center of "canvas"
        let center: CGPoint = CGPointMake(outputSize.width/2.0,
            outputSize.height/2.0)

        // calculate drawing rect relative to output Size
        let outputRect: CGRect = CGRectMake(center.x - scaledInputSize.width/2.0,
            center.y - scaledInputSize.height/2.0,
            scaledInputSize.width,
            scaledInputSize.height)

        // begin a new bitmap context, scale 0 takes display scale
        UIGraphicsBeginImageContextWithOptions(outputSize, true, 0)

        // optional: set the interpolation quality.
        // For this you need to grab the underlying CGContext
        let ctx: CGContextRef = UIGraphicsGetCurrentContext()
        CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh)

        // draw the source image into the calculated rect
        sourceImage.drawInRect(outputRect)

        // create new image from bitmap context
        let outImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()

        // clean up
        UIGraphicsEndImageContext()

        // pass back new image
        return outImage
}

Инструкции, используемые для вызова этой функции:

let image: UIImage = UIImage(named: "Image.jpg")!
let squareImage: UIImage = self.squareCropImageToSideLength(image, sideLength: 320)
self.myUIImageView.image = squareImage

Примечание: исходное вдохновение исходного кода, написанное в Objective-C, было найдено в блоге "Cocoanetics".

Король-Мастер
источник
3

Ниже может помочь фрагмент кода.

import UIKit

extension UIImage {
    func cropImage(toRect rect: CGRect) -> UIImage? {
        if let imageRef = self.cgImage?.cropping(to: rect) {
            return UIImage(cgImage: imageRef)
        }
        return nil
    }
}
luhuiya
источник
2

Выглядит немного странно, но прекрасно работает и учитывает ориентацию изображения:

var image:UIImage = ...

let img = CIImage(image: image)!.imageByCroppingToRect(rect)
image = UIImage(CIImage: img, scale: 1, orientation: image.imageOrientation)
MuniekMg
источник
1
но это явно нарушает масштаб
Sulthan
2

Свифт 5:

extension UIImage {
    func cropped(rect: CGRect) -> UIImage? {
        guard let cgImage = cgImage else { return nil }

        UIGraphicsBeginImageContextWithOptions(rect.size, false, 0)
        let context = UIGraphicsGetCurrentContext()

        context?.translateBy(x: 0.0, y: self.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        context?.draw(cgImage, in: CGRect(x: rect.minX, y: rect.minY, width: self.size.width, height: self.size.height), byTiling: false)


        let croppedImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return croppedImage
    }
}
huync
источник
1
- (UIImage *)getSubImage:(CGRect) rect{
    CGImageRef subImageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    CGRect smallBounds = CGRectMake(rect.origin.x, rect.origin.y, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));

    UIGraphicsBeginImageContext(smallBounds.size);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextDrawImage(context, smallBounds, subImageRef);
    UIImage* smallImg = [UIImage imageWithCGImage:subImageRef];
    UIGraphicsEndImageContext();

    return smallImg;
}
золотой
источник
1
 (UIImage *)squareImageWithImage:(UIImage *)image scaledToSize:(CGSize)newSize {
    double ratio;
    double delta;
    CGPoint offset;

    //make a new square size, that is the resized imaged width
    CGSize sz = CGSizeMake(newSize.width, newSize.width);

    //figure out if the picture is landscape or portrait, then
    //calculate scale factor and offset
    if (image.size.width > image.size.height) {
        ratio = newSize.width / image.size.width;
        delta = (ratio*image.size.width - ratio*image.size.height);
        offset = CGPointMake(delta/2, 0);
    } else {
        ratio = newSize.width / image.size.height;
        delta = (ratio*image.size.height - ratio*image.size.width);
        offset = CGPointMake(0, delta/2);
    }

    //make the final clipping rect based on the calculated values
    CGRect clipRect = CGRectMake(-offset.x, -offset.y,
                                 (ratio * image.size.width) + delta,
                                 (ratio * image.size.height) + delta);


    //start a new context, with scale factor 0.0 so retina displays get
    //high quality image
    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
        UIGraphicsBeginImageContextWithOptions(sz, YES, 0.0);
    } else {
        UIGraphicsBeginImageContext(sz);
    }
    UIRectClip(clipRect);
    [image drawInRect:clipRect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return newImage;
}
Bhushan_pawar
источник
1

На iOS9.2SDK я использую метод ниже, чтобы преобразовать кадр из UIView в UIImage

-(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
{
  CGSize cropSize = rect.size;
  CGFloat widthScale = image.size.width/self.imageViewOriginal.bounds.size.width;
  CGFloat heightScale = image.size.height/self.imageViewOriginal.bounds.size.height;
  cropSize = CGSizeMake(rect.size.width*widthScale, 
              rect.size.height*heightScale);
  CGPoint pointCrop = CGPointMake(rect.origin.x*widthScale,
             rect.origin.y*heightScale);
  rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, cropSize.height);
  CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
  UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
  CGImageRelease(subImage);

  return croppedImage;
}
user5176302
источник
1

Обновление Swift 2.0 ( CIImageсовместимость)

Расширение от ответа Максима, но работает, если ваше изображение также CIImageосновано.

public extension UIImage {
    func imageByCroppingToRect(rect: CGRect) -> UIImage? {
        if let image = CGImageCreateWithImageInRect(self.CGImage, rect) {
            return UIImage(CGImage: image)
        } else if let image = (self.CIImage)?.imageByCroppingToRect(rect) {
            return UIImage(CIImage: image)
        }
       return nil
   }
}
NoodleOfDeath
источник
1

Вот обновленная версия Swift 3, основанная на ответе Лапши

func cropping(to rect: CGRect) -> UIImage? {

    if let cgCrop = cgImage?.cropping(to: rect) {
        return UIImage(cgImage: cgCrop)
    }
    else if let ciCrop = ciImage?.cropping(to: rect) {
        return UIImage(ciImage: ciCrop)
    }

    return nil
}
voidref
источник
1

Следуй за ответом @Arne. Я просто фиксирую функцию категории. положить его в категорию UIImage.

-(UIImage*)cropImage:(CGRect)rect{

    UIGraphicsBeginImageContextWithOptions(rect.size, false, [self scale]);
    [self drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    UIImage* cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}
Лин Нгуен
источник
0

Я не был удовлетворен другими решениями, потому что они либо рисуют несколько раз (используя больше энергии, чем необходимо), либо имеют проблемы с ориентацией. Вот что я использовал для масштабированного квадратного обрезанного изображения из изображения UIImage *.

CGFloat minimumSide = fminf(image.size.width, image.size.height);
CGFloat finalSquareSize = 600.;

//create new drawing context for right size
CGRect rect = CGRectMake(0, 0, finalSquareSize, finalSquareSize);
CGFloat scalingRatio = 640.0/minimumSide;
UIGraphicsBeginImageContext(rect.size);

//draw
[image drawInRect:CGRectMake((minimumSide - photo.size.width)*scalingRatio/2., (minimumSide - photo.size.height)*scalingRatio/2., photo.size.width*scalingRatio, photo.size.height*scalingRatio)];

UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();
Матье Руиф
источник
0

Я использую метод ниже.

  -(UIImage *)getNeedImageFrom:(UIImage*)image cropRect:(CGRect)rect
  {
    CGSize cropSize = rect.size;
    CGFloat widthScale =  
    image.size.width/self.imageViewOriginal.bounds.size.width;
    CGFloat heightScale = 
    image.size.height/self.imageViewOriginal.bounds.size.height;
    cropSize = CGSizeMake(rect.size.width*widthScale,  
    rect.size.height*heightScale);
    CGPoint  pointCrop = CGPointMake(rect.origin.x*widthScale, 
    rect.origin.y*heightScale);
    rect = CGRectMake(pointCrop.x, pointCrop.y, cropSize.width, 
    cropSize.height);
    CGImageRef subImage = CGImageCreateWithImageInRect(image.CGImage, rect);
    UIImage *croppedImage = [UIImage imageWithCGImage:subImage];
    CGImageRelease(subImage);
    return croppedImage;

}

user5176302
источник
0

Посмотрите на https://github.com/vvbogdan/BVCropPhoto

- (UIImage *) croppedImage {
    CGFloat scale = self.sourceImage.size.width / self.scrollView.contentSize.width;

    UIImage * finalImage = ноль;
    CGRect targetFrame = CGRectMake ((self.scrollView.contentInset.left + self.scrollView.contentOffset.x) * scale,
            (self.scrollView.contentInset.top + self.scrollView.contentOffset.y) * scale,
            self.cropSize.width * scale,
            self.cropSize.height * scale);

    CGImageRef contextImage = CGImageCreateWithImageInRect ([[self imageWithRotation: self.sourceImage] CGImage], targetFrame);

    if (contextImage! = NULL) {
        finalImage = [UIImage imageWithCGImage: contextImage
                                         Масштаб: self.sourceImage.scale
                                   Ориентация: UIImageOrientationUp];

        CGImageRelease (contextImage);
    }

    вернуть finalImage;
}


- (UIImage *) imageWithRotation: (UIImage *) image {


    if (image.imageOrientation == UIImageOrientationUp) возвращаемое изображение;
    CGAffineTransform transform = CGAffineTransformIdentity;

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, image.size.height);
            transform = CGAffineTransformRotate (transform, M_PI);
            перерыв;

        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformRotate (transform, M_PI_2);
            перерыв;

        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, 0, image.size.height);
            transform = CGAffineTransformRotate (transform, -M_PI_2);
            перерыв;
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            перерыв;
    }

    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.width, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            перерыв;

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate (transform, image.size.height, 0);
            transform = CGAffineTransformScale (transform, -1, 1);
            перерыв;
        case UIImageOrientationUp:
        case UIImageOrientationDown:
        case UIImageOrientationLeft:
        case UIImageOrientationRight:
            перерыв;
    }

    // Теперь мы рисуем базовый CGImage в новом контексте, применяя преобразование
    // рассчитано выше.
    CGContextRef ctx = CGBitmapContextCreate (NULL, image.size.width, image.size.height,
            CGImageGetBitsPerComponent (image.CGImage), 0,
            CGImageGetColorSpace (image.CGImage),
            CGImageGetBitmapInfo (image.CGImage));
    CGContextConcatCTM (ctx, transform);
    switch (image.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr ...
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.height, image.size.width), image.CGImage);
            перерыв;

        дефолт:
            CGContextDrawImage (ctx, CGRectMake (0, 0, image.size.width, image.size.height), image.CGImage);
            перерыв;
    }

    // А теперь мы просто создаем новый UIImage из контекста рисования
    CGImageRef cgimg = CGBitmapContextCreateImage (ctx);
    UIImage * img = [UIImage imageWithCGImage: cgimg];
    CGContextRelease (CTX);
    CGImageRelease (cgimg);
    вернуть img;

}
Nowytech
источник