Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a perfect crop (without changing the quality) in Objective-c/Cocoa (OSX)

Is there any way in Objective-c/cocoa (OSX) to crop an image without changing the quality of the image?

I am very near to a solution, but there are still some differences that I can detect in the color. I can notice it when zooming into the text. Here is the code I am currently using:

    NSImage *target = [[[NSImage alloc]initWithSize:panelRect.size] autorelease];
    target.backgroundColor = [NSColor greenColor];
    //start drawing on target
    [target lockFocus];
    [NSGraphicsContext saveGraphicsState];
    [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationNone];
    [[NSGraphicsContext currentContext] setShouldAntialias:NO];

    //draw the portion of the source image on target image
    [source drawInRect:NSMakeRect(0,0,panelRect.size.width,panelRect.size.height)
              fromRect:NSMakeRect(panelRect.origin.x , source.size.height - panelRect.origin.y - panelRect.size.height, panelRect.size.width, panelRect.size.height)
             operation:NSCompositeCopy
              fraction:1.0];

    [NSGraphicsContext restoreGraphicsState];
    //end drawing
    [target unlockFocus];

    //create a NSBitmapImageRep
    NSBitmapImageRep *bmpImageRep = [[[NSBitmapImageRep alloc]initWithData:[target TIFFRepresentation]] autorelease];
    //add the NSBitmapImage to the representation list of the target
    [target addRepresentation:bmpImageRep];
    //get the data from the representation
    NSData *data = [bmpImageRep representationUsingType: NSJPEGFileType
                                             properties: imageProps];
    NSString *filename = [NSString stringWithFormat:@"%@%@.jpg", panelImagePrefix, panelNumber];
    NSLog(@"This is the filename: %@", filename);
    //write the data to a file
    [data writeToFile:filename atomically:NO];

Here is a zoomed-in comparison of the original and the cropped image:

The Original Image (Original image - above)

The Cropped Image (Cropped image - above)

The difference is hard to see, but if you flick between them, you can notice it. You can use a colour picker to notice the difference as well. For example, the darkest pixel on the bottom row of the image is a different shade.

I also have a solution that works exactly the way I want it in iOS. Here is the code:

-(void)testMethod:(int)page forRect:(CGRect)rect{
    NSString *filePath = @"imageName";

    NSData *data = [HeavyResourceManager dataForPath:filePath];//this just gets the image as NSData
    UIImage *image = [UIImage imageWithData:data];

    CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage], rect);//crop in the rect

    UIImage *result = [UIImage imageWithCGImage:imageRef scale:0 orientation:image.imageOrientation];
    CGImageRelease(imageRef);

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectoryPath = [paths objectAtIndex:0];

    [UIImageJPEGRepresentation(result, 1.0) writeToFile:[documentsDirectoryPath stringByAppendingPathComponent::@"output.jpg"] atomically:YES];
}

So, is there a way to crop an image in OSX so that the cropped image does not change at all? Perhaps I have to look into a different library, but I would be surprised if I could not do this with Objective-C...


Note, This is a follow up question to my previous question here.


Update I have tried (as per the suggestion) to round the CGRect values to whole numbers, but did not notice a difference. Here is the code in case I used:

    [source drawInRect:NSMakeRect(0,0,(int)panelRect.size.width,(int)panelRect.size.height)
              fromRect:NSMakeRect((int)panelRect.origin.x , (int)(source.size.height - panelRect.origin.y - panelRect.size.height), (int)panelRect.size.width, (int)panelRect.size.height)
             operation:NSCompositeCopy
              fraction:1.0];

Update I have tried mazzaroth code and it works if I save it as a png, but if I try and save it as a jpeg, the image loses quality. So close, but not close enough. Still hoping for a complete answer...

like image 944
lindon fox Avatar asked Dec 05 '12 04:12

lindon fox


2 Answers

use CGImageCreateWithImageInRect.

// this chunk of code loads a jpeg image into a cgimage
// creates a second crop of the original image with CGImageCreateWithImageInRect
// writes the new cropped image to the desktop
// ensure that the xy origin of the CGRectMake call is smaller than the width or height of the original image

NSURL *originalImage = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"lockwood" ofType:@"jpg"]];
CGImageRef imageRef = NULL;

CGImageSourceRef loadRef = CGImageSourceCreateWithURL((CFURLRef)originalImage, NULL);
if (loadRef != NULL)
{
    imageRef = CGImageSourceCreateImageAtIndex(loadRef, 0, NULL);
    CFRelease(loadRef); // Release CGImageSource reference
}    
CGImageRef croppedImage = CGImageCreateWithImageInRect(imageRef, CGRectMake(200., 200., 100., 100.));

CFURLRef saveUrl = (CFURLRef)[NSURL fileURLWithPath:[@"~/Desktop/lockwood-crop.jpg" stringByExpandingTildeInPath]];
CGImageDestinationRef destination = CGImageDestinationCreateWithURL(saveUrl, kUTTypeJPEG, 1, NULL);
CGImageDestinationAddImage(destination, croppedImage, nil);

if (!CGImageDestinationFinalize(destination)) {
    NSLog(@"Failed to write image to %@", saveUrl);
}

CFRelease(destination);
CFRelease(imageRef);
CFRelease(croppedImage);

I also made a gist:

https://gist.github.com/4259594

like image 94
maz Avatar answered Nov 16 '22 03:11

maz


Try to change the drawInRect orign to 0.5,0.5. Otherwise Quartz will distribute each pixel color to the adjacent 4 fixels.

Set the color space of the target image. You might be having a different colorspace causing to to look slightly different.

Try the various rendering intents and see which gets the best result, perceptual versus relative colorimetric etc. There are 4 options I think.

You mention that the colors get modified by the saving of JPEG versus PNG.

You can specify the compression level when saving to JPEG. Try with something like 0.8 or 0.9. you can also save JPEG without compression with 1.0, but ther PNG has a distinct advantage. You specify the compression level in the options dictionary for CGImageDesinationAddImage.

Finally - if nothing her helps - you should open a TSI with DTS, they can certainly provide you with the guidance you seek.

like image 45
Cocoanetics Avatar answered Nov 16 '22 02:11

Cocoanetics