Take squarish photos with UIImagePickerController - ios

Is there an elegant way to only allow the user to take squarish photos with UIImagePickerController instead of the default rectangular ones? Something open source, maybe?

Here's the easiest way to do it (without reimplementing UIImagePickerController). First, use an overlay to make the camera field look square. Here's an example for 3.5" screens (you'd need to update it to work for iPhone 5):
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = source;
if (source == UIImagePickerControllerSourceTypeCamera) {
//Create camera overlay
CGRect f = imagePickerController.view.bounds;
f.size.height -= imagePickerController.navigationBar.bounds.size.height;
CGFloat barHeight = (f.size.height - f.size.width) / 2;
UIGraphicsBeginImageContext(f.size);
[[UIColor colorWithWhite:0 alpha:.5] set];
UIRectFillUsingBlendMode(CGRectMake(0, 0, f.size.width, barHeight), kCGBlendModeNormal);
UIRectFillUsingBlendMode(CGRectMake(0, f.size.height - barHeight, f.size.width, barHeight), kCGBlendModeNormal);
UIImage *overlayImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *overlayIV = [[UIImageView alloc] initWithFrame:f];
overlayIV.image = overlayImage;
[imagePickerController.cameraOverlayView addSubview:overlayIV];
}
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:nil];
Then, after you get a picture back from the UIImagePickerController, crop it to a square with something like this:
//Crop the image to a square
CGSize imageSize = image.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
if (width != height) {
CGFloat newDimension = MIN(width, height);
CGFloat widthOffset = (width - newDimension) / 2;
CGFloat heightOffset = (height - newDimension) / 2;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newDimension, newDimension), NO, 0.);
[image drawAtPoint:CGPointMake(-widthOffset, -heightOffset)
blendMode:kCGBlendModeCopy
alpha:1.];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
May be it will help you.

Related

Set maximum image resolution for PickerController?

