0

I'm generating a CIImage using a few chained filters and trying to output the generated image in the users photo album for certain debug purposes. The callback I supply to UIImageWriteToSavedPhotosAlbum() always has a nil error returned, so I assume nothing is going wrong. But the image never seems to show up.

I've used this function in the past to dump OpenGL buffers to the photo album for debugging, but I realize this isn't the same case. Should I be doing something differently?

-(void)cropAndSaveImage:(CIImage *)inputImage fromFeature:(CIFaceFeature *)feature
{
    // First crop out the face.
    [_cropFilter setValue:inputImage forKey:@"inputImage"];
    [_cropFilter setValue:[CIVector vectorWithCGRect:feature.bounds] forKey:@"inputRectangle"];
    CIImage * croppedImage = _cropFilter.outputImage;
    __block CIImage * outImage = croppedImage;

    dispatch_async(dispatch_get_main_queue(), ^{
        UIImage * outUIImage = [UIImage imageWithCIImage:outImage];
        UIImageWriteToSavedPhotosAlbum(outUIImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    });
}

-(void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    NSLog(@"debug LBP output face.  error: %@", error);
}

I've verified that the boundaries are never 0.

The callback output is always

debug LBP output face. error: (null)

Joey Carson
  • 2,973
  • 7
  • 36
  • 60
  • You need permissions to access the photos. [Permission to camera roll](http://stackoverflow.com/questions/13572220/ask-permission-to-access-camera-roll) – Anticro Nov 21 '15 at 23:06
  • No that's not it. Access to the camera roll is usually granted by the prompt the first time my code tries to access it. I figured it out the actual problem. It seems I needed to actually render the image and use a CGImageRef directly to instantiate the UIImage. Not sure why this was necessary, maybe a bug or change in compatibility in iOS 9? I've seen older posts suggesting the method I was using was right. See my answer below. – Joey Carson Nov 21 '15 at 23:11

2 Answers2

0

I figured this out on my own and deleted the question, but then I thought maybe someone will get some use out of it. I say this because I came across an older answer that suggested the original implementation worked. But in actuality in had to do the following to make it work properly.

     __block CIImage * outImage = _lbpFilter.outputImage;
dispatch_async(dispatch_get_main_queue(), ^{
    CGImageRef imgRef = [self renderCIImage:outImage];
    UIImage * uiImage = [UIImage imageWithCGImage:imgRef];

    UIImageWriteToSavedPhotosAlbum(uiImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
});

+ (CGImageRef)renderCIImage:(CIImage *)img
{
    if ( !m_ctx ) {
        NSDictionary * options = @{kCIContextOutputColorSpace:[NSNull null], kCIContextWorkingColorSpace:[NSNull null]};
        m_ctx = [CIContext contextWithOptions:options];
    }

    return [m_ctx createCGImage:img fromRect:img.extent];
}
Joey Carson
  • 2,973
  • 7
  • 36
  • 60
  • I don't see why this whole thing is not a duplicate of, say, this: http://stackoverflow.com/questions/15878060/setting-uiimageview-content-mode-after-applying-a-cifilter or this: http://stackoverflow.com/questions/30558516/image-rotating-after-cifilter and many others. The fact that `imageWithCIImage` doesn't make a bitmap-backed UIImage has been repeated here many, many times. I don't mean in any way to detract from your having solved the issue yourself, but I do think it's just a duplicate. – matt Nov 22 '15 at 01:17
  • I see now that this answer happens to be in another question, though I don't agree that it's a duplicate of that one specifically. I'm a novice in this area and it doesn't seem quite right for an API that is given an object wouldn't implicitly handle rendering it when writing to the output device (which happens to be the file system instead of the display). I think this is an easy to slip on nuance of UIImageWriteToSavedPhotosAlbum and UIImage. It has the ability to render it and there's no reason it wouldn't. So unless there's a dup of that exact question, I must disagree with you. – Joey Carson Nov 22 '15 at 01:31
  • Okay, I'm willing to unmark it as a duplicate. Nor have I any quarrel about the API, which is somewhat misleading. Nevertheless, I'm right too: the general fact you needed to know, namely that a CIImage-backed UIImage is not the same as a bitmap-backed UIImage and that you can't magically treat one type as the other type, is well documented on Stack Overflow and even, if you think about it, in Apple's documentation. – matt Nov 22 '15 at 02:32
  • I agree that you're correct as well, that it's an aspect I didn't understand. The UIImage passed could be any kind of UIImage, it seems silly to assume that it must be backed by a specifically created one, though it's the same interface. No matter how the UIImage argument is created, passing it down the pipeline and expecting it rendered and written to a file seems perfectly intuitive. Which is why when it doesn't happen, it's questionable. – Joey Carson Nov 22 '15 at 02:46
0

Using filter.outputImage to convert to CGImage by CIContext.createCGImage() and converting CGImage to UIImage will save image successfully.

Striezel
  • 3,693
  • 7
  • 23
  • 37