Obtener un UIImage en blanco y negro (no en escala de grises)

Necesito get un UIImage en blanco y negro puro desde otro UIImage (no en escala de grises). ¿Alguien puede ayudarme?

Gracias por leer.

EDITADO:

Esta es la solución propuesta. Gracias a todos. Casi sé que esa no es la mejor manera de hacerlo, funciona bien.

// Gets an pure black and white image from an original image. - (UIImage *)pureBlackAndWhiteImage:(UIImage *)image { unsigned char *dataBitmap = [self bitmapFromImage:image]; for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) { if ((dataBitmap[i + 1] + dataBitmap[i + 2] + dataBitmap[i + 3]) < (255 * 3 / 2)) { dataBitmap[i + 1] = 0; dataBitmap[i + 2] = 0; dataBitmap[i + 3] = 0; } else { dataBitmap[i + 1] = 255; dataBitmap[i + 2] = 255; dataBitmap[i + 3] = 255; } } image = [self imageWithBits:dataBitmap withSize:image.size]; return image; } 

EDITADO 1:

En respuesta a los comentarios, aquí hay methods bitmapFromImage e imageWithBits .

 // Retrieves the bits from the context once the image has been drawn. - (unsigned char *)bitmapFromImage:(UIImage *)image { // Creates a bitmap from the given image. CGContextRef contex = CreateARGBBitmapContext(image.size); if (contex == NULL) { return NULL; } CGRect rect = CGRectMake(0.0f, 0.0f, image.size.width, image.size.height); CGContextDrawImage(contex, rect, image.CGImage); unsigned char *data = CGBitmapContextGetData(contex); CGContextRelease(contex); return data; } // Fills an image with bits. - (UIImage *)imageWithBits:(unsigned char *)bits withSize:(CGSize)size { // Creates a color space CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); if (colorSpace == NULL) { fprintf(stderr, "Error allocating color space\n"); free(bits); return nil; } CGContextRef context = CGBitmapContextCreate (bits, size.width, size.height, 8, size.width * 4, colorSpace, kCGImageAlphaPremultipliedFirst); if (context == NULL) { fprintf (stderr, "Error. Context not created\n"); free (bits); CGColorSpaceRelease(colorSpace ); return nil; } CGColorSpaceRelease(colorSpace ); CGImageRef ref = CGBitmapContextCreateImage(context); free(CGBitmapContextGetData(context)); CGContextRelease(context); UIImage *img = [UIImage imageWithCGImage:ref]; CFRelease(ref); return img; } 

Si lo que está buscando es limitar la image: todo lo más shiny que un cierto valor se vuelve blanco, todo lo oscuro se vuelve negro y elige el valor, entonces una biblioteca como la Imagen GPU funcionará para usted.

Este código puede ayudar:

 for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) { if (dataBitmap[i + 0] >= dataBitmap[i + 1] && dataBitmap[i + 0] >= dataBitmap[i + 2]){ dataBitmap[i + 1] = dataBitmap[i + 0]; dataBitmap[i + 2] = dataBitmap[i + 0]; } else if (dataBitmap[i + 1] >= dataBitmap[i + 0] && dataBitmap[i + 1] >= dataBitmap[i + 2]) { dataBitmap[i + 0] = dataBitmap[i + 1]; dataBitmap[i + 2] = dataBitmap[i + 1]; } else { dataBitmap[i + 0] = dataBitmap[i + 2]; dataBitmap[i + 1] = dataBitmap[i + 2]; } } 

Si bien puede ser una exageración para sus propósitos, hago precisamente eso para videos en vivo desde la camera del iPhone en mi aplicación de muestra aquí . Esa aplicación toma un color y una sensibilidad, y puede convertir todos los píxeles blancos que se encuentren dentro de ese umbral y, de ser posible, transparentes. Utilizo sombreadores progtwigbles OpenGL ES 2.0 para esto con el fin de get una respuesta en time real. Todo se describe en esta publicación aquí .

Una vez más, esto probablemente sea una exageración para lo que quieras. En el caso de un simple UIImage que desee convertir en blanco y negro, probablemente pueda leer en los píxeles sin procesar, repetir a través de ellos y aplicar el mismo tipo de umbral que hice para generar la image final. Esto no será tan rápido como el enfoque del sombreador, pero será mucho más simple de codificar.

