Обрезка UIImage - PullRequest
       152

Обрезка UIImage

204 голосов
/ 01 октября 2008

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

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

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

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

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

Ответы [ 23 ]

236 голосов
/ 03 апреля 2009

Обновление 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);
88 голосов
/ 09 декабря 2011

Чтобы обрезать изображения сетчатки, сохраняя тот же масштаб и ориентацию, используйте следующий метод в категории 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;
}
64 голосов
/ 09 октября 2011

Вы можете создать категорию 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];
49 голосов
/ 13 сентября 2014

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

enter image description here

с помощью этой функции моста 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)
    )
}

Выход:

enter image description here

49 голосов
/ 05 февраля 2013

Вот моя реализация обрезки 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;
}
38 голосов
/ 04 сентября 2013

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

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

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

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;
}
32 голосов
/ 13 августа 2014

Ни один из ответов здесь не обрабатывает все проблемы масштаба и вращения на 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;
}
10 голосов
/ 29 марта 2011
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.

7 голосов
/ 22 мая 2015

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

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
    }
}
7 голосов
/ 04 ноября 2016

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