How I can clear memory when use Camera? - ios

I'm trying to create a camera app but got memory leak.
I can clear cache of PhotoLibrary but cant clear cache of Camera.
App will down after take about 40 photos.
How can I clear memory?
// After taking a picture
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if info[UIImagePickerControllerOriginalImage] != nil {
let image = info[UIImagePickerControllerOriginalImage] as! UIImage
var orientation: Int?
if isNewPhoto == true {
UIImageWriteToSavedPhotosAlbum(image, self, #selector(BaseResultViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
let metadata = info[UIImagePickerControllerMediaMetadata] as? NSDictionary
orientation = metadata?.objectForKey("Orientation") as? Int
}
else {
switch image.imageOrientation.rawValue {
case 0:
orientation = 1
case 1:
orientation = 3
case 2:
orientation = 8
case 3:
orientation = 6
default:
break
}
}
let uuid = NSUUID().UUIDString
let imageName = "\(uuid).PNG"
let jpgImageName = "\(uuid)-thumb.JPG"
let data = UIImagePNGRepresentation(image)
let jpgData = UIImageJPEGRepresentation(image, 0.7)
let localPath = relationData.setFile(imageName, data: data!)
let _ = relationData.setFile(jpgImageName, data: jpgData!)
getViewIns()?.hideActionSheet()
baseModel.saveValuable(NSURL(fileURLWithPath: localPath), orientation: orientation)
prepareSegue(true)
resetValuable()
}
picker.dismissViewControllerAnimated(true, completion: nil)
}

Related

Apply filter to the specific Image Segmentation Matte

I need to get Teeth SegmentationMatte from the image, add some filter to it and save it to the new or to the original image.
Here is my current code:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
pictureImageView.image = pickedImage
picture = pickedImage
if #available(iOS 13.0, *) {
let base = CIImage(cgImage: picture.cgImage!)
let maxcomp = CIFilter.maximumComponent()
maxcomp.inputImage = base
let makeup = maxcomp.outputImage
guard let pictureCgImage = picture.cgImage else { return }
let matte = CIImage(cgImage: pictureCgImage, options: [.auxiliarySemanticSegmentationTeethMatte : true,
.auxiliarySemanticSegmentationSkinMatte : false,
.auxiliarySemanticSegmentationHairMatte : false])
let blend = CIFilter.blendWithMask()
blend.backgroundImage = base
blend.inputImage = makeup
blend.maskImage = matte
let result = blend.outputImage
let context = CIContext()
guard let filteredImage = result,
let perceptualColorSpace = CGColorSpace(name: CGColorSpace.sRGB),
let imageData = context.heifRepresentation(of: filteredImage, format: .RGBA8, colorSpace: perceptualColorSpace, options: [.semanticSegmentationTeethMatteImage: filteredImage]) else { return }
let neededImage = CIImage(data: imageData) // our image with filtered teeth
}
}
dismiss(animated: true, completion: nil)
}
As a result, I am expecting to have the original image with filtered teeth. But I'm getting the image with a filter added to the whole picture, not only the teeth matte.

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 to get image size in KB or Mb from image picker? [duplicate]

