12

Thanks for looking.

Here's my code

 CIImage *result = _vignette.outputImage;
self.mainImageView.image = nil;
//self.mainImageView.contentMode = UIViewContentModeScaleAspectFit;
self.mainImageView.image = [UIImage imageWithCIImage:result];
self.mainImageView.contentMode = UIViewContentModeScaleAspectFit;

in here _vignette is correctly set up filter and image effect is applying to the image correctly.

I'm using a source image with resolution 500x375. My imageView has almost iPhone screen's resolution. So to avoid stretching I'm using AspectFit.

But after applying effect when I'm assigning the result image back to my imageView it streches. No matter which UIViewContentMode I use. It doesn't work. It seems it always applies ScaleToFill regardless the filter I've given.

Any idea why is this happening? Any suggestion is highly appreciated.

Rukshan
  • 7,902
  • 6
  • 43
  • 61
  • Basically duplicates http://stackoverflow.com/questions/9094058/creating-uiimage-from-ciimage – matt Apr 08 '13 at 18:38

3 Answers3

24

(1) Aspect Fit does stretch the image - to fit. If you don't want the image stretched at all, use Center (for example).

(2) imageWithCIImage gives you a very weird beast, a UIImage not based on CGImage, and so not susceptible to the normal rules of layer display. It is really nothing but a thin wrapper around CIImage, which is not what you want. You must convert (render) the CIFilter output thru CGImage to UIImage, thus giving you a UIImage that actually has some bits (CGImage, a bitmap). My discussion here gives you code that demonstrates:

http://www.apeth.com/iOSBook/ch15.html#_cifilter_and_ciimage

In other words, at some point you must call CIContext createCGImage:fromRect: to generate a CGImageRef from the output of your CIFilter, and pass that on into a UIImage. Until you do that, you don't have the output of your filter operations as a real UIImage.

Alternatively, you can draw the image from imageWithCIImage into a graphics context. For example, you can draw it into an image graphics context and then use that image.

What you can't do is display the image from imageWithCIImage directly. That's because it isn't an image! It has no underlying bitmap (CGImage). There's no there there. All it is is a set of CIFilter instructions for deriving the image.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • thanks! I only want to keep the aspect ratio, stretching is ok,while keeping the aspect ratio. I edited my question. It was not `aspectFit` always been applying. It was `saceltoFill`. No matter which content mode I set, UI won't update. It stays with scaleToFill. But when I check the property after I set aspectFit, it's correctly set (last code line). But ImageView hasn't updated. Any idea why? I simply want to load an image to a imageView and apply a CIFilter without changing it's aspect ratio. My requirement is simple as that. But my source image can be different resolution and ratios – Rukshan Apr 09 '13 at 04:41
  • 2
    I keep telling you, but you seem not to believe me. Setting the image view's image to a UIImage created with `imageWithCIImage:` is not going to work. – matt Apr 09 '13 at 04:45
  • You were right :) I just tested this with the method you mentioned. worked like a charm. Thanks a lot! – Rukshan Apr 09 '13 at 04:58
  • I'm glad it worked but please note that I've been saying the same thing to you over and over all day long - literally for something like 12 hours and all you did is say no no no. It's important to *listen* to advice you're given and not just write it off merely because it involves work or thought. – matt Apr 09 '13 at 05:00
  • Understood :) actually I saw above method in a apple keynote video and they said "Shortcut: UIImage has built-in support for CIImage". because of that I thought issue is not with CIImage. sorry about that. – Rukshan Apr 09 '13 at 05:16
  • The line of code to go from CIImage to CGImage is: `CGImageRef cgimageref = [[CIContext contextWithOptions:nil] createCGImage:ciimage fromRect:[ciimage extent]];` – Wex Jun 27 '14 at 15:43
  • Have anybody heard a word from Apple about that initWithCIImage: workaround? It's very confusing. – Krzysztof Przygoda Feb 26 '15 at 18:58
  • There's nothing to work around, @KrzysztofPrzygoda - it works exactly as it is supposed to. – matt Feb 26 '15 at 19:28
2

This is an answer for Swift, inspired by this question and the solution by user1951992.

let imageView = UIImageView(frame: CGRect(x: 100, y: 200, width: 100, height: 50))
imageView.contentMode = .scaleAspectFit

//just an example for a CIFilter
//NOTE: we're using implicit unwrapping here because we're sure there is a filter
//(and an image) named exactly this
let filter = CIFilter(name: "CISepiaTone")!
let image = UIImage(named: "image")!
filter.setValue(CIImage(image: image), forKey: kCIInputImageKey)
filter.setValue(0.5, forKey: kCIInputIntensityKey)

guard let outputCGImage = filter?.outputImage,
    let outputImage = CIContext().createCGImage(outputCGImage, from: outputCGImage.extent) else {
        return
}

imageView.image = UIImage(cgImage: outputImage)
Tamás Sengel
  • 55,884
  • 29
  • 169
  • 223
1

I just spent all day on this. I was getting orientation problems followed by poor quality output.

After snapping an image using the camera, I do this.

NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
                UIImage *image = [[UIImage alloc] initWithData:imageData];

This causes a rotation problem so I needed to do this to it.

UIImage *scaleAndRotateImage(UIImage *image)
{
    int kMaxResolution = image.size.height; // Or whatever

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);

    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();  

    return imageCopy;  
}

Then when I apply my filter, I do this (with special thanks to this thread - specifically matt and Wex)

    -(UIImage*)processImage:(UIImage*)image {

            CIImage *inImage = [CIImage imageWithCGImage:image.CGImage];

            CIFilter *filter = [CIFilter filterWithName:@"CIColorControls" keysAndValues:
                        kCIInputImageKey, inImage,
                        @"inputContrast", [NSNumber numberWithFloat:1.0],
                        nil];

    UIImage *outImage = [filter outputImage];

//Juicy bit
            CGImageRef cgimageref = [[CIContext contextWithOptions:nil] createCGImage:outImage fromRect:[outImage extent]];

            return [UIImage imageWithCGImage:cgimageref];
        }