UIImagePickerController Edited Image - Convert NSRect to CGRect in Swift 2 - ios

I'm trying to crop an image that has been selected and edited through the UIImagePicker. For some reasons the picker doesn't return a UIImagePickerControllerEditedImage value. So what I'm trying to do is to get the original value and crop it according to the rectangle in UIImagePickerControllerCropRect but that's a NSRect and I need to convert it to a CRRect to crop it. This is my image picker controller function :
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
//Cut photo or show subview
let chosenImage : UIImage
print(editingInfo)
if let possibleImage = editingInfo!["UIImagePickerControllerEditedImage"] as? UIImage {
print("Edited image")
chosenImage = possibleImage
} else if let possibleImage = editingInfo!["UIImagePickerControllerOriginalImage"] as? UIImage {
print("Non edited image")
if let rectangle = editingInfo!["UIImagePickerControllerCropRect"] as? [[Int]] {
print(rectangle[0])
}
let rect: CGRect = CGRectMake(0, 230, 750, 750)
let imageRef: CGImageRef = CGImageCreateWithImageInRect(possibleImage.CGImage, rect)!
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
chosenImage = image
} else {
return
}
self.profilePicture.image = chosenImage.rounded?.circle
dismissViewControllerAnimated(true) { () -> Void in
//update the server
}
}
Everything else works fine except it crops the image according to the rectangle in CGRectMake(0, 230, 750, 750) I'd like to make that instead of fixed values the values of UIImagePickerControllerCropRect . Please let me know if you have any suggestion on how to get that.
Thank you!

var rect: CGRect = CGRectMake(0, 230, 750, 750)
if let rectangle = editingInfo!["UIImagePickerControllerCropRect"] as? NSValue {
rect = rectangle.CGRectValue()
}
let imageRef: CGImageRef = CGImageCreateWithImageInRect(possibleImage.CGImage, rect)!
You used wrong variable I suppose

Related

CGImageDestinationCreateWithData doubles pixel dimension

If I create a UIImage 'image' from a JPEG file selection in iOS using
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let info = convertFromUIImagePickerControllerInfoKeyDictionary(info)
if let image = info[convertFromUIImagePickerControllerInfoKey(UIImagePickerController.InfoKey.originalImage)] as? UIImage {
}
}
And then create a data object from image using
imageData = image.jpegData(compressionQuality:1.0)
and then add some properties using
func addImageProperties(imageData: Data, properties: CFDictionary) -> Data {
if let source = CGImageSourceCreateWithData(imageData as CFData, nil) {
if let uti = CGImageSourceGetType(source) {
let destinationData = NSMutableData()
if let destination = CGImageDestinationCreateWithData(destinationData, uti, 1, nil) {
CGImageDestinationAddImageFromSource(destination, source, 0, properties)
if CGImageDestinationFinalize(destination) == false {
return imageData // return input data if error
}
let destinationDataResult = destinationData as Data
return destinationDataResult
}
}
}
return imageData // return input data if error
}
and then save the data to a file using
func saveImageDataAsImage(_ data: Data) {
var newImageIdentifier: String!
PHPhotoLibrary.shared().performChanges{
let assetRequest = PHAssetCreationRequest.forAsset()
assetRequest.addResource(with: .photo, data: data, options: nil)
newImageIdentifier = assetRequest.placeholderForCreatedAsset!.localIdentifier
} completionHandler: { (success, error) in
DispatchQueue.main.async(execute: {
if success, let newAsset = PHAsset.fetchAssets(withLocalIdentifiers: [newImageIdentifier], options: nil).firstObject {
// ...
} else {
// ...
}
})
}
}
The resulting file has twice the width and height in pixels of the original selected file. How can I retain the original file width and height while adding the required metadata?
The loaded image (with UIImage.scale = 1.0) was being processed before saved in the following code, which reset the UIImage.scale value to 2.0. Code to reset UIImage.scale after this it did not change the image.cgImage size, but changing the line let scale = UIScreen.main.scale to let scale:CGFloat = 1.0 in the code resulted in the saved image having the same pixel dimensions as the input image.
func textToImage(drawText: String, inImage: UIImage, atPoint: CGPoint, textColor: UIColor, textFont: UIFont, backColor: UIColor) -> UIImage{
// Setup the font specific variables
//var textColor = UIColor.white
//var textFont = UIFont(name: "Helvetica Bold", size: 12)!
// Setup the image context using the passed image
let scale = UIScreen.main.scale// change to let scale:CGFloat = 1.0 to fix problem
UIGraphicsBeginImageContextWithOptions(inImage.size, false, scale)
// Setup the font attributes that will be later used to dictate how the text should be drawn
let textFontAttributes = [
NSAttributedString.Key.font: textFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.backgroundColor: backColor,
]
// Put the image into a rectangle as large as the original image
inImage.draw(in: CGRect(x: 0, y: 0, width: inImage.size.width, height: inImage.size.height))
// Create a point within the space that is as bit as the image
var rect = CGRect(x: atPoint.x, y: atPoint.y, width: inImage.size.width, height: inImage.size.height)
// Draw the text into an image
drawText.draw(in: rect, withAttributes: textFontAttributes)
// Create a new image out of the images we have created
var newImage = UIGraphicsGetImageFromCurrentImageContext()
// End the context now that we have the image we need
UIGraphicsEndImageContext()
//Pass the image back up to the caller
return newImage!
}

