39

I develop an application in which i process the image using its pixels but in that image processing it takes a lot of time. Therefore i want to crop UIImage (Only middle part of image i.e. removing/croping bordered part of image).I have the develop code are,

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

How i take(croping outside bordered) the middle part of UIImage?????????

Duck
  • 34,902
  • 47
  • 248
  • 470
Rajendra Bhole
  • 391
  • 1
  • 3
  • 4

8 Answers8

82

Try something like this:

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

Note: cropRect is smaller rectangle with middle part of the image...

Lukas
  • 1,346
  • 7
  • 24
  • 49
Mihir Mehta
  • 13,743
  • 3
  • 64
  • 88
  • mihirpmehta, now i trying to do something like that, - (UIImage *) cropedImage: (UIImage *) image { CGFloat width = image.size.width; CGFloat height = image.size.height; UIImage *cropedImage = [[UIImage alloc] init]; CGFloat widthCrop = (image.size.width)/2; CGFloat heightCrop = (image.size.height)/2; } Thereafter i can't visualize what to do? – Rajendra Bhole Apr 14 '10 at 07:25
  • get new X and New Y as OldX + OldX/4 and OldY + OldY/4 and make REctangle with new width height NewX and NewY and use it as cropRect – Mihir Mehta Apr 14 '10 at 08:02
  • @mihirpmehta, your above method is just drawing the mirror image of original image in the cropRect rectangle. It could not been processed the UIImage. – Rajendra Bhole Apr 14 '10 at 16:59
  • sorry... change the XY coordinate to X+(Oldwidth/4) and y+(OldHeight/4)... my mistake.. – Mihir Mehta Apr 15 '10 at 04:40
  • @mihirpmehta, I think i need to crop the image using pixel processing from your given co-ordinate.But how it does? – Rajendra Bhole Apr 15 '10 at 08:16
  • This unfortunately retains the original UIImage which may not be desirable – jjxtra Feb 21 '14 at 04:29
  • can cropRect be a random shape ? – abh Aug 30 '16 at 14:01
  • In my case with retina screen, it did not clip my resized image properly. I have use CGContextClipToRect. Answered below. – karim Apr 04 '18 at 09:15
  • If you're cropping a UIImage, you must take into account of the UIImageOrientation. M-V and @dasdom's answer below is a cleaner one for UIImage. – CodeBrew Apr 06 '19 at 15:35
39

I was looking for a way to get an arbitrary rectangular crop (ie., sub-image) of a UIImage.

Most of the solutions I tried do not work if the orientation of the image is anything but UIImageOrientationUp.

For example:

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

Typically if you use your iPhone camera, you will have other orientations like UIImageOrientationLeft, and you will not get a correct crop with the above. This is because of the use of CGImageRef/CGContextDrawImage which differ in the coordinate system with respect to UIImage.

The code below uses UI* methods (no CGImageRef), and I have tested this with up/down/left/right oriented images, and it seems to work great.


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

M-V
  • 5,167
  • 7
  • 52
  • 55
  • 2
    Just be aware that you can only run the above code on the main thread. The advantage of avoiding `UIGraphicsBeginImageContext`, etc is to get around this limitation. – William Denniss Feb 02 '12 at 08:54
  • 1
    @William Denniss are you sure? http://stackoverflow.com/questions/11528803/is-uigraphicsbeginimagecontext-thread-safe/11530675#11530675 – Klaas Oct 18 '13 at 22:39
  • @Klass, you're right, since iOS 4 you can call it on any thread. However, the code **is still not thread safe**, you may only use `UIGraphicsBeginImageContext` on one thread at a time. The docs state: "Creates a bitmap-based graphics context and makes it the current context." I believe this "current context" is global, and thus not thread safe. My app processes multiple images at once, and I had crashes when using `UIGraphicsBeginImageContext` in multiple threads, so I switched to using `CGContextRef`'s which are thread-safe. – William Denniss Oct 19 '13 at 03:59
  • perfect! Cheers mate! – mmsarquis Jul 08 '18 at 14:01
4

Because I needed it just now, here is M-V 's code in 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!
}
dasdom
  • 13,975
  • 2
  • 47
  • 58
2

It would ultimately be faster, with a lot less image creation from sprite atlases, if you could set not only the image for a UIImageView, but also the top-left offset to display within that UIImage. Maybe this is possible. It would certainly eliminate a lot of effort!

Meanwhile, I created these useful functions in a utility class that I use in my apps. It creates a UIImage from part of another UIImage, with options to rotate, scale, and flip using standard UIImageOrientation values to specify. The pixel scaling is preserved from the original image.

My app creates a lot of UIImages during initialization, and this necessarily takes time. But some images aren't needed until a certain tab is selected. To give the appearance of quicker load I could create them in a separate thread spawned at startup, then just wait till it's done when that tab is selected.

This code is also posted at Most efficient way to draw part of an image in 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;
}
Community
  • 1
  • 1
Scott Lahteine
  • 1,040
  • 11
  • 20
  • Thanks. What should be the Orientation that I send for the image if I took it from AVFoundation camera which rotates the images? – Dejell Feb 03 '13 at 15:25
2

#Very small/simple Swift 5 version,

You shouldn't mix UI and CG objects, they sometimes have very different coordinate spaces. This can make you sad.

Note : self.draw(at:)

@inlinable private prefix func - (right: CGPoint) -> CGPoint
{
    return CGPoint(x: -right.x, y: -right.y)
}

extension UIImage
{
    public func cropped(to cropRect: CGRect) -> UIImage?
    {
           let renderer = UIGraphicsImageRenderer(size: cropRect.size)
        return renderer.image
        {
           _ in
           
            self.draw(at: -cropRect.origin)
        }
    }
}
Leslie Godwin
  • 2,601
  • 26
  • 18
1

Using the function

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

Here's an example code, used for a different purpose but clips ok.

- (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;
}
karim
  • 15,408
  • 7
  • 58
  • 96
0

If you want a portrait crop down the center of every photo.

Use @M-V solution, & replace 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);
0

I wanted to be able to crop from a region based on an aspect ratio, and scale to a size based on a outer bounding extent. Here is my variation:

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

There are a couple things that I found confusing, the separate concerns of cropping and resizing. Cropping is handled with the origin of the rect that you pass to drawInRect, and scaling is handled by the size portion. In my case, I needed to relate the size of the cropping rect on the source, to my output rect of the same aspect ratio. The scale factor is then output / input, and this needs to be applied to the drawRect (passed to drawInRect).

One caveat is that this approach effectively assumes that the image you are drawing is larger than the image context. I have not tested this, but I think you can use this code to handle cropping / zooming, but explicitly defining the scale parameter to be the aforementioned scale parameter. By default, UIKit applies a multiplier based on the screen resolution.

Finally, it should be noted that this UIKit approach is higher level than CoreGraphics / Quartz and Core Image approaches, and seems to handle image orientation issues. It is also worth mentioning that it is pretty fast, second to ImageIO, according to this post here: http://nshipster.com/image-resizing/

Chris Conover
  • 8,889
  • 5
  • 52
  • 68