using a local image from the device in order to classify it successfully - swift

I am writing a visual recognition application that uses VisualRecognition.classify in order to classify images. I have configured my Swift environment and haven't been able to classify images when including a URL from the internet:
I have now created an application that uses the camera and photo library to allow users to take photos and have them classified. I am running into issues when passing along an fileURL from the device to the VisualRecognition service though.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
imageView.image = image
imagePicker.dismiss(animated: true, completion: nil)
let visualRecongnition = VisualRecognition(version: version, apiKey: apiKey)
let imageData = image.jpegData(compressionQuality: 0.01)
let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentURL.appendingPathComponent("tempImage.jpg")
try? imageData?.write(to: fileURL, options: [])
visualRecongnition.classify(imageFile: fileURL, success: { (classifiedImages) in
print(classifiedImages)
}) // getting error here " Missing argument for parameter 'completionHandler' in call"
}else {
print("There was an error picking Image")
}
}
I have even attempted to include the NSURL directly into the classify call as I have done with the working external URL, but still run into the same error. Would really like to see how to use a local image from the device in order to classify it successfully

The problem is that your call to classify does not correspond to the signature of the classify method. In this line:
visualRecongnition.classify(imageFile: fileURL, success: { (classifiedImages) in
change success to completionHandler, and add a second parameter in the closure (even if you ignore it), like this:
visualRecongnition.classify(imageFile: fileURL, completionHandler: { classifiedImages,_ in

You must first request permission to use the camera and library. Open your Info.plist file in Source code mode, and add the following lines:
<key>NSCameraUsageDescription</key>
<string>Ask for permission to use camera.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Ask for permission to use Photo Library</string>
If you also want to be able to write images to the Camera Roll add this too:
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Ask for permission to save images to Photo library</string>

Related

How to grab all metadata from any image?

I have been searching google and could not find a solution for my problem. I have some code that grabs metadata from images the user picks from the image picker. The problem is that my code doesn't grab ALL metadata from HEIC and RAW images. I set up some print statements to find out which data my code doesn't grab from HEIC.
Manufacturer not found
Camera model not found
Camera software not found
Aperture not found
Focal length not found
ISO not found
Shutter speed not found
//And this is my metadata extracting code block
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[.originalImage] as? UIImage
let url = info[.imageURL]
let optionalImageData = try? Data(contentsOf: url as! URL)
guard let imageData = optionalImageData else { return }
let source: CGImageSource = CGImageSourceCreateWithData(imageData as CFData, nil)!
let metadata = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [AnyHashable: Any]
print(metadata!)
self.dismiss(animated: true, completion: nil)
}
I'm going to guess that you have not obtained user permission to access the photos library. You are allowed to present the picker even so, but the information that you can receive in response is very limited. If you have user permission, you can receive the image as a PHAsset and you can get the desired metadata from the photos library.

How to download image from iCloud without re-saving it?

I ran into a problem when trying to pick photo from gallery (app crashes when I pick image uploaded on iCloud).
So my first solution was to check if image is on device and if not, then download it from iCloud.
I used this code
let manager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.resizeMode = .exact
requestOptions.deliveryMode = .highQualityFormat;
// Request Image
manager.requestImageData(for: asset, options: requestOptions, resultHandler: { (data, str, orientation, info) -> Void in
// Do somethign with Image Data
})
I get imageData in this situation, but if I want to save it, it saves into new photo. So I get copy of image in Photos instead getting one original image that stores on device.
So my question is how to download image without re-saving it.
Basically I want same behaviour as in photos app (when photo is not on device, it downloads photo and save it into same picture instead creating copy of it)
I faced this issue, here is a workaround for it:
Set isNetworkAccessAllowed to false:
requestOptions.isNetworkAccessAllowed = false
That's because you need to make sure that we will not fetch it from the iCloud. At this point, if the image is not downloaded, the data in resultHandler should be nil:
manager.requestImageData(for: PHAsset(), options: requestOptions, resultHandler: { (data, str, orientation, info) -> Void in
if let fetchedData = data {
// the image is already downloaded
} else {
// the image is not downloaded...
// now what you should do is to set isNetworkAccessAllowed to true
// and make a new request to get it from iCloud.
}
})
Furthermore, you could leverage PHImageResultIsInCloudKey:
manager.requestImageData(for: PHAsset(), options: requestOptions, resultHandler: { (data, str, orientation, info) -> Void in
if let keyNSNumber = info?[PHImageResultIsInCloudKey] as? NSNumber {
if keyNSNumber.boolValue {
// its on iCloud...
// read the "keep in mind" below note,
// now what you should do is to set isNetworkAccessAllowed to true
// and make a new request.
} else {
//
}
}
})
As per mentioned in PHImageResultIsInCloudKey documentation, keep in mind that:
If true, no image was provided, because the asset data must be
downloaded from iCloud. To download the data, submit another request,
and specify true for the isNetworkAccessAllowed option.

File Directory Changing Every Reboot Swift iOS

I am currently saving images a user selects from their photo library & saving the url to User Defaults.
The issue I am currently facing is whenever my app reboots, such as an Xcode re-launch the URL I am saving to User Defaults is no longer valid. From my understanding/researching I need to save a relative URL for the image; however I am not having any luck doing this correctly. This occurs with both simulator & actual device.
From my UIImagePicker here are my steps:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let image = info[.originalImage] as? UIImage else { return }
let savingImage : SavingImage = SavingImage()
let ourStringedUrl : String = savingImage.saveImageDocumentDirectory(image: image, imageName: image.description)
}
From SavingImage, here is the saving of the photo itself
func getDirectoryPath() -> NSURL {
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("AllImages")
let url = NSURL(string: path)
return url!
}
func saveImageDocumentDirectory(image: UIImage, imageName: String) -> String {
let fileManager = FileManager.default
let path = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("AllImages")
if !fileManager.fileExists(atPath: path) {
try! fileManager.createDirectory(atPath: path, withIntermediateDirectories: true, attributes: nil)
}
let url = NSURL(string: path)
let imagePath = url!.appendingPathComponent(imageName)
let urlString: String = imagePath!.absoluteString
let imageData = image.jpegData(compressionQuality: 1)
fileManager.createFile(atPath: urlString as String, contents: imageData, attributes: nil)
return urlString as String
}
& Finally here is how I am loading the image itself:
func getImageFromDocumentDirectory(imageName : String) -> UIImage {
let fileManager = FileManager.default
var ourImage : UIImage!
let imagePath = "\(imageName)"
let urlString: String = imagePath
if fileManager.fileExists(atPath: urlString) {
ourImage = UIImage(contentsOfFile: urlString)
} else {
if ourImage == nil {
ourImage = UIImage(named: "defaultImage.png")
}
}
return ourImage
}
Where am I going wrong with the loading aspect? How exactly should I be saving the URL?
The URL that is being saved looks something similar to the following:
//var/mobile/Containers/Data/Application/17C16D0D-1BFA-44F5-A6BD-18DFAEA051E0/Documents/AllImages/%3CUIImage:0x280a393b0%20anonymous%20%7B3024,%204032%7D%3E
But will come up as nil upon loading the image on reboot.
The new directory will be created every time you reboot your simulator or every time you install new build to device. (Uninstall previous and install new). iOS does not give us permission to select the directory. As you install new application it will create its sandbox. Please make sure you check your directory on the same build.
An alternative might be to use iCloud Drive and upload your image there. The iCloud drive is linked to an user's iCloud account, and therefore should persist in case of an app update.
Here's a great tutorial to get started: https://theswiftdev.com/2018/05/17/how-to-use-icloud-drive-documents/
Here's a link to the docs: https://developer.apple.com/icloud/icloud-drive/

swift UIImagePickerController and extracting EXIF data from existing photos

I need to get location data (coordinates for latitude and longitude) from an image picked from PhotoLibrary using UIImagePickerController. Existing answers proposes using fetchAssetsWithALAssetURLs, but it is deprecated for iOS 8 to 11, so I wonder what's the alternative?
Thanks!
This worked for me. For new iOS versions, use the following lines of code.
var asset: PHAsset?
asset = info[UIImagePickerControllerPHAsset] as? PHAsset
Note that asset = nil if the user did not give permission to access his Photo Library since the PHAsset data is sensitive.
To obtain permission, edit Info.plist accordingly and request for permission using PHPhotoLibrary.requestAuthorization().
If you are only supporting iOS 11 and later then you can directly get the PHAsset using the .phAsset key. You only need to use PHAsset fetchAssetsWithALAssetURLs if you need to support iOS 10 or earlier.
Once you have the PHAsset reference, then you can access the location property to get the coordinates of the image.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
var asset: PHAsset?
if #available(iOS 11.0, *) {
asset = info[.phAsset] as? PHAsset
} else {
if let url = info[.referenceURL] as? URL {
let result = PHAsset.fetchAssets(withALAssetURLs: [url], options: nil)
asset = result.firstObject
}
}
if let asset = asset {
if let location = asset.location {
print("Image location is \(location.coordinate.latitude), \(location.coordinate.longitude)")
}
}
}

Swift: image cache not working as expected

I am trying to use image cache in swift but nothing works as expected. I have tried NSCache and AlamofireImage but both didn't work for me. I did not get any error with code but when I tried to fetch image from cache it always returns nil.
I guess the image is not saved in cache. Can someone here share some links with example on how to use image caching in swift or ios?
What you can do is download images in background thread. When image is downloaded save it an Object and update you'r main thread too. For displaying image next time you can directly load image from the cache.
Below I'm sharing my code
var imageCache = NSMutableDictionary()
if let img = imageCache.valueForKey("key") as? UIImage{
imageView.image = img
}
else{
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(NSURL(string: imgURL!)!, completionHandler: { (data, response, error) -> Void in
if(error == nil){
let img = UIImage(data: data!)
imageCache.setValue(img, forKey: "key") // Image saved for cache
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Update your image view
})
}
})
task.resume()
}
Hope so this may help you
You can use this as a disk and memory cache, if you implemented downloading image mechanism. You can specify cache size, expire time
https://github.com/huynguyencong/DataCache
Or you can use Kingfisher as a image caching library