0

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)
}
Chewie The Chorkie
  • 4,896
  • 9
  • 46
  • 90
  • 1
    Which value is nil when you crash? That's your first clue. – Luke Oct 27 '17 at 20:55
  • 1
    You should avoid to use force unwrapping(`!`) if possible. Always unwrap safely with `if let` or `guard let`. – Ryan Oct 27 '17 at 20:56
  • I thought it would be "image", but if I get rid of the function call for rotateHue, it does not crash, and shows the cameras output on the app. It occurs when sourceCore is assigned in rotateHue(). – Chewie The Chorkie Oct 27 '17 at 20:57

1 Answers1

4

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:

  1. call UIImage's ciImage property, and if you get a non-nil value, use that.
  2. If ciImage is nil, then fall back on calling cgImage and making a CIImage from it if it is non-nil.
  3. If cgImage also happens to be nil, then something weird happened; in this case, you can throw an error or otherwise bail out gracefully.
Charles Srstka
  • 16,665
  • 3
  • 34
  • 60
  • It seems that UIImage's ciImage is non-nil and it calls the function and crashes the same way: if(image?.ciImage != nil) image = rotateHue(with:image!, rotatedByHue:hueDeg) – Chewie The Chorkie Oct 27 '17 at 21:21
  • 1
    @VagueExplanation That one will crash whenever `image` is nil. Don't use force-unwrap. Use `if let` or `guard let` or `??` instead. – Charles Srstka Oct 27 '17 at 21:24
  • 1
    @VagueExplanation Also, instead of testing `image?.ciImage != nil`, you should do something like `if let ciImage = image?.ciImage` and then use the resulting `CIImage`. I'd recommend changing `rotateHue`'s signature to take a `CIImage` instead of an `UIImage`, and then pass it the value you get from your `if let`. – Charles Srstka Oct 27 '17 at 21:25