Rotate image in share extension

I have this extension and it works perfect in app target but crash in share extension when trying to rotate image captured on camera. How to rotate image in share extension? Or maybe it possible to load image from Photo Library already in right orientation.
extension UIImage {
func fixOrientation() -> UIImage {
switch imageOrientation {
case .up:
return self
default:
UIGraphicsBeginImageContextWithOptions(size, false, scale)
draw(in: CGRect(origin: .zero, size: size)) //Thread 1: EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=120 MB, unused=0x0)
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result!
}
}
}
Crash screenshots:
First of all it is clear that you have a memory crash. According to App Extension Programming Guide:
Memory limits for running app extensions are significantly lower than the memory limits imposed on a foreground app. On both platforms, the system may aggressively terminate extensions because users want to return to their main goal in the host app.
And from error it is clear that you exceed 120 mb. But you might wonder what is took so much memory.
According to Optimizing Images
Written by Jordan Morgan:
iOS essentially derives its memory hit from an image’s dimensions - whereas the actual file size has much less to do with it.
So if we calculate size or 4032 x 3024 photo it will be... 46mb for 4 bit color and 79mb for 8 bit color. Pretty big, but still less that a limit...
Thing is - you have two copies of your image. One is original and second one - rotated.
To solve this issue you need load only rotated image into memory, without original. This can be done with Image I/O Framework:
extension UIImage {
static func imageWithFixedOrientation(at url: URL) -> UIImage? {
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else { return nil }
guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? Dictionary<CFString, Any> else { return nil }
guard
let width = imageProperties[kCGImagePropertyPixelWidth] as? CGFloat,
let height = imageProperties[kCGImagePropertyPixelHeight] as? CGFloat
else { return nil }
let options: [NSString: Any] = [
kCGImageSourceThumbnailMaxPixelSize: max(width, height),
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceCreateThumbnailWithTransform: true
]
guard let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options as CFDictionary) else { return nil }
return UIImage(cgImage: cgImage)
}
}
In sample app:
extension ViewController: UIImagePickerControllerDelegate & UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let url = info[.imageURL] as? URL else { return }
let image = UIImage.imageWithFixedOrientation(at: url)
}
}
it reduced memory peaks from 180+mb to just 80mb.
You may try to use more optimized UIGraphicsImageRendererFormat instead of using old UIGraphicsBeginImageContextWithOptions, you can find more information in this post. So your code will look something like below:
func fixOrientation() -> UIImage {
let format = UIGraphicsImageRendererFormat()
format.scale = scale
format.prefersExtendedRange = true
let renderer = UIGraphicsImageRenderer(size: size, format: format)
let image = renderer.image(actions: { context in
var workSize = size;
workSize.width = floor(workSize.width / scale)
workSize.height = floor(workSize.height / scale)
// if the orientation is already correct
// if image.imageOrientation == .up { draw image }
var transform = CGAffineTransform.identity
//TO-DO - set transform depends on current image orientation
//transform =
let ctx = context.cgContext
ctx.concatenate(transform)
guard let cgImageCopy = cgImage else {
return
}
switch imageOrientation {
case .left, .leftMirrored, .right, .rightMirrored:
ctx.draw(cgImageCopy, in: CGRect(x: 0.0, y:0.0, width: workSize.height, height: workSize.width))
break;
default:
ctx.draw(cgImageCopy, in: CGRect(origin: .zero, size: workSize))
break;
}
})
return image
}
Did you try using the function you provided within an didFinishPicking delegate method?
func photoLibrary() {
if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
let myPickerController = UIImagePickerController()
myPickerController.delegate = self
myPickerController.sourceType = .photoLibrary
self.present(myPickerController, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
picker.dismiss(animated: true)
guard let image = info[.originalImage] as? UIImage else {
print("No image found")
return
}
// Flip the image here
self.mainImageView.image = image.fixOrientation()
}

How do I create a croppable banner image in uiimagepickercontroller?

When I present a UIImagePickerController, I want the user to be able to crop a 7.8 ratio for the banner that the user wants to import from their photo library.
This question is similar to many questions relating to creating custom crop rects built to work with UIImagePickerController but all the answers point to outdated libraries or libraries with too much complexity. I want something simple.
Try this:
open func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
var newImage: UIImage
if picker.sourceType == .camera {
if let possibleImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
// save photo
}
}
if UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.pad && picker.sourceType == .photoLibrary {
if let possibleImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
newImage = possibleImage
}
} else {
if let possibleImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
newImage = possibleImage
} else if let possibleImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
newImage = possibleImage
}
}
// logic to manipulate image
print("image size: \(newImage.size)")
let newHeight = 100 // change to preferred height
let scale = 7.8
let newWidth = newImage.size.width
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
newImage.draw(in: CGRect(x: 0, y: 0,width: newWidth, height: newHeight))
newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
print("image size: \(newImage.size)")
// add logic here
}

