Crop, Rotate and Resize are the fundamental image operations. In app development we come to a situation when we have to do these operations. In this post, I will guide you how to use these operations in your iOS applications using XCode 8.0 and Swift 3.0.
Image Cropping:
Image cropping is a process of extracting some interested portion of an image. In this post we will create following method to crop an image:
func crop(image:UIImage, cropRect:CGRect) -> UIImage? { }
In this method `image` parameter is source image to be cropped and `cropRect` is a rectangle in which image will be cropped. This method will return UIImage object of cropped image.
First we will start from creating an image context for drawing environment
UIGraphicsBeginImageContextWithOptions(cropRect.size, false, image.scale)
Here we passed the size of resulting image as first parameter, second parameter indicates that the image is not opaque and third parameter specifies the image scale.
Now we will create a point which will translate the source image according to the origin of given cropping rectangle.
let origin = CGPoint(x: cropRect.origin.x * CGFloat(-1), y: cropRect.origin.y * CGFloat(-1))
Now we will draw the source image into current context at the translated point
image.draw(at: origin)
Now we will get the resulted image, close the image context and return the resulting UIImage object.
let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext(); return result
So the final method for image cropping will be:
func crop(image:UIImage, cropRect:CGRect) -> UIImage? { UIGraphicsBeginImageContextWithOptions(cropRect.size, false, image.scale) let origin = CGPoint(x: cropRect.origin.x * CGFloat(-1), y: cropRect.origin.y * CGFloat(-1)) image.draw(at: origin) let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext(); return result }
Image Rotation:
In image rotation we will use transformation matrix to rotate an image. In this example we will do three type of rotations: rotating an image by some degree(θ), flip the image vertically and flip the image horizontally. For these rotations we will use 3D rotation matrix.
To rotate an image by some degree(θ) we will rotate the image by θ degree w.r.t. Z-Axis. For vertical flip we will rotate the the image by 180 degree w.r.t. Y-Axis and for horizontal flip we will rotate the image by 180 degree w.r.t. X-Axis.
The 3D rotation matrix for rotation is following for different axises:
Now we know that what will be our procedure to rotate and flip an image, So let’s start by defining a method for rotation.
func rotateImage(image:UIImage, angle:CGFloat, flipVertical:CGFloat, flipHorizontal:CGFloat) -> UIImage? { }
Here, `image` will be source image to be rotated, `angle` will be the rotation angle in radian, `flipVertical` will indicate whether the image will be flipped vertically or not by setting its value to 1 or 0 and `flipHorizontal` will indicate whether the image will be flipped horizontally or not by setting its value to 1 or 0. This method will return the UIImage object of rotated image.
To convert from degree to radian use following formula:
angleInRadian = angleInDegree * M_PI / 180
Now we will create a CIImage object of given source image and create a Affine Transform filter using CIFilter and set the CIImage object as input to the filter.
let ciImage = CIImage(image: image) let filter = CIFilter(name: "CIAffineTransform") filter?.setValue(ciImage, forKey: kCIInputImageKey) filter?.setDefaults()
By default Affine Transform rotate the image in anti-clockwise direction, so for clockwise rotation we have to multiply the angle by `-1`.
Now we will create a 3D identity transformation matrix.
let newAngle = angle * CGFloat(-1) var transform = CATransform3DIdentity
Apply rotation about Z-Axis by given angle:
transform = CATransform3DRotate(transform, CGFloat(newAngle), 0, 0, 1)
Apply rotation about Y-Axis by radian (180 degrees) if flipVertical is 1.
transform = CATransform3DRotate(transform, CGFloat(Double(flipVertical) * M_PI), 0, 1, 0)
Apply rotation about X-Axis by radian (180 degrees) if flipHorizontal is 1.
transform = CATransform3DRotate(transform, CGFloat(Double(flipHorizontal) * M_PI), 1, 0, 0)
Now create an Affine transform object from above Transformation matrix and set it to the filter created earlier.
let affineTransform = CATransform3DGetAffineTransform(transform) filter?.setValue(NSValue(cgAffineTransform: affineTransform), forKey: "inputTransform")
Now get the rotated image and create a CIContext object for creating new image with extent of rotated image and convert it into UIImage and return.
let contex = CIContext(options: [kCIContextUseSoftwareRenderer:true]) let outputImage = filter?.outputImage let cgImage = contex.createCGImage(outputImage!, from: (outputImage?.extent)!) let result = UIImage(cgImage: cgImage!) return result
So the complete method for image rotation and flip will be following:
func rotateImage(image:UIImage, angle:CGFloat, flipVertical:CGFloat, flipHorizontal:CGFloat) -> UIImage? { let ciImage = CIImage(image: image) let filter = CIFilter(name: "CIAffineTransform") filter?.setValue(ciImage, forKey: kCIInputImageKey) filter?.setDefaults() let newAngle = angle * CGFloat(-1) var transform = CATransform3DIdentity transform = CATransform3DRotate(transform, CGFloat(newAngle), 0, 0, 1) transform = CATransform3DRotate(transform, CGFloat(Double(flipVertical) * M_PI), 0, 1, 0) transform = CATransform3DRotate(transform, CGFloat(Double(flipHorizontal) * M_PI), 1, 0, 0) let affineTransform = CATransform3DGetAffineTransform(transform) filter?.setValue(NSValue(cgAffineTransform: affineTransform), forKey: "inputTransform") let contex = CIContext(options: [kCIContextUseSoftwareRenderer:true]) let outputImage = filter?.outputImage let cgImage = contex.createCGImage(outputImage!, from: (outputImage?.extent)!) let result = UIImage(cgImage: cgImage!) return result }
Image Resizing:
Resizing is very frequently used operation in mobile applications while uploading an image to application server. Due to high resolution camera available on mobile devices we resize the image before uploading them to our application server.
So let’s define a method for image resizing:
func resizeImage(image:UIImage, targetSize:CGSize) -> UIImage? { }
Here, `image` is the source image to be resized and `targetSize` is a new size in which source image has to be resized. It returns an object of UIImage.
Calculate the width and height ratio of new image and derive a new size based on the minimum ratio and also make an rectangle using this new size.
let originalSize = image.size let widthRatio = targetSize.width / originalSize.width let heightRatio = targetSize.height / originalSize.height let ratio = min(widthRatio, heightRatio) let newSize = CGSize(width: originalSize.width * ratio, height: originalSize.height * ratio) // preparing rect for new image size let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height)
Now we will create an image context for drawing environment
UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale)
Here we passed the size of resulting image as first parameter, second parameter indicates that the image is not opaque and third parameter specifies the image scale.
Now we will draw the source image into the new rectangle
image.draw(in: rect)
Now we will get the resulted image, close the image context and return the resulting UIImage object.
let result = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext(); return result
So the final method for Image resizing will be:
func resizeImage(image:UIImage, targetSize:CGSize) -> UIImage? { let originalSize = image.size let widthRatio = targetSize.width / originalSize.width let heightRatio = targetSize.height / originalSize.height let ratio = min(widthRatio, heightRatio) let newSize = CGSize(width: originalSize.width * ratio, height: originalSize.height * ratio) // preparing rect for new image size let rect = CGRect(x: 0, y: 0, width: newSize.width, height: newSize.height) // Actually do the resizing to the rect using the ImageContext stuff UIGraphicsBeginImageContextWithOptions(newSize, false, UIScreen.main.scale) image.draw(in: rect) let newImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return newImage }