I am having some problem getting a CIImage from my UIImage in extension.
I am trying to apply a WhitePointAdjust on the UIImage.
Here is my code:
extension UIImage{
enum ImageError: Error {
case filterLookupError(String)
case filterError(CIFilter)
case ciImageError(CIImage?)
case cgImageError(CIImage, CGRect)
}
func applyWhitePointAdjustment(_ whiteColor: UIColor) -> UIImage?{
let context = CIContext(options: nil)
let filterName = "CIWhitePointAdjust"
guard let wpaFilter = CIFilter(name: filterName) else {
ImageError.filterLookupError("filter not found: " + filterName).handle()
return nil
}
guard let inputImage = self.ciImage else {
ImageError.ciImageError(self.ciImage).handle()
return self
}
wpaFilter.setValue(inputImage, forKey: kCIInputImageKey)
wpaFilter.setValue(whiteColor, forKey: kCIInputColorKey)
guard let output = wpaFilter.outputImage else {
ImageError.filterError(wpaFilter).handle()
return nil
}
guard let cgImage = context.createCGImage(output, from: output.extent) else {
ImageError.cgImageError(output, output.extent).handle()
return nil
}
return UIImage(cgImage: cgImage)
}
}
Your help would be highly appreciated. I checked if, the UIImage is there and it is.
The filter also doesn't work, but I didn't have time to check it out. However if anyone has a quick solution for that, it would be appreciated as well
Update:
I replaced
guard let inputImage = self.ciImage
with:
guard let inputImage = CIImage(image: self)
and it seams to work. But I don't really understand the difference. Could anyone please give an explanation?
Related
This is my code block to invert colors of an image:
import Foundation
import UIKit
import CoreImage
extension UIImage {
static let defaultImage = UIImage(systemName: "person.circle.fill")!
func colorInverted() -> UIImage {
guard let ciImage = CIImage(image: self) else {
print("UIImage: Failed to get CIImage!")
return UIImage.defaultImage
}
guard let filter = CIFilter(name: "CIColorInvert") else {
print("UIImage: Failed to get filter!")
return UIImage.defaultImage
}
filter.setValue(ciImage, forKey: kCIInputImageKey)
guard let image = filter.outputImage else {
print("UIImage: Failed to get output image!")
return UIImage.defaultImage
}
return UIImage(ciImage: image)
}
}
This is working fine on simulator:
But on real device the result is a full blue image:
I have searched for a while but could not able to find a solution.
Can anyone help?
Note: I am getting images from GitHub API:
https://avatars0.githubusercontent.com/u/1?v=4
use CIContext instead of UIImage(CIImage:)
func applyFilter(){
let image = CIImage(CGImage: myimage.image?.CGImage)
let filter = CIFilter(name: "CISepiaTone")
filter.setDefaults()
filter.setValue(image, forKey: kCIInputImageKey)
let context = CIContext(options: nil)
let imageRef = context.createCGImage(filter.outputImage, fromRect: image.extent())
myimage.image = UIImage(CGImage: imageRef)
}
I am applying a number of core image filters to an image that I have captured. The problem that I am facing is that once I have applied the filters and saved the resulting image, I am unable to save it. I do not get any errors in my code but when I go to retrieve the image the application is unable to locate it. It says that the file does not exist. I suspect I may have used the filters incorrectly some how.
// PROCESS IMAGE TO MAKE IT LOOK 'SCANNED'
guard let scannedImage = self.processImage(image: image)else{
print("failed to process image")
return
}
// SAVE SCANNED IMAGE TO LOCAL FILE SYSTEM
self.saveImageToLocalFile(image: scannedImage) // PROBELM HERE.
// PROCESS IMAGE
func processImage(image: UIImage) -> UIImage? {
guard let ciimage = CIImage.init(image: image) else{return nil}
// APPLY FILTERS
guard let shadowCimage = shadow(inputImage: ciimage) else{return nil}
guard let colorControlCimage = colorControl(input: shadowCimage, contrast: 4.0, saturation: 0.3, brightness: 0.3) else{return nil}
guard let sharpenCimage = sharpen(inputImage: colorControlCimage, inputRadius: 2.5, inputIntensity: 1.0) else {return nil}
guard let sharpIntensity = sharpenLumin(inputImg: sharpenCimage, inputSharpness: 0.2) else{return nil}
print("processImage END")
return UIImage.init(ciImage: sharpIntensity)
}
// FILTER 'CIColorControls'
func colorControl(input: CIImage, contrast: Float, saturation: Float, brightness: Float) -> CIImage? {
let filter = CIFilter(name: "CIColorControls")
filter?.setValue(input, forKey: kCIInputImageKey)
filter?.setValue(contrast, forKey: kCIInputContrastKey)
filter?.setValue(saturation, forKey: kCIInputSaturationKey)
filter?.setValue(brightness, forKey: kCIInputBrightnessKey)
return filter?.outputImage
}
// FILTER 'CIUnsharpMask'
func sharpen(inputImage: CIImage, inputRadius: Float, inputIntensity: Float) -> CIImage? {
let filter = CIFilter(name: "CIUnsharpMask")
filter?.setValue(inputImage, forKey: kCIInputImageKey)
//filter?.setDefaults()
filter?.setValue(inputRadius, forKey: kCIInputRadiusKey)
filter?.setValue(inputIntensity, forKey: kCIInputIntensityKey)
return filter?.outputImage
}
// FILTER 'CIHighlightShadowAdjust'
func shadow(inputImage: CIImage) -> CIImage? {
let filter = CIFilter(name: "CIHighlightShadowAdjust")
filter?.setValue(inputImage, forKey: kCIInputImageKey)
filter?.setDefaults()
return filter?.outputImage
}
// FILTER 'CISharpenLuminance'
func sharpenLumin(inputImg: CIImage, inputSharpness: Float) -> CIImage? {
let filter = CIFilter.init(name: "CISharpenLuminance")
filter?.setValue(inputImg, forKey: kCIInputImageKey)
filter?.setValue(inputSharpness, forKey: kCIInputSharpnessKey)
return filter?.outputImage
}
// SAVE UIIMAGE TO LOCAL FILE SYSTEM
func saveImageToLocalFile(image: UIImage) -> Void {
// CREATE URL - SAVE TO PATH
let imageURL = createPhotoURL() // CORRECT FULL LENGTH URL FOR FILE UPLAOD
print("IMAGE SAVED TO URL: \(imageURL)")
let imageData = UIImageJPEGRepresentation(image, 1.0)
do{
try imageData?.write(to: imageURL)
self.scannedImageURL = imageURL
}catch{
print("error writing img to local dir")
}
}
The problem is that your imageData is nil, because of how you are creating the UIImage.
As explained in this answer, you have a nil in the UIImage.cgImage property, which UIImageJPEGRepresentation uses. Here's the code you need to use:
func saveImageToLocalFile(image: UIImage) -> Void {
let imageURL = createPhotoURL()
var uiImage = image
if image.cgImage == nil {
guard let ciImage = image.ciImage, let cgImage = CIContext(options: nil).createCGImage(ciImage, from: ciImage.extent) else { return }
uiImage = UIImage(cgImage: cgImage)
}
if let imageData = UIImageJPEGRepresentation(uiImage, 1.0) {
do {
try imageData?.write(to: imageURL)
self.scannedImageURL = imageURL
} catch {
print("error writing img to local dir")
}
} else {
print("could not create JPEG image")
}
}
I get nil on an iOS device (works ok on Simulator or Playground) when I create create a barcode UIImage with generateBarcode("ABCDEF") and call UIImageJPEGRepresentation or UIImagePNGRepresentation.
There is clearly something still wrong in the UIImage as the debugger cannot display the UIImage either. The image exists, the cgImage property is set but UIImageJPEGRepresentation doesn't like it.
I have tried to solve it as per: UIImageJPEGRepresentation returns nil
class func generateBarcode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CICode128BarcodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
if let ciImage = filter.outputImage {
if let cgImange = convertCIImageToCGImage(inputImage: ciImage) {
return UIImage(cgImage: cgImange)
}
}
}
return nil
}
class func convertCIImageToCGImage(inputImage: CIImage) -> CGImage? {
let context = CIContext(options: nil)
if let cgImage = context.createCGImage(inputImage, from: inputImage.extent) {
return cgImage
}
return nil
}
I wrote a function to apply the filter to a photo. Why the function always returns nil?
func getImage() -> UIImage? {
let openGLContext = EAGLContext(api: .openGLES2)
let context = CIContext(eaglContext: openGLContext!)
let coreImage = UIImage(cgImage: image.cgImage!)
filter.setValue(coreImage, forKey: kCIInputImageKey)
filter.setValue(1, forKey: kCIInputContrastKey)
if let outputImage = filter.value(forKey: kCIOutputImageKey) as? CIImage {
let output = context.createCGImage(outputImage, from: outputImage.extent)
return UIImage(cgImage: output!)
}
return nil
}
When you working with coreImage in iOS.You have to considered coreImage classes such as CIFilter, CIContext, CIVector, and CIColor and the input image should be a CIImage which holds the image data. It can be created from a UIImage.
Note: You haven't mentioned about your filter definition or custom filter that you using.From your code, I can see you trying to change the contrast of an input image.So, I am testing the code using CIColorControls filter to adjust the contrast.
func getImage(inputImage: UIImage) -> UIImage? {
let openGLContext = EAGLContext(api: .openGLES2)
let context = CIContext(eaglContext: openGLContext!)
let filter = CIFilter(name: "CIColorControls")
let coreImage = CIImage(image: filterImage.image!)
filter?.setValue(coreImage, forKey: kCIInputImageKey)
filter?.setValue(5, forKey: kCIInputContrastKey)
if let outputImage = filter?.value(forKey: kCIOutputImageKey) as? CIImage {
let output = context.createCGImage(outputImage, from: outputImage.extent)
return UIImage(cgImage: output!)
}
return nil
}
You can call the above func like below.
filterImage.image = getImage(inputImage: filterImage.image!)
Output:
You never enter the if in that case. Maybe filter.value returns nil OR it isn't a CIImage it returns. Extract it like that and set a breakpoint at if or add a print between:
let value = filter.value(forKey: kCIOutputImageKey)
if let outputImage = value as? CIImage {
let output = context.createCGImage(outputImage, from: outputImage.extent)
return UIImage(cgImage: output!)
}
If you need more informations you have to share the filter definition and usage code.
I am making image form QR Code by using following code:
func createQRFromString(str: String) -> CIImage? {
let stringData = str.dataUsingEncoding(NSUTF8StringEncoding)
let filter = CIFilter(name: "CIQRCodeGenerator")
filter?.setValue(stringData, forKey: "inputMessage")
filter?.setValue("H", forKey: "inputCorrectionLevel")
return filter?.outputImage
}
And Then I am adding to UIImageView Like this:
if let img = createQRFromString(strQRData) {
let somImage = UIImage(CIImage: img, scale: 1.0, orientation: UIImageOrientation.Down)
imgviewQRcode.image = somImage
}
Now I need to save this to a JPEG or PNG file. But when I am doing so my app crashes:
#IBAction func btnSave(sender: AnyObject) {
// // Define the specific path, image name
let documentsDirectoryURL = try! NSFileManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
// create a name for your image
let fileURL = documentsDirectoryURL.URLByAppendingPathComponent("image.jpg")
if let image = imgviewQRcode.image // imgviewQRcode is UIImageView
{
if let path = fileURL?.path
{
if !NSFileManager.defaultManager().fileExistsAtPath(fileURL!.path!)
{
if UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
{
print("file saved")
}
}//Checking existing file
}//Checking path
}//CHecking image
}
Crash Point
UIImageJPEGRepresentation(image, 1.0)!.writeToFile(path, atomically: true)
Reason
fatal error: unexpectedly found nil while unwrapping an Optional value
Debug Tests:
func convert(cmage:CIImage) -> UIImage
{
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
Use this function to convert CIImage to UIImage . It works .
func convert(image:CIImage) -> UIImage
{
let image:UIImage = UIImage.init(ciImage: image)
return image
}
Perhaps, this was unavailable before, but it is now possible to create UIImages directly from CIImage.
My final code
func generateQRCode(from string: String) -> UIImage? {
let data = string.data(using: String.Encoding.ascii)
if let filter = CIFilter(name: "CIQRCodeGenerator") {
filter.setValue(data, forKey: "inputMessage")
let transform = CGAffineTransform(scaleX: 3, y: 3)
if let output = filter.outputImage?.transformed(by: transform) {
let context:CIContext = CIContext.init(options: nil)
let cgImage:CGImage = context.createCGImage(output, from: output.extent)!
let image:UIImage = UIImage.init(cgImage: cgImage)
return image
}
}
return nil
}