crop UIImage to the shape of a rectangular overlay - Swift - ios

I am pretty new to Swift.
I intend to draw a rectangle and capture the image in the rectangular overlay.
I drew a transparent image over the camera.
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.Camera
imagePicker.mediaTypes = [kUTTypeImage as String]
imagePicker.allowsEditing = true
var overlayedImageView: UIImageView = UIImageView(image: UIImage(named: "transparent.png"))
var cgRect: CGRect = CGRect(x: 200, y: 50, width: 100, height: 400)
overlayedImageView.frame = cgRect
imagePicker.cameraOverlayView = overlayedImageView
self.presentViewController(imagePicker, animated: true,
completion: nil)
This is what i do after i capture the image
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let mediaType = info[UIImagePickerControllerMediaType] as! String
self.dismissViewControllerAnimated(true, completion: nil)
if mediaType == (kUTTypeImage as String) {
let image = info[UIImagePickerControllerOriginalImage]
as! UIImage
imageView.image = cropToBoundsNew(image)
if (newMedia == true) {
UIImageWriteToSavedPhotosAlbum(image, self,
"image:didFinishSavingWithError:contextInfo:", nil)
} else if mediaType == (kUTTypeMovie as String) {
// Code to support video here
}
}
}
My Problem is the quality of the image diminishes
func cropToBoundsNew(image: UIImage) -> UIImage {
let contextImage: UIImage = UIImage(CGImage: image.CGImage!)
let contextSize: CGSize = contextImage.size
let rect: CGRect = CGRectMake(200, 50, 100, 400)
let imageRef: CGImageRef = CGImageCreateWithImageInRect(contextImage.CGImage, rect)!
let image: UIImage = UIImage(CGImage: imageRef, scale: image.scale, orientation: image.imageOrientation)
return image
}

Related

Using Vision to scan images from photo library

Is there a way that I can use the Vision framework to scan an existing image from the user's photo library? As in, not taking a new picture using the camera, but just choosing an image that the user already has?
Yes, you can. Adding on to #Zulqarnayn's answer, here's a working example to detect and draw a bounding box on rectangles.
1. Set up the image view where the image will be displayed
#IBOutlet weak var imageView: UIImageView!
#IBAction func pickImage(_ sender: Any) {
let picker = UIImagePickerController()
picker.delegate = self
self.present(picker, animated: true)
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.borderWidth = 4
imageView.layer.borderColor = UIColor.blue.cgColor
imageView.contentMode = .scaleAspectFill
imageView.backgroundColor = UIColor.green.withAlphaComponent(0.3)
imageView.layer.masksToBounds = false /// allow image to overflow, for testing purposes
}
2. Get the image from the image picker
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else { return }
/// set the imageView's image
imageView.image = image
/// start the request & request handler
detectCard()
/// dismiss the picker
dismiss(animated: true)
}
}
3. Start the vision request
func detectCard() {
guard let cgImage = imageView.image?.cgImage else { return }
/// perform on background thread, so the main screen is not frozen
DispatchQueue.global(qos: .userInitiated).async {
let request = VNDetectRectanglesRequest { request, error in
/// this function will be called when the Vision request finishes
self.handleDetectedRectangle(request: request, error: error)
}
request.minimumAspectRatio = 0.0
request.maximumAspectRatio = 1.0
request.maximumObservations = 1 /// only look for 1 rectangle
let imageRequestHandler = VNImageRequestHandler(cgImage: cgImage, orientation: .up)
do {
try imageRequestHandler.perform([request])
} catch let error {
print("Error: \(error)")
}
}
}
4. Get the result from the Vision request
func handleDetectedRectangle(request: VNRequest?, error: Error?) {
if let results = request?.results {
if let observation = results.first as? VNRectangleObservation {
/// get back to the main thread
DispatchQueue.main.async {
guard let image = self.imageView.image else { return }
let convertedRect = self.getConvertedRect(
boundingBox: observation.boundingBox,
inImage: image.size,
containedIn: self.imageView.bounds.size
)
self.drawBoundingBox(rect: convertedRect)
}
}
}
}
5. Convert observation.boundingBox to the UIKit coordinates of the image view, then draw a border around the detected rectangle
I explain this more in detail in this answer.
func getConvertedRect(boundingBox: CGRect, inImage imageSize: CGSize, containedIn containerSize: CGSize) -> CGRect {
let rectOfImage: CGRect
let imageAspect = imageSize.width / imageSize.height
let containerAspect = containerSize.width / containerSize.height
if imageAspect > containerAspect { /// image extends left and right
let newImageWidth = containerSize.height * imageAspect /// the width of the overflowing image
let newX = -(newImageWidth - containerSize.width) / 2
rectOfImage = CGRect(x: newX, y: 0, width: newImageWidth, height: containerSize.height)
} else { /// image extends top and bottom
let newImageHeight = containerSize.width * (1 / imageAspect) /// the width of the overflowing image
let newY = -(newImageHeight - containerSize.height) / 2
rectOfImage = CGRect(x: 0, y: newY, width: containerSize.width, height: newImageHeight)
}
let newOriginBoundingBox = CGRect(
x: boundingBox.origin.x,
y: 1 - boundingBox.origin.y - boundingBox.height,
width: boundingBox.width,
height: boundingBox.height
)
var convertedRect = VNImageRectForNormalizedRect(newOriginBoundingBox, Int(rectOfImage.width), Int(rectOfImage.height))
/// add the margins
convertedRect.origin.x += rectOfImage.origin.x
convertedRect.origin.y += rectOfImage.origin.y
return convertedRect
}
/// draw an orange frame around the detected rectangle, on top of the image view
func drawBoundingBox(rect: CGRect) {
let uiView = UIView(frame: rect)
imageView.addSubview(uiView)
uiView.backgroundColor = UIColor.clear
uiView.layer.borderColor = UIColor.orange.cgColor
uiView.layer.borderWidth = 3
}
Result | Demo repo
Input image
Result
Yes, you can. First, take an instance of UIImagePickerController & present it.
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .photoLibrary
present(picker, animated: true, completion: nil)
Then implement the delegate method take the desired image
extension YourViewController: UIImagePickerControllerDelegate {
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[.originalImage] as? UIImage {
## here start your request & request handler
}
picker.dismiss(animated: true, completion: nil)
}
}

