Konvertieren von Bildern in Graustufen

Ich versuche, ein Bild auf folgende Weise in Graustufen zu konvertieren:

#define bytesPerPixel 4 #define bitsPerComponent 8 -(unsigned char*) getBytesForImage: (UIImage*)pImage { CGImageRef image = [pImage CGImage]; NSUInteger width = CGImageGetWidth(image); NSUInteger height = CGImageGetHeight(image); NSUInteger bytesPerRow = bytesPerPixel * width; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); unsigned char *rawData = malloc(height * width * 4); CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); CGColorSpaceRelease(colorSpace); CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); CGContextRelease(context); return rawData; } -(UIImage*) processImage: (UIImage*)pImage { DebugLog(@"processing image"); unsigned char *rawData = [self getBytesForImage: pImage]; NSUInteger width = pImage.size.width; NSUInteger height = pImage.size.height; DebugLog(@"width: %d", width); DebugLog(@"height: %d", height); NSUInteger bytesPerRow = bytesPerPixel * width; for (int xCoordinate = 0; xCoordinate < width; xCoordinate++) { for (int yCoordinate = 0; yCoordinate < height; yCoordinate++) { int byteIndex = (bytesPerRow * yCoordinate) + xCoordinate * bytesPerPixel; //Getting original colors float red = ( rawData[byteIndex] / 255.f ); float green = ( rawData[byteIndex + 1] / 255.f ); float blue = ( rawData[byteIndex + 2] / 255.f ); //Processing pixel data float averageColor = (red + green + blue) / 3.0f; red = averageColor; green = averageColor; blue = averageColor; //Assigning new color components rawData[byteIndex] = (unsigned char) red * 255; rawData[byteIndex + 1] = (unsigned char) green * 255; rawData[byteIndex + 2] = (unsigned char) blue * 255; } } NSData* newPixelData = [NSData dataWithBytes: rawData length: height * width * 4]; UIImage* newImage = [UIImage imageWithData: newPixelData]; free(rawData); DebugLog(@"image processed"); return newImage; } 

Wenn ich also ein Bild konvertieren will, rufe ich einfach processImage auf:

 imageToDisplay.image = [self processImage: image]; 

Aber imageToDisplay wird nicht angezeigt. Was könnte das Problem sein?

Vielen Dank.

   

Was genau passiert bei Verwendung dieser function? Gibt die function ein ungültiges Bild zurück oder zeigt das Display es nicht korrekt an?

