English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Método para quitar el fondo de color de una imagen en iOS

Escena de proyecto real: Eliminar el fondo blanco puro de la imagen para obtener una imagen de fondo transparente para la función de composición de imágenes

Se presentan tres métodos de procesamiento de dos vías (no sé por qué me acordé de Kong Yiji), no se compararon las especificaciones específicas, si algún maestro puede informar, estaré muy agradecido.

Core Image Core Graphics/Quarz 2D Core Image

Core Image es un marco muy potente. Te permite aplicar fácilmente varios filtros para procesar imágenes, como modificar la intensidad, el color o la exposición. Utiliza GPU (o CPU) para procesar datos de imágenes y cuadros de video muy rápidamente, incluso en tiempo real. Y oculta todos los detalles de procesamiento gráfico subyacente, permitiendo su uso simple a través de la API proporcionada, sin necesidad de preocuparse por cómo OpenGL o OpenGL ES utilizan al máximo las capacidades de GPU, ni de saber cómo GCD juega su papel, Core Image maneja todos los detalles.

en la documentación oficial de AppleGuía de Programación de Core Imagese mencionóReceta de Filtro de Chroma KeyPara el ejemplo de procesamiento de fondo

Se utiliza el modelo de color HSV, ya que el modelo HSV es más amigable para la representación del rango de color en comparación con RGB.

Proceso general del proceso:

Se crea un mapeo cúbico de cuboMap para eliminar el rango de valores de color, se establece el Alpha del color objetivo en 0.0f, se utiliza el filtro CIColorCube y el cubeMap para procesar el color de la imagen de origen, se obtiene el objeto CIImage procesado por CIColorCube y se convierte en el objeto CGImageRef de Core Graphics a través de imageWithCGImage: para obtener la imagen de resultado

Atención: En el tercer paso, no se puede usar directamente imageWithCIImage:, porque lo obtenido no es un UIImage estándar, y si se utiliza directamente, puede aparecer el problema de que no se muestra.

