I am having issues with my AVCaptureVideoPreviewLayer method when grabbing a cropped UIImage of the viewable screen. Currently it is working but not outputting the correct crop that I require.
I am trying to output a square but it (by the looks of it) seems to be giving the full height and compressing the image.
The before image shows the LIVE screen and the after image shows the image once the capture button has been pressed. You can see that it has been changed vertically to fit the square but the height hasn't been cropped vertically.
Capture Image Code
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
[self processImage:[UIImage imageWithData:imageData]];
}
}];
Cropping code
- (void) processImage:(UIImage *)image { //process captured image, crop, resize and rotate
haveImage = YES;
CGRect deviceScreen = _previewLayer.bounds;
CGFloat width = deviceScreen.size.width;
CGFloat height = deviceScreen.size.height;
NSLog(@"WIDTH %f", width); // Outputing 320
NSLog(@"HEIGHT %f", height); // Outputting 320
UIGraphicsBeginImageContext(CGSizeMake(width, width));
[image drawInRect: CGRectMake(0, 0, width, width)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
CGRect cropRect = CGRectMake(0, 0, width, width);
CGImageRef imageRef = CGImageCreateWithImageInRect([smallImage CGImage], cropRect);
CGImageRelease(imageRef);
[captureImageGrab setImage:[UIImage imageWithCGImage:imageRef]];
}
Even if the preview layer is square, keep in mind that the generated still image keeps its original size.
From what I see, the problem is here:
You already made your context square with the first line. You still need to draw your image in its original format, it will be clipped by that context. On the second line, you are forcing the drawing of the original image in a square, thus making it look "squeezed".
You should find the right image height that keeps the original ratio while fitting your "width". Next, you will want to draw that image with the correct size (keeping the original ratio) in your square context. If you want to clip the center, change your Y position of the drawing.
Something similar to this:
That should do it.
Have you tried to set videoGravity?
Looking at the code it appears to me that you are drawing the image into a square. This is the part that shrinks the image without regard to the aspect ratio.
Instead, do something like this:
The aspect ratio is distorted because the image captured by the camera is not square. When drawing it into a square it will be drawn with a distorted aspect ratio. A possible solution is to draw the image into the square preserving the aspect ration like:
the new image will be of width width. For that width the height that will preserve the aspect ratio is aspect_h. Then, the image is shifted along the y axis to center it vertically.
Notice that if your app were to work on orientations that are not portrait a little more work is needed.
Another observation: you can get a better resolution image if you use image.size.width as the width for the new image context.
hope this helps!