I want take a image with a fixed resolution. The user need to be forced for crop the image. I need a square image.
I'am using next code and it just crop by width screen resolution.
self.imagepicker = [[UIImagePickerController alloc]init];
self.imagepicker.delegate = self;
self.imagepicker.allowsEditing=YES;
self.imagepicker.preferredContentSize = CGSizeMake(200, 200);
self.imagepicker.sourceType=UIImagePickerControllerSourceTypePhotoLibrary;
[self presentViewController:self.imagepicker animated:YES completion:nil];
preferredContentSize is not a property of the UIImagePickerController itself, but of any UIViewController, for when it is embedded in a UIPopoverController.
You'll need to present a cropping interface yourself.
If you want to done after selecting image please try this code for crop image in center
- (UIImage *)squareImageFromImage:(UIImage *)image scaledToSize:(CGFloat)newSize1
{
CGAffineTransform scaleTransform;
CGPoint origin;
CGFloat newSize;
if (image.size.width > image.size.height)
{
newSize=image.size.height;
CGFloat scaleRatio = newSize / image.size.height;
scaleTransform = CGAffineTransformMakeScale(scaleRatio, scaleRatio);
origin = CGPointMake(-(image.size.width - image.size.height) / 2.0f, 0);
}
else
{
newSize=image.size.width;
CGFloat scaleRatio = newSize / image.size.width;
scaleTransform = CGAffineTransformMakeScale(scaleRatio, scaleRatio);
origin = CGPointMake(0, -(image.size.height - image.size.width) / 2.0f);
}
CGSize size = CGSizeMake(newSize, newSize);
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(size, YES, 0);
} else {
UIGraphicsBeginImageContext(size);
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextConcatCTM(context, scaleTransform);
[image drawAtPoint:origin];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
Its helpful to you. Thanks

Move UIImage inside UIImageView

I have an UIImageView (red squares) that will display a UIImage that must be scaled (I can receive images greater or smaller that the UIImageView). After scaling it, the showed part of the UIImage is the center of it.
What I need is to show the part of the image in the blue squares, how can I archive it?
I'm only able to get the image size (height and width), but it display the original size, when it's supposed to be the scaled one.
self.viewIm = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 120, 80)];
self.viewIm.backgroundColor = [UIColor greenColor];
self.viewIm.layer.borderColor = [UIColor redColor].CGColor;
self.viewIm.layer.borderWidth = 5.0;
UIImage *im = [UIImage imageNamed:#"benjen"];
self.viewIm.image = im;
self.viewIm.contentMode = UIViewContentModeScaleAspectFill;
// self.viewim.clipsToBounds = YES;
[self.view addSubview:self.viewIm];
To do what you're trying to do, I'd recommend looking into CALayer's contentsRect property.
Since seeing your answer, I've been trying to work out the proper solution for a while, but the mathematics escapes me because contentsRect:'s x and y parameters seem sort of mysterious... But here's some code that may point you in the right direction...
float imageAspect = self.imageView.image.size.width/self.imageView.image.size.height;
float imageViewAspect = self.imageView.frame.size.width/self.imageView.frame.size.height;
if (imageAspect > imageViewAspect) {
float scaledImageWidth = self.imageView.frame.size.height * imageAspect;
float offsetWidth = -((scaledImageWidth-self.imageView.frame.size.width)/2);
self.imageView.layer.contentsRect = CGRectMake(offsetWidth/self.imageView.frame.size.width, 0.0, 1.0, 1.0);
} else if (imageAspect < imageViewAspect) {
float scaledImageHeight = self.imageView.frame.size.width * imageAspect;
float offsetHeight = ((scaledImageHeight-self.imageView.frame.size.height)/2);
self.imageView.layer.contentsRect = CGRectMake(0.0, offsetHeight/self.imageView.frame.size.height, 1.0, 1.0);
}
Try something like this:
CGRect cropRect = CGRectMake(0,0,200,200);
CGImageRef imageRef = CGImageCreateWithImageInRect([ImageToCrop CGImage],cropRect);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
I found a very good approximation on this answer. In that, the category resize the image, and use the center point to crop after that. I adapt it to crop using (0,0) as origin point. As I don't really need a category, I use it as a single method.
- (UIImage *)imageByScalingAndCropping:(UIImage *)image forSize:(CGSize)targetSize {
UIImage *sourceImage = image;
UIImage *newImage = nil;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetSize.width;
CGFloat scaledHeight = targetSize.height;
if (CGSizeEqualToSize(image.size, targetSize) == NO) {
if ((targetSize.width / image.size.width) > (targetSize.height / image.size.height)) {
scaleFactor = targetSize.width / image.size.width; // scale to fit height
} else {
scaleFactor = targetSize.height / image.size.height; // scale to fit width
}
scaledWidth = image.size.width * scaleFactor;
scaledHeight = image.size.height * scaleFactor;
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = CGPointZero;
thumbnailRect.size.width = scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil) {
NSLog(#"could not scale image");
}
//pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
And my call is something like this:
self.viewIm = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 120, 80)];
self.viewIm.image = [self imageByScalingAndCropping:[UIImage imageNamed:#"benjen"] forSize:CGSizeMake(120, 80)];
[self.view addSubview:self.viewIm];
I've spent some time on this and finally created a Swift 3.2 solution (based on one of my answers on another thread, as well as one of the answers above). This code only allows for Y translation of the image, but with some tweaks anyone should be able to add horizontal translation as well ;)
let yOffset: CGFloat = 20
myImageView.contentMode = .scaleAspectFill
//scale image to fit the imageView's width (maintaining aspect ratio), but allow control over the image's Y position
UIGraphicsBeginImageContextWithOptions(myImageView.frame.size, myImageView.isOpaque, 0.0)
let ratio = myImage.size.width / myImage.size.height
let newHeight = myImageView.frame.width / ratio
let rect = CGRect(x: 0, y: -yOffset, width: myImageView.frame.width, height: newHeight)
myImage.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext() ?? myImage
UIGraphicsEndImageContext()
//set the new image
myImageView.image = newImage
Now you can adjust how far down or up you need the image to be by changing the yOffset.

iOS cropping image taken with camera

I am using the camera (iOS 7, iPad mini) to take a picture and crop it to a square. I am pretty content with the default cropping functionality I get from the following code:
imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.delegate = self;
imagePickerController.mediaTypes = #[(NSString *) kUTTypeImage];
imagePickerController.allowsEditing = YES;
UIImage *image = info[UIImagePickerControllerEditedImage]
It gives me a squared overlay over the image I took. If I zoom, I can pan the image a little. However I am surprised I can not move the image up and down (without zoom) to move the crop rectangle over the Image. If I try to do so, it just bounces back to the original location. What am I missing? Or is this functionality just missing?
From the many questions here, most are answered with a disappointing 'It has to be done in a custom way'.
Here's the easiest way to do it (without reimplementing UIImagePickerController). First, use an overlay to make the camera field look square. Here's an example for 3.5" screens (you'd need to update it to work for iPhone 5):
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = source;
if (source == UIImagePickerControllerSourceTypeCamera) {
//Create camera overlay
CGRect f = imagePickerController.view.bounds;
f.size.height -= imagePickerController.navigationBar.bounds.size.height;
CGFloat barHeight = (f.size.height - f.size.width) / 2;
UIGraphicsBeginImageContext(f.size);
[[UIColor colorWithWhite:0 alpha:.5] set];
UIRectFillUsingBlendMode(CGRectMake(0, 0, f.size.width, barHeight), kCGBlendModeNormal);
UIRectFillUsingBlendMode(CGRectMake(0, f.size.height - barHeight, f.size.width, barHeight), kCGBlendModeNormal);
UIImage *overlayImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *overlayIV = [[UIImageView alloc] initWithFrame:f];
overlayIV.image = overlayImage;
[imagePickerController.cameraOverlayView addSubview:overlayIV];
}
imagePickerController.delegate = self;
[self presentViewController:imagePickerController animated:YES completion:nil];
Then, after you get a picture back from the UIImagePickerController, crop it to a square with something like this:
//Crop the image to a square
CGSize imageSize = image.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
if (width != height) {
CGFloat newDimension = MIN(width, height);
CGFloat widthOffset = (width - newDimension) / 2;
CGFloat heightOffset = (height - newDimension) / 2;
UIGraphicsBeginImageContextWithOptions(CGSizeMake(newDimension, newDimension), NO, 0.);
[image drawAtPoint:CGPointMake(-widthOffset, -heightOffset)
blendMode:kCGBlendModeCopy
alpha:1.];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
This is Objective c Code.

When using avfoundation - crop visible portion of captured image represented in display layer

I am using PBJVision library to capture images.
Under the hood it is using AVFoundation.
I set up the camera to use the following options
PBJVision *camera = [[PBJVision alloc] init];
self.camera = camera;
self.camera.delegate = self;
[self.camera setCameraMode:PBJCameraModePhoto];
[self.camera setCameraDevice:PBJCameraDeviceFront];
[self.camera setCameraOrientation:PBJCameraOrientationPortrait];
[self.camera setFocusMode:PBJFocusModeAutoFocus];
[self.camera setPresentationFrame:self.previewView.frame];
[self.camera previewLayer].frame = self.previewView.bounds;
[self.camera previewLayer].videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.previewView.layer addSublayer:[self.camera previewLayer]];
because the preview layer gravity is set as AVLayerVideoGravityResizeAspectFill the captured image isn't identical to the previewed image.
How can I crop it according to the video gravity?
Based on Erica Sadun's excellent Cookbook, adding the code below to your view controller will allow you to do
UIImage *newImage = [self.applyAspectFillImage: image InRect: self.previewView.bounds];
You can obtain the maximum image size by using the smaller edge (width or height) of the original photo image to size your destination rectangle.
CGFloat scaleW = image.size.width / previewView.bounds.size.width;
CGRect destRect = CGRectMake(0, 0, image.size.width, preview.bounds.size.height * scaleW);
UIImage *newImage = [self.applyAspectFillImage: image InRect: destRect];
The code:
CGRect CGRectCenteredInRect(CGRect rect, CGRect mainRect)
{
CGFloat xOffset = CGRectGetMidX(mainRect)-CGRectGetMidX(rect);
CGFloat yOffset = CGRectGetMidY(mainRect)-CGRectGetMidY(rect);
return CGRectOffset(rect, xOffset, yOffset);
}
// Calculate the destination scale for filling
CGFloat CGAspectScaleFill(CGSize sourceSize, CGRect destRect)
{
CGSize destSize = destRect.size;
CGFloat scaleW = destSize.width / sourceSize.width;
CGFloat scaleH = destSize.height / sourceSize.height;
return MAX(scaleW, scaleH);
}
CGRect CGRectAspectFillRect(CGSize sourceSize, CGRect destRect)
{
CGSize destSize = destRect.size;
CGFloat destScale = CGAspectScaleFill(sourceSize, destRect);
CGFloat newWidth = sourceSize.width * destScale;
CGFloat newHeight = sourceSize.height * destScale;
CGFloat dWidth = ((destSize.width - newWidth) / 2.0f);
CGFloat dHeight = ((destSize.height - newHeight) / 2.0f);
CGRect rect = CGRectMake (dWidth, dHeight, newWidth, newHeight);
return rect;
}
- (UIImage *) applyAspectFillImage: (UIImage *) image InRect: (CGRect) bounds
{
CGRect destRect;
UIGraphicsBeginImageContext(bounds.size);
CGRect rect = CGRectAspectFillRect(image.size, bounds);
destRect = CGRectCenteredInRect(rect, bounds);
[image drawInRect: destRect];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}

Image cropping issue in iOS

I am creating an iPhone app which has image cropping feature. In this, I am getting the photos from the UIImagePickerController and passing it for cropping. There it has a scrollview and the selected image will be added as a subview to the scrollview. And I am using a UIButton for selecting the area for cropping. User can move the button over the imageview and place it anywhere, and when click on CROP button, the area similar to the frame size of the button should be cropped from the imageview.
I used the following code, but it is not returning the actual image.
CGRect clippedRect = CGRectMake(self.scrollView.frame.origin.x+90, self.scrollView.frame.origin.y, self.scrollView.frame.size.width-180, self.scrollView.frame.size.height-220);
CGImageRef imageRef = CGImageCreateWithImageInRect([self.myPhoto CGImage], clippedRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
self.imageView.image = newImage;
also used
- (UIImage *)cropImage:(UIImage *)oldImage {
CGSize imageSize = self.cropFrame.frame.size;
UIGraphicsBeginImageContextWithOptions( CGSizeMake( imageSize.width, imageSize.height), NO, 0.);
[oldImage drawAtPoint:CGPointMake( xPosition, yPosition)
blendMode:kCGBlendModeCopy
alpha:1.];
UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return croppedImage;
}
but the result image is not the exact image as per the button frame. I am getting the image from another area.
Updated code
- (void)loadPhoto{
CGFloat w = self.myPhoto.size.width;
CGFloat h = self.myPhoto.size.height;
CGRect imageViewFrame = CGRectMake(0.0f, 0.0f, roundf(w / 2.0f), roundf(h / 2.0f));
self.scrollView.contentSize = imageViewFrame.size;
UIImageView *iv = [[UIImageView alloc] initWithFrame:imageViewFrame];
iv.contentMode = UIViewContentModeScaleAspectFit;
iv.image = self.myPhoto;
[self.view addSubview:iv];
self.imageView = iv;
[iv release];
}
CGRect crop;//= CGRectMake(10, 10, 360, 360);
crop.origin.x = self.cropFrame.frame.origin.x;
crop.origin.y = self.cropFrame.frame.origin.y;
crop.size.width = roundf(self.cropFrame.frame.size.width * 2.0f); //self.cropFrame.frame.size.width * 2;
crop.size.height = roundf(self.cropFrame.frame.size.height * 2.0f); //self.cropFrame.frame.size.height * 2;
NSLog(#"Rect: %#", NSStringFromCGRect(crop));
self.imageView.image = [self croppedImage:crop];
- (UIImage *)croppedImage:(CGRect)bounds {
CGImageRef imageRef = CGImageCreateWithImageInRect([self.imageView.image CGImage], bounds);
UIImage *croppedImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:self.myPhoto.imageOrientation];
CGImageRelease(imageRef);
return croppedImage;
}
Please help to find a solution.
The iOS has a default feature for cropping images.Try this code.
picker.allowsEditing = YES;
and also check this controller for cropping..this is exactly the one you are looking for I think https://github.com/barrettj/BJImageCropper .Hope this helps you..
Since you are using a scrollView that allows the image to be scrolled, you need to adjust your crop rect to the scrollView's position:
float zoomScale = self.scrollView.zoomScale;
int cropX = (self.scrollView.contentOffset.x-imageView.frame.origin.x)/zoomScale;
int cropY = (self.scrollView.contentOffset.y-imageView.frame.origin.y)/zoomScale;
You could use this crop tool that I made. It essentially gives you an interface to allow the user to select the crop area. I think it is in line with that you are looking for.
https://github.com/nicholjs/BFCropInterface
Believing you have solve this problem. Me too had this when tried cropping functionality
Set image.size as the imageView.size & scrollView.contentSize. Below code will give the rect to crop
cropRect.origin = scrollView.contentOffset;
cropRect.size = scrollView.bounds.size;
cropRect.origin.x /= scrollView.zoomScale;
cropRect.origin.y /= scrollView.zoomScale;
cropRect.size.width /= scrollView.zoomScale;
cropRect.size.height /= scrollView.zoomScale;
If planning to show the full image first on visible rect. Setting the imageView.size & scrollView.contentSize to visible view size will give crop image of some other area. Instead try finding the zoom scale by
CGFloat dxWidth = viewCrop.frame.size.width / imageView.image.size.width;
CGFloat dxHeight = viewCrop.frame.size.height / imageView.image.size.height;
CGFloat zoomScale = fmaxf(dWidth, dHeight)
and apply (if by adding subView then after addSubView)
scrollView.minimumZoomScale = zoomScale; // to disable further zoom-out
[scrollView setZoomScale: zoomScale];

Resources