- (UIImage *)removeColorWithMinHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle image:(UIImage *)originalImage{
 CIImage *image = [CIImage imageWithCGImage:originalImage.CGImage];
 CIContext *context = [CIContext contextWithOptions:nil];// kCIContextUseSoftwareRenderer : CPURender
 /** Note
 * The UIImage initialized through CIimage is not a standard UIImage like CGImage
 * So if you don't use context for rendering processing, it is impossible to display normally
 */
 CIImage *renderBgImage = [self outputImageWithOriginalCIImage:image minHueAngle:minHueAngle maxHueAngle:maxHueAngle];
 CGImageRef renderImg = [context createCGImage:renderBgImage fromRect:image.extent];
 UIImage *renderImage = [UIImage imageWithCGImage:renderImg];
 return renderImage;
}
struct CubeMap {
 int length;
 float dimension;
 float *data;
};
- (CIImage *)outputImageWithOriginalCIImage:(CIImage *)originalImage minHueAngle:(float)minHueAngle maxHueAngle:(float)maxHueAngle{
 struct CubeMap map = createCubeMap(minHueAngle, maxHueAngle);
 const unsigned int tamaño = 64;
 // Create memory with the cube data
 NSData *data = [NSData dataWithBytesNoCopy:map.data
   length:map.length
   freeWhenDone:YES];
 CIFilter *colorCube = [CIFilter filterWithName:@"CIColorCube"];
 [colorCube setValue:@(size) forKey:@"inputCubeDimension"];
 // Establecer datos para el cubo
 [colorCube setValue:data forKey:@"inputCubeData"];
 [colorCube setValue:originalImage forKey:kCIInputImageKey];
 CIImage *resultado = [colorCube valueForKey:kCIOutputImageKey];
 return resultado;
}
struct CubeMap createCubeMap(float minHueAngle, float maxHueAngle) {
 const unsigned int tamaño = 64;
 struct CubeMap map;
 longitudDeMapa = tamaño; * tamaño * tamaño * tamañoDeByteDeFloat * 4;
 dimensiónDeMapa = tamaño;
 float *cubeData = (float *)malloc (longitudDeMapa);
 float rgb[3], hsv[3], *c = cubeData;
 for (int z = 0; z < tamaño; z++{
 rgb[2] = ((double)z)/(tamaño-1); // Valor azul
 for (int y = 0; y < tamaño; y++{
 rgb[1] = ((double)y)/(tamaño-1); // Valor verde
 for (int x = 0; x < tamaño; x ++{
 rgb[0] = ((double)x)/(tamaño-1); // Valor rojo
 rgbToHSV(rgb,hsv);
 // Usa el valor de tono para determinar cuál hacer transparente
 // El ángulo de tono mínimo y máximo depende de
 // el color que deseas eliminar
 float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) &63; 0.0f: 1.0f;
 // Calcular valores de alfa premultiplicados para el cubo
 c[0] = rgb[0] * alpha;
 c[1] = rgb[1] * alpha;
 c[2] = rgb[2] * alpha;
 c[3] = alpha;
 c += 4; // avanzar nuestro puntero en la memoria para el siguiente valor de color
 }
 }
 }
 map.data = cubeData;
 return map;
}

rgbToHSV no se menciona en el documento oficial, el autor encontró el proceso de conversión relacionado en el blog de los expertos mencionados a continuación. Gracias

void rgbToHSV(float *rgb, float *hsv) {
 float min, max, delta;
 float r = rgb[0], g = rgb[1], b = rgb[2];
 float *h = hsv, *s = hsv + 1, *v = hsv + 2;
 min = fmin(fmin(r, g), b );
 max = fmax(fmax(r, g), b );
 *v = max;
 delta = max - min;
 if( max != 0 )
 *s = delta / max;
 else {
 *s = 0;
 *h = -1;
 return;
 }
 if( r == max )
 *h = ( g - b ) / delta;
 else if( g == max )
 *h = 2 + ( b - r ) / delta;
 else
 *h = 4 + ( r - g ) / delta;
 *h *= 60;
 if( *h < 0 )
 *h += 360;
}

A continuación, probemos el efecto de eliminar el fondo verde

Podemos usarHerramienta HSV, determine el rango aproximado del valor de HUE verde50-170

Llame a este método para probar

[[SPImageChromaFilterManager sharedManager] removeColorWithMinHueAngle:50 maxHueAngle:170 image:[UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"nb" ofType:@"jpeg"]]]

El efecto

El efecto parece bastante bueno.

Si observan atentamente el modelo HSV, tal vez se darán cuenta de que mediante la especificación del ángulo de tono (Hue), no podemos actuar sobre el gris, blanco y negro. Tenemos que usar la saturación (Saturation) y la luminosidad (Value) para juzgar conjuntamente, los estudiantes interesados pueden encontrarlo en el códigojuzgar Alpha float alpha = (hsv[0] > minHueAngle && hsv[0] < maxHueAngle) &63; 0.0f: 1.0f;pruebe el efecto allí. (Respecto a por qué RGB y HSV se convierten así en el código, por favor, busque en Baidu sus conversiones, ya que el autor novato tampoco lo entiende. ¡Ah, el autor novato no tiene vida!)

Si están interesados en Core Image, por favor, diríjase a la serie de artículos del experto

iOS8 Core Image In Swift: mejora automática de imágenes y uso de filtros integrados

iOS8 Core Image In Swift: filtros más complejos

iOS8 Core Image In Swift: detección de rostros y máscara

iOS8 Core Image In Swift: filtros en tiempo real de video

Core Graphics/Quarz 2D

Como se mencionó anteriormente, Core Image basado en OpenGL es muy potente, como otro pilar de la vistaCore Graphicstambién muy poderoso. Su exploración ha permitido que el autor novato tenga una mayor comprensión de los conocimientos relacionados con las imágenes. Por lo tanto, aquí se realiza un resumen para futuras consultas.

Si no están interesados en la exploración, por favor, salten directamente a la parte final del artículo Masking an Image with Color

Bitmap


en Quarz 2En el documento oficial de D, paraBitMap tiene la siguiente descripción:

Una imagen de mapa de bits (o imagen muestreada) es un array de píxeles (o muestras). Cada píxel representa un único punto en la imagen. Los archivos gráficos JPEG, TIFF y PNG son ejemplos de imágenes de mapa de bits.

32-y 16-formatos de pixel de 32 bits para los espacios de color CMYK y RGB en Quartz 2D

Volviendo a nuestras necesidades, para eliminar el color específico de la imagen, si podemos leer la información RGBA en cada píxel, determinar sus valores, si coinciden con el rango objetivo, cambiaremos su valor Alpha a 0 y lo saldremos como una nueva imagen, de esta manera hemos implementado una forma similar al tratamiento mencionado en el texto anterior cubeMap.

fuerte Quarz 2D nos proporciona la capacidad de realizar esta operación, a continuación, por favor, vea el ejemplo de código:

- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{
 // Asignar memoria}}
 const int imageWidth = image.size.width;
 const int imageHeight = image.size.height;
 size_t bytesPerRow = imageWidth * 4;
 uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
 // Crear context
 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();// Contenedor del rango de colores
 CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
 CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
 // Recorrer los píxeles
 int pixelNum = imageWidth * imageHeight;
 uint32_t* pCurPtr = rgbImageBuf;
 for (int i = 0; i < pixelNum; i++, pCurPtr++)
 {
 uint8_t* ptr = (uint8_t*)pCurPtr;
 if (ptr[3] >= minR && ptr[3] <= maxR &&
 ptr[2] >= minG && ptr[2] <= maxG &&
 ptr[1] >= minB && ptr[1] <= maxB) {
 ptr[0] = 0;
 }else{
 printf("\n---->ptr0:%d ptr1:%d ptr2:%d ptr3:%d<----\n",ptr[0],ptr[1], ptr[2], ptr[3]);
 }
 }
 // Convertir la memoria en imagen
 CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, nil);
 CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight,8, 32, bytesPerRow, colorSpace,kCGImageAlphaLast |kCGBitmapByteOrder32Little, dataProvider,NULL,true,kCGRenderingIntentDefault);
 CGDataProviderRelease(dataProvider);
 UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef]; 
 // liberar
 CGImageRelease(imageRef);
 CGContextRelease(context);
 CGColorSpaceRelease(colorSpace);
 return resultUIImage;
}

