Как обрезать UIImage? - PullRequest
       33

Как обрезать UIImage?

38 голосов
/ 14 апреля 2010

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

- (NSInteger) processImage1: (UIImage*) image
{

 CGFloat width = image.size.width;
 CGFloat height = image.size.height;
 struct pixel* pixels = (struct pixel*) calloc(1, image.size.width * image.size.height * sizeof(struct pixel));
 if (pixels != nil)
 {
  // Create a new bitmap
  CGContextRef context = CGBitmapContextCreate(
              (void*) pixels,
              image.size.width,
              image.size.height,
              8,
              image.size.width * 4,
              CGImageGetColorSpace(image.CGImage),
              kCGImageAlphaPremultipliedLast
              );
  if (context != NULL)
  {
   // Draw the image in the bitmap
   CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, image.size.width, image.size.height), image.CGImage);
   NSUInteger numberOfPixels = image.size.width * image.size.height;

   NSMutableArray *numberOfPixelsArray = [[[NSMutableArray alloc] initWithCapacity:numberOfPixelsArray] autorelease];
}

Как мне взять (обрезать за пределами границы) среднюю часть UIImage ?????????

Ответы [ 7 ]

79 голосов
/ 14 апреля 2010

Попробуйте что-то вроде этого:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
image = [UIImage imageWithCGImage:imageRef]; 
CGImageRelease(imageRef);

Примечание: cropRect - это меньший прямоугольник со средней частью изображения ...

39 голосов
/ 28 июля 2010

Я искал способ получить произвольный прямоугольный фрагмент (т. Е. Субизображение) UIImage.

Большинство решений, которые я пробовал, не работают, если ориентация изображения отличается от UIImageOrientationUp.

Например:

http://www.hive05.com/2008/11/crop-an-image-using-the-iphone-sdk/

Как правило, если вы используете камеру iPhone, у вас будут другие ориентации, такие как UIImageOrientationLeft, и вы не получите правильный урожай с вышеописанным. Это происходит из-за использования CGImageRef / CGContextDrawImage, которые отличаются в системе координат относительно UIImage.

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


// get sub image
- (UIImage*) getSubImageFrom: (UIImage*) img WithRect: (CGRect) rect {

    UIGraphicsBeginImageContext(rect.size);
    CGContextRef context = UIGraphicsGetCurrentContext();

    // translated rectangle for drawing sub image 
    CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, img.size.width, img.size.height);

    // clip to the bounds of the image context
    // not strictly necessary as it will get clipped anyway?
    CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));

    // draw image
    [img drawInRect:drawRect];

    // grab image
    UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return subImage;
}

2 голосов
/ 30 декабря 2017

Поскольку я нуждался в этом только что, вот код M-V в Swift 4:

func imageWithImage(image: UIImage, croppedTo rect: CGRect) -> UIImage {

    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()

    let drawRect = CGRect(x: -rect.origin.x, y: -rect.origin.y, 
                          width: image.size.width, height: image.size.height)

    context?.clip(to: CGRect(x: 0, y: 0, 
                             width: rect.size.width, height: rect.size.height))

    image.draw(in: drawRect)

    let subImage = UIGraphicsGetImageFromCurrentImageContext()

    UIGraphicsEndImageContext()
    return subImage!
}
2 голосов
/ 27 сентября 2012

В конечном итоге было бы быстрее, с гораздо меньшим созданием изображений из атласов спрайтов, если бы вы могли установить не только изображение для UIImageView, но и смещение в верхнем левом углу для отображения в этом UIImage. Может быть, это возможно. Это, безусловно, устранит много усилий!

Тем временем я создал эти полезные функции в служебном классе, который я использую в своих приложениях. Он создает UIImage из части другого UIImage с опциями поворота, масштабирования и отражения, используя стандартные значения UIImageOrientation для указания. Пиксельное масштабирование сохраняется от исходного изображения.

Мое приложение создает много UIImages во время инициализации, и это обязательно требует времени. Но некоторые изображения не нужны, пока не выбрана определенная вкладка. Чтобы создать видимость более быстрой загрузки, я мог бы создать их в отдельном потоке, созданном при запуске, а затем просто подождать, пока это не будет сделано, когда выбрана эта вкладка.

Этот код также размещен на Самый эффективный способ нарисовать часть изображения в iOS

+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture {
    return [ChordCalcController imageByCropping:imageToCrop toRect:aperture withOrientation:UIImageOrientationUp];
}