Issue with saving image in gallery and displaying

Do consider this as a question from someone who is not so good at swift..:).I have a button on the click of which the imagepicker is opened and I am able to select the images. In the didFinishPickingMediaWithInfo I'm adding the image to array like so...
var imageArray = [UIImage]()
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
imageArray.append(image)
for i in 0..<imageArray.count {
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.imageScrollView.frame.width, height: self.imageScrollView.frame.height)
imageScrollView.contentSize.width = imageScrollView.frame.width * (CGFloat(i + 1))
imageScrollView.addSubview(imageView)
}
}
self.dismiss(animated: true, completion: nil)
}
I'm also having these functions:
func saveImage(image: UIImage, path: String) -> Bool {
let jpgImageData = UIImageJPEGRepresentation(image, 1.0)
do {
try jpgImageData?.write(to: URL(fileURLWithPath: path), options: .atomic)
} catch {
print(error)
}
return (jpgImageData != nil)
}
func getDocumentsURL() -> NSURL {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
return documentsURL as NSURL
}
func fileInDocumentsDirectory(filename: String) -> String {
let fileURL = getDocumentsURL().appendingPathComponent(filename)
return fileURL!.path
}
But my issue is this..I just don't want to show just one image that is picked from the gallery. I want to pick multiple images from the gallery(one at a time), store them in an array and then display them all in a horizontal scrolling format. For this purpose, I'm setting a scrollview to take the images(as given in didFinishPickingMediaWithInfo)
Maybe I have to read the image also. But how that can be done I'm not able to figure out...Please help!
Please see this loop which i have corrected
You are only creating one UIImageView and adding to the scrollview.
Please initialize the UIImageView every time
for i in 0..<imageArray.count {
var imageView = UIImageView() //*** Add this line to your code
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.imageScrollView.frame.width, height: self.imageScrollView.frame.height)
imageScrollView.contentSize.width = imageScrollView.frame.width * (CGFloat(i + 1))
imageScrollView.addSubview(imageView)
}
When ever you update your scrollview with newImages dont forget to remove the old ones.
Use this to save image
var imageArr:[UIImage] = []
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
UIImageWriteToSavedPhotosAlbum(chosenImage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
imageArr.append(chosenImage)
for i in 0..<imageArray.count
{
imageView.image = imageArray[i]
imageView.contentMode = .scaleAspectFit
let xPosition = self.view.frame.width * CGFloat(i)
imageView.frame = CGRect(x: xPosition, y: 0, width: self.imageScrollView.frame.width, height: self.imageScrollView.frame.height)
imageScrollView.contentSize.width = imageScrollView.frame.width * (CGFloat(i + 1))
imageScrollView.addSubview(imageView)
}
}

Image Selected From Image Picker Not Displaying