Dies ist die Methode, die ich zur Konvertierung in Graustufen verwende.

 - (UIImage *) convertToGreyscale:(UIImage *)i { int kRed = 1; int kGreen = 2; int kBlue = 4; int colors = kGreen | kBlue | kRed; int m_width = i.size.width; int m_height = i.size.height; uint32_t *rgbImage = (uint32_t *) malloc(m_width * m_height * sizeof(uint32_t)); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(rgbImage, m_width, m_height, 8, m_width * 4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGContextSetInterpolationQuality(context, kCGInterpolationHigh); CGContextSetShouldAntialias(context, NO); CGContextDrawImage(context, CGRectMake(0, 0, m_width, m_height), [i CGImage]); CGContextRelease(context); CGColorSpaceRelease(colorSpace); // now convert to grayscale uint8_t *m_imageData = (uint8_t *) malloc(m_width * m_height); for(int y = 0; y < m_height; y++) { for(int x = 0; x < m_width; x++) { uint32_t rgbPixel=rgbImage[y*m_width+x]; uint32_t sum=0,count=0; if (colors & kRed) {sum += (rgbPixel>>24)&255; count++;} if (colors & kGreen) {sum += (rgbPixel>>16)&255; count++;} if (colors & kBlue) {sum += (rgbPixel>>8)&255; count++;} m_imageData[y*m_width+x]=sum/count; } } free(rgbImage); // convert from a gray scale image back into a UIImage uint8_t *result = (uint8_t *) calloc(m_width * m_height *sizeof(uint32_t), 1); // process the image back to rgb for(int i = 0; i < m_height * m_width; i++) { result[i*4]=0; int val=m_imageData[i]; result[i*4+1]=val; result[i*4+2]=val; result[i*4+3]=val; } // create a UIImage colorSpace = CGColorSpaceCreateDeviceRGB(); context = CGBitmapContextCreate(result, m_width, m_height, 8, m_width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast); CGImageRef image = CGBitmapContextCreateImage(context); CGContextRelease(context); CGColorSpaceRelease(colorSpace); UIImage *resultUIImage = [UIImage imageWithCGImage:image]; CGImageRelease(image); free(m_imageData); // make sure the data will be released by giving it to an autoreleased NSData [NSData dataWithBytesNoCopy:result length:m_width * m_height]; return resultUIImage; } 

Ich brauchte eine Version, die den Alphakanal erhalten hat, also habe ich den von Dutchie432 geposteten Code modifiziert:

 @implementation UIImage (grayscale) typedef enum { ALPHA = 0, BLUE = 1, GREEN = 2, RED = 3 } PIXELS; - (UIImage *)convertToGrayscale { CGSize size = [self size]; int width = size.width; int height = size.height; // the pixels will be painted to this array uint32_t *pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t)); // clear the pixels so any transparency is preserved memset(pixels, 0, width * height * sizeof(uint32_t)); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // create a context with RGBA pixels CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); // paint the bitmap to our context which will fill in the pixels array CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x]; // convert to grayscale using recommended method: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale uint32_t gray = 0.3 * rgbaPixel[RED] + 0.59 * rgbaPixel[GREEN] + 0.11 * rgbaPixel[BLUE]; // set the pixels to gray rgbaPixel[RED] = gray; rgbaPixel[GREEN] = gray; rgbaPixel[BLUE] = gray; } } // create a new CGImageRef from our context with the modified pixels CGImageRef image = CGBitmapContextCreateImage(context); // we're done with the context, color space, and pixels CGContextRelease(context); CGColorSpaceRelease(colorSpace); free(pixels); // make a new UIImage to return UIImage *resultUIImage = [UIImage imageWithCGImage:image]; // we're done with image now too CGImageRelease(image); return resultUIImage; } @end 

Hier ist ein Code, der nur UIKit und den Leuchtkraft-Mischmodus verwendet. Ein bisschen wie ein Hack, aber es funktioniert gut.

 // Transform the image in grayscale. - (UIImage*) grayishImage: (UIImage*) inputImage { // Create a graphic context. UIGraphicsBeginImageContextWithOptions(inputImage.size, YES, 1.0); CGRect imageRect = CGRectMake(0, 0, inputImage.size.width, inputImage.size.height); // Draw the image with the luminosity blend mode. // On top of a white background, this will give a black and white image. [inputImage drawInRect:imageRect blendMode:kCGBlendModeLuminosity alpha:1.0]; // Get the resulting image. UIImage *filteredImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return filteredImage; } 

Um die Transparenz UIGraphicsBeginImageContextWithOptions , können Sie den opaque UIGraphicsBeginImageContextWithOptions von UIGraphicsBeginImageContextWithOptions auf NO . Muss überprüft werden.

Basierend auf Cam-Code mit der Fähigkeit, mit der Skala für Retina-Displays umzugehen.

 - (UIImage *) toGrayscale { const int RED = 1; const int GREEN = 2; const int BLUE = 3; // Create image rectangle with current image width/height CGRect imageRect = CGRectMake(0, 0, self.size.width * self.scale, self.size.height * self.scale); int width = imageRect.size.width; int height = imageRect.size.height; // the pixels will be painted to this array uint32_t *pixels = (uint32_t *) malloc(width * height * sizeof(uint32_t)); // clear the pixels so any transparency is preserved memset(pixels, 0, width * height * sizeof(uint32_t)); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); // create a context with RGBA pixels CGContextRef context = CGBitmapContextCreate(pixels, width, height, 8, width * sizeof(uint32_t), colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); // paint the bitmap to our context which will fill in the pixels array CGContextDrawImage(context, CGRectMake(0, 0, width, height), [self CGImage]); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { uint8_t *rgbaPixel = (uint8_t *) &pixels[y * width + x]; // convert to grayscale using recommended method: http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale uint8_t gray = (uint8_t) ((30 * rgbaPixel[RED] + 59 * rgbaPixel[GREEN] + 11 * rgbaPixel[BLUE]) / 100); // set the pixels to gray rgbaPixel[RED] = gray; rgbaPixel[GREEN] = gray; rgbaPixel[BLUE] = gray; } } // create a new CGImageRef from our context with the modified pixels CGImageRef image = CGBitmapContextCreateImage(context); // we're done with the context, color space, and pixels CGContextRelease(context); CGColorSpaceRelease(colorSpace); free(pixels); // make a new UIImage to return UIImage *resultUIImage = [UIImage imageWithCGImage:image scale:self.scale orientation:UIImageOrientationUp]; // we're done with image now too CGImageRelease(image); return resultUIImage; } 

Ich mochte Mathieu Godarts Antwort, aber bei Retina oder Alpha-Bildern schien es nicht richtig zu funktionieren. Hier ist eine aktualisierte Version, die für beide bei mir funktioniert:

 - (UIImage*)convertToGrayscale { UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale); CGRect imageRect = CGRectMake(0.0f, 0.0f, self.size.width, self.size.height); CGContextRef ctx = UIGraphicsGetCurrentContext(); // Draw a white background CGContextSetRGBFillColor(ctx, 1.0f, 1.0f, 1.0f, 1.0f); CGContextFillRect(ctx, imageRect); // Draw the luminosity on top of the white background to get grayscale [self drawInRect:imageRect blendMode:kCGBlendModeLuminosity alpha:1.0f]; // Apply the source image's alpha [self drawInRect:imageRect blendMode:kCGBlendModeDestinationIn alpha:1.0f]; UIImage* grayscaleImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return grayscaleImage; } 

Unterschiedlicher Ansatz mit CIFilter. Erhält Alpha-Kanal und arbeitet mit transparentem Hintergrund:

 + (UIImage *)convertImageToGrayScale:(UIImage *)image { CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage]; CIContext *context = [CIContext contextWithOptions:nil]; CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"]; [filter setValue:inputImage forKey:kCIInputImageKey]; [filter setValue:@(0.0) forKey:kCIInputSaturationKey]; CIImage *outputImage = filter.outputImage; CGImageRef cgImageRef = [context createCGImage:outputImage fromRect:outputImage.extent]; UIImage *result = [UIImage imageWithCGImage:cgImageRef]; CGImageRelease(cgImageRef); return result; } 

Eine schnelle Erweiterung auf UIImage , Alpha erhalten:

 extension UIImage { private func convertToGrayScaleNoAlpha() -> CGImageRef { let colorSpace = CGColorSpaceCreateDeviceGray(); let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.None.rawValue) let context = CGBitmapContextCreate(nil, UInt(size.width), UInt(size.height), 8, 0, colorSpace, bitmapInfo) CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), self.CGImage) return CGBitmapContextCreateImage(context) } /** Return a new image in shades of gray + alpha */ func convertToGrayScale() -> UIImage { let bitmapInfo = CGBitmapInfo(CGImageAlphaInfo.Only.rawValue) let context = CGBitmapContextCreate(nil, UInt(size.width), UInt(size.height), 8, 0, nil, bitmapInfo) CGContextDrawImage(context, CGRectMake(0, 0, size.width, size.height), self.CGImage); let mask = CGBitmapContextCreateImage(context) return UIImage(CGImage: CGImageCreateWithMask(convertToGrayScaleNoAlpha(), mask), scale: scale, orientation:imageOrientation)! } } 

Hier ist eine weitere gute Lösung als Kategoriemethode auf UIImage. Es basiert auf diesem Blogpost und seinen Kommentaren. Aber ich habe hier ein Speicherproblem behoben:

 - (UIImage *)grayScaleImage { // Create image rectangle with current image width/height CGRect imageRect = CGRectMake(0, 0, self.size.width * self.scale, self.size.height * self.scale); // Grayscale color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); // Create bitmap content with current image size and grayscale colorspace CGContextRef context = CGBitmapContextCreate(nil, self.size.width * self.scale, self.size.height * self.scale, 8, 0, colorSpace, kCGImageAlphaNone); // Draw image into current context, with specified rectangle // using previously defined context (with grayscale colorspace) CGContextDrawImage(context, imageRect, [self CGImage]); // Create bitmap image info from pixel data in current context CGImageRef grayImage = CGBitmapContextCreateImage(context); // release the colorspace and graphics context CGColorSpaceRelease(colorSpace); CGContextRelease(context); // make a new alpha-only graphics context context = CGBitmapContextCreate(nil, self.size.width * self.scale, self.size.height * self.scale, 8, 0, nil, kCGImageAlphaOnly); // draw image into context with no colorspace CGContextDrawImage(context, imageRect, [self CGImage]); // create alpha bitmap mask from current context CGImageRef mask = CGBitmapContextCreateImage(context); // release graphics context CGContextRelease(context); // make UIImage from grayscale image with alpha mask CGImageRef cgImage = CGImageCreateWithMask(grayImage, mask); UIImage *grayScaleImage = [UIImage imageWithCGImage:cgImage scale:self.scale orientation:self.imageOrientation]; // release the CG images CGImageRelease(cgImage); CGImageRelease(grayImage); CGImageRelease(mask); // return the new grayscale image return grayScaleImage; } 

Eine schnelle und effiziente Swift 3-Implementierung für iOS 9/10. Ich halte das für effizient, nachdem ich jetzt jede Bildfiltermethode ausprobiert habe, die ich für die Verarbeitung von mehreren Bildern gleichzeitig finden könnte (beim Herunterladen mit der ImageFilter-Option von AlamofireImage). Ich entschied mich für diese Methode als FAR besser als jede andere, die ich (für meinen Anwendungsfall) in Bezug auf Speicher und Geschwindigkeit versuchte.

 func convertToGrayscale() -> UIImage? { UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) let imageRect = CGRect(x: 0.0, y: 0.0, width: self.size.width, height: self.size.height) let context = UIGraphicsGetCurrentContext() // Draw a white background context!.setFillColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0) context!.fill(imageRect) // optional: increase contrast with colorDodge before applying luminosity // (my images were too dark when using just luminosity - you may not need this) self.draw(in: imageRect, blendMode: CGBlendMode.colorDodge, alpha: 0.7) // Draw the luminosity on top of the white background to get grayscale of original image self.draw(in: imageRect, blendMode: CGBlendMode.luminosity, alpha: 0.90) // optional: re-apply alpha if your image has transparency - based on user1978534's answer (I haven't tested this as I didn't have transparency - I just know this would be the the syntax) // self.draw(in: imageRect, blendMode: CGBlendMode.destinationIn, alpha: 1.0) let grayscaleImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return grayscaleImage } 

Die Verwendung von colorDodge: Ich hatte anfangs Probleme, meine Bilder so hell zu machen, dass sie mit der Graustufenfärbung übereinstimmten, die mit CIFilter (“CIPhotoEffectTonal”) erzeugt wurde – meine Ergebnisse waren zu dunkel. Ich konnte mit CGBlendMode.colorDodge @ ~ 0.7 alpha eine ordentliche Übereinstimmung erzielen, was den Gesamtkontrast zu erhöhen scheint.

Andere Farbmischungseffekte funktionieren möglicherweise auch – aber ich denke, dass Sie vor der Helligkeit anwenden möchten, die der Graustufenfiltereffekt ist. Ich fand diese Seite sehr hilfreich, um auf die verschiedenen BlendModes zu verweisen .

Re Effizienzgewinne, die ich gefunden habe: Ich muss Hunderte von Miniaturbildern verarbeiten, wenn sie von einem Server geladen werden (mit AlamofireImage für asynchronen Laden, Zwischenspeichern und Anwenden eines Filters). Ich fing an, Abstürze zu erleben, wenn die Gesamtgröße meiner Bilder die Cachegröße überschritt, also experimentierte ich mit anderen Methoden.

Der CoreImage-CPU-basierte CIFilter-Ansatz war der erste, den ich ausprobierte, und er war nicht speichereffizient genug für die Anzahl der Bilder, die ich behandle.

Ich habe auch versucht, einen CIFilter über die GPU unter Verwendung von EAGLContext(api: .openGLES3) , der tatsächlich noch speicherintensiver war – ich bekam tatsächlich Speicherwarnungen für 450+ mb Verwendung beim Laden von 200 + Bildern.

Ich habe versucht, Bitmap-Verarbeitung (dh CGContext(data: nil, width: width, height: height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.none.rawValue) … was gut funktionierte, außer ich konnte nicht context.scaleBy(x: scaleFactor, y: scaleFactor) Für ein modernes Retina-Gerät ist die Auflösung hoch genug, selbst wenn ich context.scaleBy(x: scaleFactor, y: scaleFactor) hinzugefügt context.scaleBy(x: scaleFactor, y: scaleFactor) .

Also, von allem, was ich ausprobiert habe, ist diese Methode (UIGraphics Context Draw) bei der Anwendung als Filter auf AlamofireImage wesentlich effizienter bei Geschwindigkeit und Speicher. Wie in, sehen weniger als 70 MB RAM bei der Verarbeitung meiner mehr als 200 Bilder und sie grundsätzlich im Laden sofort, anstatt über etwa 35 Sekunden dauerte es mit den OpenEAGL-Methoden. Ich weiß, dass dies keine sehr wissenschaftlichen Benchmarks sind. Ich werde es instrumentieren, wenn jemand sehr neugierig ist 🙂

Und schließlich, wenn Sie diesen oder einen anderen Graustufenfilter in AlamofireImage übergeben müssen – so geht es: (Sie müssen AlamofireImage in Ihre class importieren, um ImageFilter zu verwenden)

 public struct GrayScaleFilter: ImageFilter { public init() { } public var filter: (UIImage) -> UIImage { return { image in return image.convertToGrayscale() ?? image } } } 

Um es zu verwenden, erstellen Sie den Filter wie folgt und übergeben Sie ihn in af_setImage wie folgt:

 let filter = GrayScaleFilter() imageView.af_setImage(withURL: url, filter: filter) 
 @interface UIImageView (Settings) - (void)convertImageToGrayScale; @end @implementation UIImageView (Settings) - (void)convertImageToGrayScale { // Create image rectangle with current image width/height CGRect imageRect = CGRectMake(0, 0, self.image.size.width, self.image.size.height); // Grayscale color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); // Create bitmap content with current image size and grayscale colorspace CGContextRef context = CGBitmapContextCreate(nil, self.image.size.width, self.image.size.height, 8, 0, colorSpace, kCGImageAlphaNone); // Draw image into current context, with specified rectangle // using previously defined context (with grayscale colorspace) CGContextDrawImage(context, imageRect, [self.image CGImage]); // Create bitmap image info from pixel data in current context CGImageRef imageRef = CGBitmapContextCreateImage(context); // Create a new UIImage object UIImage *newImage = [UIImage imageWithCGImage:imageRef]; // Release colorspace, context and bitmap information CGColorSpaceRelease(colorSpace); CGContextRelease(context); CFRelease(imageRef); // Return the new grayscale image self.image = newImage; } @end 

Ich habe noch eine andere Antwort. Dieser ist extrem performant und verarbeitet Retina-Grafiken sowie Transparenz. Es erweitert Sargis Gevorgyan Ansatz:

 + (UIImage*) grayScaleFromImage:(UIImage*)image opaque:(BOOL)opaque { // NSTimeInterval start = [NSDate timeIntervalSinceReferenceDate]; CGSize size = image.size; CGRect bounds = CGRectMake(0, 0, size.width, size.height); // Create bitmap content with current image size and grayscale colorspace CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); size_t bitsPerComponent = 8; size_t bytesPerPixel = opaque ? 1 : 2; size_t bytesPerRow = bytesPerPixel * size.width * image.scale; CGContextRef context = CGBitmapContextCreate(nil, size.width, size.height, bitsPerComponent, bytesPerRow, colorSpace, opaque ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast); // create image from bitmap CGContextDrawImage(context, bounds, image.CGImage); CGImageRef cgImage = CGBitmapContextCreateImage(context); UIImage* result = [[UIImage alloc] initWithCGImage:cgImage scale:image.scale orientation:UIImageOrientationUp]; CGImageRelease(cgImage); CGContextRelease(context); // performance results on iPhone 6S+ in Release mode. // Results are in photo pixels, not device pixels: // ~ 5ms for 500px x 600px // ~ 15ms for 2200px x 600px // NSLog(@"generating %dx %d @ %dx grayscale took %f seconds", (int)size.width, (int)size.height, (int)image.scale, [NSDate timeIntervalSinceReferenceDate] - start); return result; } 

Die Verwendung von Füllmethoden ist elegant, aber das Kopieren in eine Graustufen-Bitmap ist leistungsfähiger, da Sie nur einen oder zwei Farbkanäle anstelle von vier verwenden. Die Deckkraft ist dazu gedacht, die undurchsichtige Flagge Ihrer UIView aufzunehmen, so dass Sie keinen Alphakanal verwenden können, wenn Sie wissen, dass Sie keinen benötigen.

Ich habe die Core-Image-basierten Lösungen in diesem Antwort-Thread nicht ausprobiert, aber ich wäre sehr vorsichtig bei der Verwendung von Core Image, wenn die performance wichtig ist.

Das ist mein Versuch, schnell zu konvertieren, indem ich direkt in den Graustufenfarbraum ohne jede Pixelaufzählung zeichne. Es arbeitet 10x schneller als CIImageFilter Lösungen.

 @implementation UIImage (Grayscale) static UIImage *grayscaleImageFromCIImage(CIImage *image, CGFloat scale) { CIImage *blackAndWhite = [CIFilter filterWithName:@"CIColorControls" keysAndValues:kCIInputImageKey, image, kCIInputBrightnessKey, @0.0, kCIInputContrastKey, @1.1, kCIInputSaturationKey, @0.0, nil].outputImage; CIImage *output = [CIFilter filterWithName:@"CIExposureAdjust" keysAndValues:kCIInputImageKey, blackAndWhite, kCIInputEVKey, @0.7, nil].outputImage; CGImageRef ref = [[CIContext contextWithOptions:nil] createCGImage:output fromRect:output.extent]; UIImage *result = [UIImage imageWithCGImage:ref scale:scale orientation:UIImageOrientationUp]; CGImageRelease(ref); return result; } static UIImage *grayscaleImageFromCGImage(CGImageRef imageRef, CGFloat scale) { NSInteger width = CGImageGetWidth(imageRef) * scale; NSInteger height = CGImageGetHeight(imageRef) * scale; NSMutableData *pixels = [NSMutableData dataWithLength:width*height]; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); CGContextRef context = CGBitmapContextCreate(pixels.mutableBytes, width, height, 8, width, colorSpace, 0); CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); CGImageRef ref = CGBitmapContextCreateImage(context); UIImage *result = [UIImage imageWithCGImage:ref scale:scale orientation:UIImageOrientationUp]; CGContextRelease(context); CGColorSpaceRelease(colorSpace); CGImageRelease(ref); return result; } - (UIImage *)grayscaleImage { if (self.CIImage) { return grayscaleImageFromCIImage(self.CIImage, self.scale); } else if (self.CGImage) { return grayscaleImageFromCGImage(self.CGImage, self.scale); } return nil; } @end