El código funcionó bien para mí, solo necesito algo de ajuste … aquí hay algunos cambios que hice para que funcione correctamente asignando valor a dataBitmap[] array 's s zeroth index …

  for (int i = 0; i < image.size.width * image.size.height * 4; i += 4) { //here an index for zeroth element is assigned if ((dataBitmap[i + 0]+dataBitmap[i + 1] + dataBitmap[i + 2] + dataBitmap[i + 3]) < (255 * 4 / 2)) { // multiply four,instead of three dataBitmap[i + 0] = 0; dataBitmap[i + 1] = 0; dataBitmap[i + 2] = 0; dataBitmap[i + 3] = 0; } else { dataBitmap[i + 0] = 255; dataBitmap[i + 1] = 255; dataBitmap[i + 2] = 255; dataBitmap[i + 3] = 255; } } 

Espero que funcione.

Aquí hay una solución rápida 3:

 class func pureBlackAndWhiteImage(_ inputImage: UIImage) -> UIImage? { guard let inputCGImage = inputImage.cgImage, let context = getImageContext(for: inputCGImage), let data = context.data else { return nil } let white = RGBA32(networking: 255, green: 255, blue: 255, alpha: 255) let black = RGBA32(networking: 0, green: 0, blue: 0, alpha: 255) let width = Int(inputCGImage.width) let height = Int(inputCGImage.height) let pixelBuffer = data.bindMemory(to: RGBA32.self, capacity: width * height) for x in 0 ..< height { for y in 0 ..< width { let offset = x * width + y if pixelBuffer[offset].networking > 0 || pixelBuffer[offset].green > 0 || pixelBuffer[offset].blue > 0 { pixelBuffer[offset] = black } else { pixelBuffer[offset] = white } } } let outputCGImage = context.makeImage() let outputImage = UIImage(cgImage: outputCGImage!, scale: inputImage.scale, orientation: inputImage.imageOrientation) return outputImage } class func getImageContext(for inputCGImage: CGImage) ->CGContext? { let colorSpace = CGColorSpaceCreateDeviceRGB() let width = inputCGImage.width let height = inputCGImage.height let bytesPerPixel = 4 let bitsPerComponent = 8 let bytesPerRow = bytesPerPixel * width let bitmapInfo = RGBA32.bitmapInfo guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else { print("unable to create context") return nil } context.setBlendMode(.copy) context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: CGFloat(width), height: CGFloat(height))) return context } struct RGBA32: Equatable { var color: UInt32 var networking: UInt8 { return UInt8((color >> 24) & 255) } var green: UInt8 { return UInt8((color >> 16) & 255) } var blue: UInt8 { return UInt8((color >> 8) & 255) } var alpha: UInt8 { return UInt8((color >> 0) & 255) } init(networking: UInt8, green: UInt8, blue: UInt8, alpha: UInt8) { color = (UInt32(networking) << 24) | (UInt32(green) << 16) | (UInt32(blue) << 8) | (UInt32(alpha) << 0) } static let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue } func ==(lhs: RGBA32, rhs: RGBA32) -> Bool { return lhs.color == rhs.color } 

Con Swift 3, pude lograr este efecto utilizando CIFilters, primero aplicando CIPhotoEffectNoir (para hacerlo en escala de grises) y luego aplicando el filter CIColorControl con el parámetro de input kCIInputContrastKey establecido en un valor alto (es decir, 50). La configuration del parámetro kCIInputBrightnessKey también ajustará la intensidad del contraste en blanco y negro, negativo para una image más oscura y positivo para una image más shiny. Por ejemplo:

 extension UIImage { func toBlackAndWhite() -> UIImage? { guard let ciImage = CIImage(image: self) else { return nil } guard let grayImage = CIFilter(name: "CIPhotoEffectNoir", withInputParameters: [kCIInputImageKey: ciImage])?.outputImage else { return nil } let bAndWParams: [String: Any] = [kCIInputImageKey: grayImage, kCIInputContrastKey: 50.0, kCIInputBrightnessKey: 10.0] guard let bAndWImage = CIFilter(name: "CIColorControls", withInputParameters: bAndWParams)?.outputImage else { return nil } guard let cgImage = CIContext(options: nil).createCGImage(bAndWImage, from: bAndWImage.extent) else { return nil } return UIImage(cgImage: cgImage) } }