I'm kind of lost. My Image Picker is working but the image is not displaying in my Image View. I have looked over my code and various solutions and it still is not working. I have set the delegate to self and double checked my methods and it is still not showing the image where it is supposed to.
import UIKit
class AlterProfileViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
view?.backgroundColor = UIColor.white
navigationItem.title = "Profile Settings"
view.addSubview(selectProfileImage)
///Constraints for all views will go here
_ = selectProfileImage.anchor(view.centerYAnchor, left: view.leftAnchor, bottom: nil, right: nil, topConstant: -275, leftConstant: 135, bottomConstant: 0, rightConstant: 0, widthConstant: 100, heightConstant: 100)
// selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width/2
///////////////////////////////////////////////
// Do any additional setup after loading the view.
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
selectProfileImage.layer.cornerRadius = selectProfileImage.frame.size.width / 2;
selectProfileImage.layer.masksToBounds = true
}
//Where all buttons and labels will be added
//will just be a nice looking image view to be next to the profile settings button
lazy var selectProfileImage: UIImageView = {
let selectPicture = UIImageView()
// self.selectProfileImage.layer.cornerRadius = self.selectProfileImage.frame.size.width / 2;
selectPicture.image = UIImage(named: "Paris")
// selectPicture.layer.cornerRadius = selectPicture.frame.size.width / 2;
selectPicture.clipsToBounds = true
selectPicture.translatesAutoresizingMaskIntoConstraints = false
selectPicture.layer.cornerRadius = selectPicture.frame.size.width/2
selectPicture.contentMode = .scaleAspectFill
selectPicture.isUserInteractionEnabled = true
selectPicture.layer.shouldRasterize = true
// will allow you to add a target to an image click
selectPicture.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleSelectProfileImageView)))
selectPicture.layer.masksToBounds = true
return selectPicture
}()
func handleSelectProfileImageView() {
print("123")
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}
// will dispaly info of image selected
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print("info")
var selectedImageFromPicker: UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage{
print((editedImage as AnyObject).size)
selectedImageFromPicker = editedImage
}else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage{
print((originalImage as AnyObject).size)
selectedImageFromPicker = originalImage
}
if let selectedImage = selectedImageFromPicker {
selectProfileImage.image = selectedImage
}
dismiss(animated: true, completion: nil)
}
// will handle the picker being closed/canceled
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("picker canceled")
dismiss(animated: true, completion: nil)
}
///////////////////////////////////////////////////////////////////////////////////
}

ios Swift 3 using Property Observer update Image from UIImagePicker

Hi I'm using IOS swift 3 to let user pick images from library or album.I have an UIImage variable.How can we use property Observer to update the UIImage when user finished pick an Image
Some thing like
var image: UIImage = {
didSet....
}
Currently I'm doing this
func show(image: UIImage) {
imageView.image = image
imageView.isHidden = false
imageView.frame = CGRect(x: 10, y: 10, width: 260, height: 260)
addPhotoLabel.isHidden = true
}
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [String : Any]) {
image = info[UIImagePickerControllerEditedImage] as? UIImage
if let theImage = image {
show(image: theImage)
}
dismiss(animated: true, completion: nil)
}
Thinking of using property Observer to improve the approach.Any help is much appreciate.Thanks!
If you really want to update the image view any time the image property is set, then simply put all of the code in your show method in the didSet block for the image property.
var image: UIImage = {
didSet {
imageView.image = image
imageView.isHidden = false
imageView.frame = CGRect(x: 10, y: 10, width: 260, height: 260)
addPhotoLabel.isHidden = true
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let theImage = info[UIImagePickerControllerEditedImage] as? UIImage {
image = theImage
}
picker.dismiss(animated: true, completion: nil)
}

How to add any custom image in Camera screen while capturing image?

I want to add custom UIImage in Camera Screen while user capture the photo. Also i want that image in foreground of that captured image on the same position the image is added.
Here the demo image that explain what i want.
To work around this i have try
let picker = UIImagePickerController()
picker.sourceType = .Camera
picker.view.addSubView(imageView)
But using this code it will only add image not add image with the capture image
Any help is appreciate.
You can use UIImagePickerController's cameraOverlayView property to add the imageView on top of the camera screen:
let picker = UIImagePickerController()
picker.delegate = self
picker.sourceType = .Camera
picker.cameraOverlayView = imageView
presentViewController(picker, animated: true, completion: nil)
For adding the image to the foreground of the captured photo you can create an extension for UIImage:
extension UIImage {
func imageWithOverlayImage(overlayImage: UIImage) -> UIImage {
let imageRect = CGRectMake(0, 0, self.size.width, self.size.height)
UIGraphicsBeginImageContext(self.size)
self.drawInRect(imageRect)
overlayImage.drawInRect(imageRect)
let resultImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultImage
}
}
Then you can call it from the delegate method of the image picker:
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
finalImage = image.imageWithOverlayImage(overlayImage!)
dismissViewControllerAnimated(true, completion: nil)
}
try putting Overlayview. add custom image to overlayview.
func openCamera(){
let pickercontroller : UIImagePickerController = UIImagePickerController()
pickercontroller.delegate = self
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
pickercontroller.sourceType = UIImagePickerControllerSourceType.Camera
}
else{
}
pickercontroller.editing = false
pickercontroller.showsCameraControls = false
let graphRect:CGRect = CGRectMake(0, 0,pickercontroller.view.frame.size.width-20 ,pickercontroller.view.frame.size.height)
let overlayView : UIView = UIView(frame: graphRect)
overlayView.backgroundColor = UIColor(colorLiteralRed: 0.0/255.0, green: 0/255.0, blue: 0.0/255.0, alpha: 0.5)
overlayView.layer.opaque = false
overlayView.opaque = false
var maskLayer : CAShapeLayer = CAShapeLayer()
overlayView.layer.mask = maskLayer
}

Resources