SWIFT 3 - CGImage to PNG

I am trying to use a color mask to make a color in JPG image transparent because as I read, color mask only works with JPG.
This code work when I apply the color mask and save the image as a JPG, but as a JPG, there is no transparency so I want to transform the JPG image to a PNG image to keep the transparency but when I try to do it, the color mask doesn't work.
Am I doing something wrong or maybe this isn't the right approach.
Here is the code of the 2 functions :
func callChangeColorByTransparent(_ sender: UIButton){
var colorMasking: [CGFloat] = []
if let textLabel = sender.titleLabel?.text {
switch textLabel {
case "Remove Black":
colorMasking = [0,30,0,30,0,30]
break
case "Remove Red":
colorMasking = [180,255,0,50,0,60]
break
default:
colorMasking = [222,255,222,255,222,255]
}
}
print(colorMasking)
let newImage = changeColorByTransparent(selectedImage, colorMasking: colorMasking)
symbolImageView.image = newImage
}
func changeColorByTransparent(_ image : UIImage, colorMasking : [CGFloat]) -> UIImage {
let rawImage: CGImage = (image.cgImage)!
//let colorMasking: [CGFloat] = [222,255,222,255,222,255]
UIGraphicsBeginImageContext(image.size)
let maskedImageRef: CGImage = rawImage.copy(maskingColorComponents: colorMasking)!
if let context = UIGraphicsGetCurrentContext() {
context.draw(maskedImageRef, in: CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height))
var newImage = UIImage(cgImage: maskedImageRef, scale: image.scale, orientation: image.imageOrientation)
UIGraphicsEndImageContext()
var pngImage = UIImage(data: UIImagePNGRepresentation(newImage)!, scale: 1.0)
return pngImage!
}
print("fail")
return image
}
Thank for your help.
Thanks the issue of DonMag in my other question SWIFT 3 - CGImage copy always nil, here is the code to solve this :
func saveImageWithAlpha(theImage: UIImage, destFile: URL) -> Void {
// odd but works... solution to image not saving with proper alpha channel
UIGraphicsBeginImageContext(theImage.size)
theImage.draw(at: CGPoint.zero)
let saveImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let img = saveImage, let data = UIImagePNGRepresentation(img) {
try? data.write(to: destFile)
}
}

CIDetector , detected face image is not showing?

I am using CIDetector to detect face in a UIImage. i am getting the face rect correctly but when i crop the image to detected face rect. it is not showing on my image view.
I have already checked. my image is not nil
Here is my code :-
#IBAction func detectFaceOnImageView(_: UIButton) {
let image = myImageView.getFaceImage()
myImageView.image = image
}
extension UIView {
func getFaceImage() -> UIImage? {
let faceDetectorOptions: [String: AnyObject] = [CIDetectorAccuracy: CIDetectorAccuracyHigh as AnyObject]
let faceDetector: CIDetector = CIDetector(ofType: CIDetectorTypeFace, context: nil, options: faceDetectorOptions)!
let viewScreenShotImage = generateScreenShot(scaleTo: 1.0)
if viewScreenShotImage.cgImage != nil {
let sourceImage = CIImage(cgImage: viewScreenShotImage.cgImage!)
let features = faceDetector.features(in: sourceImage)
if features.count > 0 {
var faceBounds = CGRect.zero
var faceImage: UIImage?
for feature in features as! [CIFaceFeature] {
faceBounds = feature.bounds
let faceCroped: CIImage = sourceImage.cropping(to: faceBounds)
faceImage = UIImage(ciImage: faceCroped)
}
return faceImage
} else {
return nil
}
} else {
return nil
}
}
func generateScreenShot(scaleTo: CGFloat = 3.0) -> UIImage {
let rect = self.bounds
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
self.layer.render(in: context!)
let screenShotImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
let aspectRatio = screenShotImage.size.width / screenShotImage.size.height
let resizedScreenShotImage = screenShotImage.scaleImage(toSize: CGSize(width: self.bounds.size.height * aspectRatio * scaleTo, height: self.bounds.size.height * scaleTo))
return resizedScreenShotImage!
}
}
For More Information, I am attaching Screen Shots of values .
Screen Shot 1
Screen Shot 2
Screen Shot 3
Try this:
let faceCroped: CIImage = sourceImage.cropping(to: faceBounds)
//faceImage = UIImage(ciImage: faceCroped)
let cgImage: CGImage = {
let context = CIContext(options: nil)
return context.createCGImage(faceCroped, from: faceCroped.extent)!
}()
faceImage = UIImage(cgImage: cgImage)

Resources