This question already has answers here:
How to get image file size in Swift?
(14 answers)
Closed 4 years ago.
I'm facing some issue to get image size in my code here is what I'm doing:
let imageData : NSData = UIImageJPEGRepresentation(images[0], 1) as! NSData
let formatter = ByteCountFormatter()
formatter.allowedUnits = .useKB
formatter.countStyle = .binary
let imageSize = formatter.string(fromByteCount: Int64(imageData.length))
print(imageSize)
Here You can get image size
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
var Size = Float()
var data = Data()
btnCancel.isUserInteractionEnabled = true
if let mediaType = info[UIImagePickerControllerMediaType] as? String {
if mediaType == "public.image" {
if let pickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
data = UIImageJPEGRepresentation(pickedImage, 1.0)!
//Here you get MB size
Size = Float(Double(data.count)/1024/1024)
//For Kb just remove single 1024 from size
// I am checking 5 MB size here you check as you want
if Size <= 5.00{
// Here your image
}
}
}
else
if mediaType == "public.movie" {
let videoURL = info[UIImagePickerControllerMediaURL] as? URL
data = try! Data.init(contentsOf: videoURL!)
Size = Float(Double(data.count)/1024/1024)
if Size <= 5.00{
//your video here
}
}
}
picker.dismiss(animated: true) {
if Size > 5.0{
Utilities.showAlertView(title: "title", message: "message")
}
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
picker.dismiss(animated: true, completion: nil)
}
You can simply check the size in KB by the following UIImage extension:
extension UIImage {
func logImageSizeInKB(scale: CGFloat) -> (Int, Data) {
let data = UIImageJPEGRepresentation(self, scale)!
let formatter = ByteCountFormatter()
formatter.allowedUnits = ByteCountFormatter.Units.useKB
formatter.countStyle = ByteCountFormatter.CountStyle.file
let imageSize = formatter.string(fromByteCount: Int64(data.count))
print("ImageSize(KB): \(imageSize)")
return (Int(Int64(data.count) / 1024), data)
}
}
Note:
Can use UIImagePNGRepresentation too for png image size. By the way PNG images are heavier than JPEG/ JPG.
Try this code, to get image size when getting an image from picker view.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
dismiss(animated: true, completion: nil);
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
//get the size of image
let imgData: NSData = NSData(data: UIImageJPEGRepresentation((chosenImage), 1)!)
let imageSize: Int = imgData.length
print("size of image in MB: %f ", Double(imageSize) / 1024.0/1024.0)
}

UIImagePickerControllerOriginalImage from camera is not calling in ios swift

I am able select image from gallery and save to UIImageView but when trying of camera i can not able save the image in UIImageView. it calls didFinishPickingMediaWithInfo method but its going inside UIImagePickerControllerOriginalImage if case.
here is the code i used for camera and gallery picker:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let pickedImage = info[UIImagePickerControllerImageURL] as? URL {
if let data : Data = try? Data(contentsOf: pickedImage as URL)
{
print(data.format)
if data.format == "gif" {
let data = try? Data(contentsOf: pickedImage) //make sure your image in this url does exist, otherwise unwrap in a if let check / try-catch
let imageView = UIImage(data: data!)
let width = imageView?.size.width
let height = imageView?.size.height
let url = SaveItemFilemanager.sharedinstance.saveGifimage(data: data!)
addGifImage(url: url!, width: width!, height: height!)
} else {
if let photo = info[UIImagePickerControllerLivePhoto] as? PHLivePhoto {
let assetResources = PHAssetResource.assetResources(for: photo)
let asset = assetResources[1]
let url = SaveItemFilemanager.sharedinstance.saveLiveimage(data: asset)
print(url!)
let frameCount = 16
let delayTime = Float(0.2)
let loopCount = 0
let width = photo.size.width
let height = photo.size.height
let regift : Regift = Regift(sourceFileURL: url!, frameCount: frameCount, delayTime: delayTime, loopCount: loopCount)
addGifImage(url: regift.createGif()!, width: width, height: height)
} else if let normalImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
let width = normalImage.size.width
let height = normalImage.size.height
let url = SaveItemFilemanager.sharedinstance.saveimage(image: normalImage)
addImage(url: url!, width: width, height: height)
} else {
let normalImage = info[UIImagePickerControllerOriginalImage] as? UIImage
let width = normalImage?.size.width
let height = normalImage?.size.height
let url = SaveItemFilemanager.sharedinstance.saveimage(image: normalImage!)
addImage(url: url!, width: width!, height: height!)
}
}
}
}
dismiss(animated: true, completion: nil)
}
func showCamera() {
let imagePicker = UIImagePickerController()
if(UIImagePickerController .isSourceTypeAvailable(.camera))
{
imagePicker.delegate = self
imagePicker.sourceType = .camera
}
present(imagePicker, animated: true, completion: nil)
}
I checked even by keeping break point in didfinishpicking media it calls and dismissed immediately.

