I have a function to rotate the hue on UIImages that work from other functions within the same class, but within the captureOutput function (used with AVCaptureVideoDataOutput), the same image object crashes my app with the "unexpectedly found nil while unwrapping an Optional value". There doesn't look to be a difference in how I'm calling it, so maybe it has to do with the captureOutput function and what it allows?
Outside of viewController class declaration:
var image:UIImage? = nil
var imageSource:UIImage? = nil
var imageView:UIImageView? = nil;
var hueDeg:CGFloat = 0.00
functions:
func rotateHue(with source: UIImage, rotatedByHue deltaHueRadians: CGFloat) -> UIImage {
// Create a Core Image version of the image.
let sourceCore = CIImage(cgImage: (source.cgImage)!) // crashes here
// Apply a CIHueAdjust filter
let hueAdjust = CIFilter(name: "CIHueAdjust")
hueAdjust?.setDefaults()
hueAdjust?.setValue(sourceCore, forKey: "inputImage")
hueAdjust?.setValue(deltaHueRadians, forKey: "inputAngle")
let resultCore = hueAdjust?.value(forKey: "outputImage") as? CIImage
// Convert the filter output back into a UIImage.
let context = CIContext(options: nil)
let resultRef = context.createCGImage(resultCore!, from: (resultCore?.extent)!)
let result = UIImage(cgImage: resultRef!)
//CGImageRelease(resultRef)
return result
}
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection){
let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
let cameraImage = CIImage(cvPixelBuffer: pixelBuffer!)
image = UIImage(ciImage: cameraImage)
image = rotateHue(with:image!, rotatedByHue:hueDeg)
DispatchQueue.main.async(){
imageView!.image = image
}
}
Here's another function that I call rotateHue from, and it doesn't crash. It's a function that is called by a timer:
@objc func cycleHue(){
hueDeg += 0.2;
image = imageSource
image = rotateHue(with:image!, rotatedByHue:hueDeg)
imageView!.image = UIImage(cgImage: (image?.cgImage)!, scale: 1.0, orientation: UIImageOrientation.right)
}
The documentation for UIImage
's cgImage
property says this:
If the UIImage object was initialized using a CIImage object, the value of the property is NULL.
Since this value can be nil
, you should not use the force-unwrap (!
) operator, or you could get a crash, as you're seeing. Instead, we need to test for the condition that the value may be nil
.
Fortunately, the condition that makes cgImage
be nil
is that it was apparently initialized using a CIImage
. Since it appears a CIImage
is what you want anyway, then the proper way to go about what you're trying to do is to:
UIImage
's ciImage
property, and if you get a non-nil value, use that. ciImage
is nil
, then fall back on calling cgImage
and making a CIImage
from it if it is non-nil
.cgImage
also happens to be nil, then something weird happened; in this case, you can throw an error or otherwise bail out gracefully.