// Draw a full image into a crop-sized area and offset to produce a cropped, rotated image
+ (UIImage*)imageByCropping:(UIImage *)imageToCrop toRect:(CGRect)aperture withOrientation:(UIImageOrientation)orientation {

            // convert y coordinate to origin bottom-left
    CGFloat orgY = aperture.origin.y + aperture.size.height - imageToCrop.size.height,
            orgX = -aperture.origin.x,
            scaleX = 1.0,
            scaleY = 1.0,
            rot = 0.0;
    CGSize size;

    switch (orientation) {
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            size = CGSizeMake(aperture.size.height, aperture.size.width);
            break;
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
        case UIImageOrientationUp:
        case UIImageOrientationUpMirrored:
            size = aperture.size;
            break;
        default:
            assert(NO);
            return nil;
    }


    switch (orientation) {
        case UIImageOrientationRight:
            rot = 1.0 * M_PI / 2.0;
            orgY -= aperture.size.height;
            break;
        case UIImageOrientationRightMirrored:
            rot = 1.0 * M_PI / 2.0;
            scaleY = -1.0;
            break;
        case UIImageOrientationDown:
            scaleX = scaleY = -1.0;
            orgX -= aperture.size.width;
            orgY -= aperture.size.height;
            break;
        case UIImageOrientationDownMirrored:
            orgY -= aperture.size.height;
            scaleY = -1.0;
            break;
        case UIImageOrientationLeft:
            rot = 3.0 * M_PI / 2.0;
            orgX -= aperture.size.height;
            break;
        case UIImageOrientationLeftMirrored:
            rot = 3.0 * M_PI / 2.0;
            orgY -= aperture.size.height;
            orgX -= aperture.size.width;
            scaleY = -1.0;
            break;
        case UIImageOrientationUp:
            break;
        case UIImageOrientationUpMirrored:
            orgX -= aperture.size.width;
            scaleX = -1.0;
            break;
    }

    // set the draw rect to pan the image to the right spot
    CGRect drawRect = CGRectMake(orgX, orgY, imageToCrop.size.width, imageToCrop.size.height);

    // create a context for the new image
    UIGraphicsBeginImageContextWithOptions(size, NO, imageToCrop.scale);
    CGContextRef gc = UIGraphicsGetCurrentContext();

    // apply rotation and scaling
    CGContextRotateCTM(gc, rot);
    CGContextScaleCTM(gc, scaleX, scaleY);

    // draw the image to our clipped context using the offset rect
    CGContextDrawImage(gc, drawRect, imageToCrop.CGImage);

    // pull the image from our cropped context
    UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();

    // pop the context to get back to the default
    UIGraphicsEndImageContext();

    // Note: this is autoreleased
    return cropped;
}
0 голосов
/ 04 апреля 2018

Использование функции

CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));

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

- (UIImage *)aspectFillToSize:(CGSize)size
{
    CGFloat imgAspect = self.size.width / self.size.height;
    CGFloat sizeAspect = size.width/size.height;

    CGSize scaledSize;

        if (sizeAspect > imgAspect) { // increase width, crop height
            scaledSize = CGSizeMake(size.width, size.width / imgAspect);
        } else { // increase height, crop width
            scaledSize = CGSizeMake(size.height * imgAspect, size.height);
        }
    UIGraphicsBeginImageContextWithOptions(size, NO, 0.0f);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextClipToRect(context, CGRectMake(0, 0, size.width, size.height));
    [self drawInRect:CGRectMake(0.0f, 0.0f, scaledSize.width, scaledSize.height)];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}
0 голосов
/ 28 мая 2015

Я хотел иметь возможность обрезать область, основываясь на соотношении сторон, и масштабировать до размера, основанного на внешнем ограничивающем экстенте. Вот мой вариант:

import AVFoundation
import ImageIO

class Image {

    class func crop(image:UIImage, source:CGRect, aspect:CGSize, outputExtent:CGSize) -> UIImage {

        let sourceRect = AVMakeRectWithAspectRatioInsideRect(aspect, source)
        let targetRect = AVMakeRectWithAspectRatioInsideRect(aspect, CGRect(origin: CGPointZero, size: outputExtent))

        let opaque = true, deviceScale:CGFloat = 0.0 // use scale of device's main screen
        UIGraphicsBeginImageContextWithOptions(targetRect.size, opaque, deviceScale)

        let scale = max(
            targetRect.size.width / sourceRect.size.width,
            targetRect.size.height / sourceRect.size.height)

        let drawRect = CGRect(origin: -sourceRect.origin * scale, size: image.size * scale)
        image.drawInRect(drawRect)

        let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return scaledImage
    }
}

Есть пара вещей, которые я нахожу запутанными, отдельные проблемы обрезки и изменения размера. Обрезка обрабатывается с началом координат прямоугольника, который вы передаете drawInRect, а масштабирование обрабатывается частью размера. В моем случае мне нужно было связать размер прямоугольника обрезки на источнике с моим выходным прямоугольником с тем же соотношением сторон. Затем масштабный коэффициент выводится / вводится, и его необходимо применить к drawRect (передается в drawInRect).

Одно предостережение в том, что этот подход фактически предполагает, что изображение, которое вы рисуете, больше, чем контекст изображения. Я не проверял это, но я думаю, что вы можете использовать этот код для обработки кадрирования / масштабирования, но явно определив параметр масштаба как вышеупомянутый параметр масштаба. По умолчанию UIKit применяет множитель в зависимости от разрешения экрана.

Наконец, следует отметить, что этот подход UIKit имеет более высокий уровень, чем подходы CoreGraphics / Quartz и Core Image, и, похоже, решает проблемы ориентации изображения. Также стоит упомянуть, что это довольно быстро, второй после ImageIO, согласно этому посту здесь: http://nshipster.com/image-resizing/

0 голосов
/ 27 января 2015

Если вы хотите обрезать портрет по центру каждой фотографии.

Используйте решение @ M-V и замените cropRect.

CGFloat height = imageTaken.size.height;
CGFloat width = imageTaken.size.width;

CGFloat newWidth = height * 9 / 16;
CGFloat newX = abs((width - newWidth)) / 2;

CGRect cropRect = CGRectMake(newX,0, newWidth ,height);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...