I am trying to save an image as a bitmap using AVFoundation. Right now I am using jpeg, and was wondering what I would need to change in order to save the image as a bitmap. Side note - we plan on storing the byte[] onto our google app engine and then any device (Android or IOS) that would need the image, would be able to pull the byte[] from the database and convert it into an image on the device. Is this a valid way of going about storing images? If not what would you suggest?
Here is my code saving the image as a jpeg
#IBAction func didPressTakePhoto(sender: AnyObject) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) {
var imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
var dataProvider = CGDataProviderCreateWithCFData(imageData)
var cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, kCGRenderingIntentDefault)
//change orientation based on if the camera is front or back
if self.selectedCamera == 1 {
var image = UIImage(CGImage: cgImageRef, scale: 1.0, orientation: UIImageOrientation.LeftMirrored)
self.capturedImage.image = image
} else {
var image = UIImage(CGImage: cgImageRef, scale: 1.0, orientation:UIImageOrientation.Right)
self.capturedImage.image = image
}
Related
I am masking an image using an image_mask, the following code shows that:
func maskOriginalImage() -> UIImage? {
if(self.maskImage != nil && self.originalImage != nil){
let maskReference = self.maskImage?.cgImage!
let maskedReference = self.originalImage?.cgImage!.masking(maskReference!)
self.maskImage = nil
return UIImage(cgImage: maskedReference!)
}
return nil
Now I want to save the masked image to photo library.
func saveImageToLibrary(){
UIImageWriteToSavedPhotosAlbum(self.segmentedimage, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
}
But while saving the maskedImage, original image is saved. The workaround I found is to create a new UIimagecontext and draw the maskedimage in the context and use that image to save.
func getImageFromContext() -> UIImage? {
UIGraphicsBeginImageContextWithOptions(self.size, false, scale)
draw(in: CGRect(origin: .zero, size: size))
let result = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return result
}
Why I just can't save the masked image directly? I am relatively new to IOS development so any inputs will be helpful.
Is NSKeyedArchiver appropriate to convert UIImage to Data?
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: UIImage(named: somePath), requiringSecureCoding: true)
...
} catch {
print(error)
}
Or is it overkill and using pngData() is more appropriate?
let image = UIImage(named: somePath)
let data = image?.pngData()
and how can I convert from UIImage to HEIF / HEIC Data ?
The goal is to save the image to the device's file system.
No. Never use NSKeyedArchiver to convert your image to Data. Choose an image format (HEIC, PNG, JPEG, etc) and get its data representation. You should only use PNG when saving images to use in your UI. Most of the time jpeg is the preferred choice. If the device supports HEIC it is an option considering the image quality and reduced data size.
If you need to check if the user device supports HEIC type you can do it as follow:
var isHeicSupported: Bool {
(CGImageDestinationCopyTypeIdentifiers() as! [String]).contains("public.heic")
}
If you need to convert your image to HEIC you need to get a CGImage from your UIImage and convert your UIImage's imageOrientation to CGImagePropertyOrientation to preserve the orientation when creating its data representation:
extension UIImage {
var heic: Data? { heic() }
func heic(compressionQuality: CGFloat = 1) -> Data? {
guard
let mutableData = CFDataCreateMutable(nil, 0),
let destination = CGImageDestinationCreateWithData(mutableData, "public.heic" as CFString, 1, nil),
let cgImage = cgImage
else { return nil }
CGImageDestinationAddImage(destination, cgImage, [kCGImageDestinationLossyCompressionQuality: compressionQuality, kCGImagePropertyOrientation: cgImageOrientation.rawValue] as CFDictionary)
guard CGImageDestinationFinalize(destination) else { return nil }
return mutableData as Data
}
}
extension CGImagePropertyOrientation {
init(_ uiOrientation: UIImage.Orientation) {
switch uiOrientation {
case .up: self = .up
case .upMirrored: self = .upMirrored
case .down: self = .down
case .downMirrored: self = .downMirrored
case .left: self = .left
case .leftMirrored: self = .leftMirrored
case .right: self = .right
case .rightMirrored: self = .rightMirrored
#unknown default:
fatalError()
}
}
}
extension UIImage {
var cgImageOrientation: CGImagePropertyOrientation { .init(imageOrientation) }
}
Usage for lossless compression:
if isHeicSupported, let heicData = image.heic {
// write your heic image data to disk
}
or adding compression to your image:
if isHeicSupported, let heicData = image.heic(compressionQuality: 0.75) {
// write your compressed heic image data to disk
}
I'm building a camera app that captures a photo in the BGRA format, and applies a Core Image filter on it before saving it to the Photos app. On the iPhone 7 Plus, the input photo is in the Display P3 color space, but the output is in the sRGB color space:
How do I prevent this from happening?
Here's my code:
let sampleBuffer: CMSampleBuffer = ...
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
let metadata = CMCopyDictionaryOfAttachments(nil, self, kCMAttachmentMode_ShouldPropagate)!
let ciImage = CIImage(cvImageBuffer: pixelBuffer,
options:[kCIImageProperties: metadata])
NSLog("\(ciImage.colorSpace)")
let context = CIContext()
let data = context.jpegRepresentation(of: ciImage,
colorSpace: ciImage.colorSpace!,
options: [:])!
// Save this using PHPhotoLibrary.
This prints:
Optional(<CGColorSpace 0x1c40a8a60> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Display P3))
(In my actual code, I apply a filter to the CIImage, which creates another CIImage, which I save. But I can reproduce this problem even with the original CIImage, so I've eliminated the filter.)
How do I apply a Core Image filter to a P3 image and save it as a P3 image, not sRGB?
Notes:
(1) This is on iPhone 7 Plus running iOS 11.
(2) I'm using the wide camera, not tele, dual or front.
(3) If I ask AVFoundation to give me a JPEG-encoded image rather than BGRA, and save it without involving Core Image, this problem doesn't occur — the color space isn't reduced to sRGB.
(4) I tried using kCIImageColorSpace, but it made no difference:
let p3 = CGColorSpace(name: CGColorSpace.displayP3)!
let ciImage = CIImage(
cvImageBuffer: pixelBuffer,
options:[kCIImageProperties: metadata,
kCIImageColorSpace: p3])
(5) I tried using kCIContextOutputColorSpace in addition to the above, as an argument when creating the CIContext, but it again made no difference.
(6) The code that takes a Data and saves it to PHPhotoLibrary is not the problem, since it works in case (2) above.
let context = CIContext(options: [kCIContextOutputColorSpace: CGColorSpace.p3])
How do I apply a Core Image filter to a P3 image and save it as a P3 image, not sRGB?
I've had the same issue and I think this may be a bug with context.jpegRepresentation(..).
I've had more success using ImageIO to create the JPEG data, as shown in the createJPEGData function below. For example:
let eaglContext = EAGLContext(api: .openGLES2)
let options = [kCIContextWorkingColorSpace: CGColorSpace(name: CGColorSpace.extendedSRGB)!,
kCIContextOutputPremultiplied: true,
kCIContextUseSoftwareRenderer: false] as [String : Any]
let ciContext = CIContext(eaglContext: eaglContext, options: options)
let colorSpace = CGColorSpace(name: CGColorSpace.displayP3)!
guard let imageData = createJPEGData(from: image,
jpegQuality: 0.9,
outputColorSpace: colorSpace,
context: ciContext) else {
return
}
PHPhotoLibrary.shared().performChanges({ () -> Void in
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .photo,
data: imageData,
options: nil)
}, completionHandler: { (success: Bool, error : Error?) -> Void in
// handle errors, etc
})
func createJPEGData(from image: CIImage,
jpegQuality: Float,
outputColorSpace: CGColorSpace,
context: CIContext) -> Data? {
let jpegData: CFMutableData = CFDataCreateMutable(nil, 0)
if let destination = CGImageDestinationCreateWithData(jpegData, kUTTypeJPEG, 1, nil) {
if let cgImage = context.createCGImage(image,
from: image.extent,
format: kCIFormatRGBA8,
colorSpace: outputColorSpace) {
CGImageDestinationAddImage(destination, cgImage, image.properties as CFDictionary?)
if CGImageDestinationFinalize(destination) {
return jpegData as Data
}
}
}
}
I know how to save the captured photo to the library but I added some extra code because I wanted the label in my camera view to combine together and save. When I try to save it to the photo library it doesn't save with the label. Here is the code I have:
#IBAction func takePicture(sender: AnyObject) {
if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
videoConnection.videoOrientation = AVCaptureVideoOrientation.Portrait
stillImageOutput?.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: {(sampleBuffer, error) in
if (sampleBuffer != nil) {
let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
let dataProvider = CGDataProviderCreateWithCFData(imageData)
let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
UIGraphicsBeginImageContextWithOptions(self.previewCamera.bounds.size, self.previewCamera.opaque, 0.0)
self.previewCamera.layer.renderInContext(UIGraphicsGetCurrentContext()!)
UIGraphicsEndImageContext()
self.capturedImage.image = UIGraphicsGetImageFromCurrentImageContext()
//saves captured picture to camera roll.
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
}
I'm working with PhotoKit and have implemented filters users can apply to photos in their Photo Library. I am currently obtaining the image, applying a filter, returning the edited version as a CIImage, then I convert the CIImage into NSData using UIImageJPEGRepresentation so I may write that out to disk. While this works beautifully, when users attempt to edit really large (like 30 MB) photos it can take upwards of 30 seconds for this to occur, with 98% of the time spent on UIImageJPEGRepresentation (stat obtained from Instruments).
I am looking for a more efficient way to save this edited photo to disk without compromising quality, if possible.
I understand UIImagePNGRepresentation may result in improved quality, but this is even slower than the JPEG representation.
This is what I am currently doing, on the default priority thread (qos_class_utility):
func jpegRepresentationOfImage(image: CIImage) -> NSData {
let eaglContext = EAGLContext(API: .OpenGLES2)
let ciContext = CIContext(EAGLContext: eaglContext)
let outputImageRef = ciContext.createCGImage(image, fromRect: image.extent())
let uiImage = UIImage(CGImage: outputImageRef, scale: 1.0, orientation: UIImageOrientation.Up)
return UIImageJPEGRepresentation(uiImage, 0.9) //this takes upwards of 20-30 seconds with large photos!
}
//writing out to disk:
var error: NSError?
let success = jpegData.writeToURL(contentEditingOutput.renderedContentURL, options: NSDataWritingOptions.AtomicWrite, error: &error)
I would suggest passing the CGImage directly to ImageIO using CGImageDestination. You can pass a dictionary to CGImageDestinationAddImage to indicate the compression quality, image orientation, etc.
CFDataRef save_cgimage_to_jpeg (CGImageRef image)
{
CFMutableDataRef cfdata = CFDataCreateMutable(nil,0);
CGImageDestinationRef dest = CGImageDestinationCreateWithData(data, CFSTR("public.jpeg"), 1, NULL);
CGImageDestinationAddImage(dest, image, NULL);
if(!CGImageDestinationFinalize(dest))
; // error
CFRelease(dest);
return cfdata
}
Try using the CIContext method writeJPEGRepresentation (available iOS 10) as follows to eschew UIImage and make the writing faster.
extension CIImage {
#objc func saveJPEG(_ name:String, inDirectoryURL:URL? = nil, quality:CGFloat = 1.0) -> String? {
var destinationURL = inDirectoryURL
if destinationURL == nil {
destinationURL = try? FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
}
if var destinationURL = destinationURL {
destinationURL = destinationURL.appendingPathComponent(name)
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let context = CIContext()
try context.writeJPEGRepresentation(of: self, to: destinationURL, colorSpace: colorSpace, options: [kCGImageDestinationLossyCompressionQuality as CIImageRepresentationOption : quality])
return destinationURL.path
} catch {
return nil
}
}
}
return nil
}
}
The #objc keyword enables you to call the method in Objective-C as:
NSString* path = [image saveJPEG:#"image.jpg" inDirectoryURL:url quality:1.0];
For PNG there is a similar method writePNGRepresentation for iOS 11:
if #available(iOS 11.0, *) {
if let colorSpace = CGColorSpace(name: CGColorSpace.sRGB) {
do {
let format = CIFormat.RGBA8
try context.writePNGRepresentation(of: self, to: destinationURL, format: format, colorSpace: colorSpace)
return destinationURL.path
} catch {
return nil
}
}
}