How to get image name from UIImagePickerController taken with Camera

This below code is working perfectly fine for images picked from gallery. But will not work if taken with Camera. I tried to save image into storage and read again, but I was unable to do that. So could any one help me in this? Thank you.
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
if let referenceUrl = info[UIImagePickerControllerReferenceURL] as? NSURL, image = info[UIImagePickerControllerOriginalImage] as? UIImage {
let phAsset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).lastObject as! PHAsset
PHImageManager.defaultManager().requestImageDataForAsset(phAsset, options: PHImageRequestOptions(), resultHandler: { (imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey")) {
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
print("path q\(path)")
self.mImageUrl = path
self.mlocalPath = path.path
self.mImageExtension = path.pathExtension
self.mImageName = path.lastPathComponent!
print("mImageName q\(self.mImageName)")
}
})
}
dismissViewControllerAnimated(true, completion: nil)
}
Swift 5+
As the previous answers sugested, the image is not stored in gallery yet and hence no imageName. You need to store it in gallery. Use the below Helper class to save and get images from FileManager.
Thanks to this Answer
class CameraImageManager {
static let shared = CameraImageManager()
public func saveImage(imageName: String, image: UIImage) {
guard let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileName = imageName
let fileURL = documentsDirectory.appendingPathComponent(fileName)
guard let data = image.jpegData(compressionQuality: 1) else { return }
//Checks if file exists, removes it if so.
if FileManager.default.fileExists(atPath: fileURL.path) {
do {
try FileManager.default.removeItem(atPath: fileURL.path)
print("Removed old image")
} catch let removeError {
print("couldn't remove file at path", removeError)
}
}
do {
try data.write(to: fileURL)
} catch let error {
print("error saving file with error", error)
}
}
public func getImagePathFromDiskWith(fileName: String) -> URL? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
return imageUrl
}
return nil
}
public func loadImageFromDiskWith(fileName: String) -> UIImage? {
let documentDirectory = FileManager.SearchPathDirectory.documentDirectory
let userDomainMask = FileManager.SearchPathDomainMask.userDomainMask
let paths = NSSearchPathForDirectoriesInDomains(documentDirectory, userDomainMask, true)
if let dirPath = paths.first {
let imageUrl = URL(fileURLWithPath: dirPath).appendingPathComponent(fileName)
let image = UIImage(contentsOfFile: imageUrl.path)
return image
}
return nil
}
}
Now, in your imagePickerController didFinishPickingMediaWithInfo callback function, this is how you can assign a name to an image and save it.
public func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
guard let image = info[.editedImage] as? UIImage else { return }
let imageName = "RDV_" + UUID().uuidString
CameraImageManager.shared.saveImage(imageName: imageName, image: image)
print("IMAGE NAME IS: ", imageName)
}
Hope It Helps.
You can use a notification with addObserver like this
ViewController A : where you want image to be changed, add this in viewDidLoad
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(self.methodOfImageChange(_:)), name:"ImageChanged", object: nil)
Add this method in ViewController A
func methodOfImageChange(notification: NSNotification){
let appStatus = notification.userInfo as? Dictionary<String,AnyObject>
// appStatus contains your image in "image" key
}
Now in didFinishPickingMediaWithInfo add this
let dictionary: [String:AnyObject] = [
"image" : (info[UIImagePickerControllerOriginalImage] as? UIImage)!,
]
NSNotificationCenter.defaultCenter().postNotificationName("ImageChanged", object: self, userInfo: dictionary)
picker .dismissViewControllerAnimated(true, completion: nil)
Hope this helps
The image isn't in the gallery yet, so I don't believe you have a name.
In my app the flow (via navigation controller) is:
Selection VC (choice of Camera or Photo Library) ->
UIImagePickerController ->
Edit VC (with back navigation and action button for - among others - saving to Photo Library)
If the user chooses Camera, they take a picture and the options are "Retake" or "Use Photo". Is they choose "Use Photo", they are in the Edit VC.
If they then choose to go back to the Select VC, the image is nowhere to be found.

Resources