¿Recuerda los defectos del modo HSV mencionados en Core Image? Entonces, Quarz 2D es directamente utilizar la información de RGBA para procesar, evitando muy bien el problema de que no es amigable con el negro y el blanco, solo necesitamos configurar el rango de RGB (porque el negro y el blanco en el modo de color RGB se pueden determinar muy bien), podemos encapsularlo aproximadamente. Así

- (UIImage *)removeWhiteColorWithImage:(UIImage *)image{
 return [self removeColorWithMaxR:255 minR:250 maxG:255 minG:240 maxB:255 minB:240 image:image];
}
- (UIImage *)removeBlackColorWithImage:(UIImage *)image{
 return [self removeColorWithMaxR:15 minR:0 maxG:15 minG:0 maxB:15 minB:0 image:image];
}

Mire la comparación del efecto de procesamiento de fondo blanco que hemos hecho

Parece que está bien, pero para la ropa de gasa, no es muy amigable. Mire las pruebas de varios grupos de imágenes que hice

Claramente, si no es de fondo blanco, el efecto de 'vestido desgastado' es muy evidente. Este problema, en los tres métodos que intenté, no escapó a la ruleta, si algún gran maestro conoce un buen método y puede decirle a la pato, estaré muy agradecido. (Pongo aquí dos rodillas primero)

Además de los problemas mencionados anteriormente, el método de comparación de cada píxel, los valores leídos coinciden con el error que aparece al dibujar. Sin embargo, este error es prácticamente invisible a simple vista.


Como se muestra en la imagen siguiente, los valores RGB establecidos al dibujar son100/240/220 Pero al leer los valores mediante el procesamiento de CG anterior, los valores leídos son92/241/220. Comparando las imágenes "nuevas" y "actuales", prácticamente no se nota la diferencia en el color. Este pequeño problema, todos lo sabemos, no afecta mucho el efecto real de la eliminación de colores

Enmascarar una Imagen con Color

Después de intentar entender y usar el método anterior, al volver a leer el documento, descubrí este método, que es como encontrar un regalo de la mano de Father Apple. Aquí va el código

- (UIImage *)removeColorWithMaxR:(float)maxR minR:(float)minR maxG:(float)maxG minG:(float)minG maxB:(float)maxB minB:(float)minB image:(UIImage *)image{
 const CGFloat myMaskingColors[6= {minR, maxR, minG, maxG, minB, maxB};
 CGImageRef ref = CGImageCreateWithMaskingColors(image.CGImage, myMaskingColors);
 return [UIImage imageWithCGImage:ref];
}

Pulse aquí para la documentación oficial

Resumen

El modo de color HSV es más beneficioso que el modo RGB para eliminar colores de las imágenes, mientras que RGB es exactamente lo contrario. Dado que solo necesito eliminar el fondo blanco en mi proyecto, finalmente opté por la última manera.

Te gustará