objective c - permite - ¿Cómo accedo y manipulo píxeles de imágenes JPEG?
formatos de imagen wikipedia (1)
tengo un archivo jpg Necesito convertirlo en datos de píxeles y luego cambiar el color de algunos píxeles. Lo hago así:
NSString *string = [[NSBundle mainBundle] pathForResource:@"pic" ofType:@"jpg"];
NSData *data = [NSData dataWithContentsOfFile:string];
unsigned char *bytesArray = dataI.bytes;
NSUInteger byteslenght = data.length;
//--------pixel to array
NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:byteslenght];
for (int i = 0; i<byteslenght; i++) {
[array addObject:[NSNumber numberWithUnsignedChar:bytesArray[i]]];
}
Aquí trato de cambiar el color de los píxeles desde 95 hasta 154.
NSNumber *number = [NSNumber numberWithInt:200];
for (int i=95; i<155; i++) {
[array replaceObjectAtIndex:i withObject:number];
}
Pero cuando convierto matriz a imagen, tengo una imagen borrosa. No entiendo por qué no influyo en algunos píxeles y por qué influyo en la imagen en total.
El proceso de acceder a los datos a nivel de píxel es un poco más complicado de lo que su pregunta podría sugerir, porque, como señaló Martin, JPEG puede ser un formato de imagen comprimida. Apple analiza la técnica aprobada para obtener datos de píxeles en la Q & A Q QA1 técnica .
En pocas palabras, para obtener los datos de píxeles descomprimidos para un UIImage
, usted:
Obtén la imagen
CGImage
para elUIImage
.Obtenga el proveedor de datos para ese
CGImageRef
través deCGImageGetDataProvider
.Obtenga los datos binarios asociados con ese proveedor de datos a través de
CGDataProviderCopyData
.Extraiga parte de la información sobre la imagen, para que sepa cómo interpretar ese búfer.
Así:
UIImage *image = ...
CGImageRef imageRef = image.CGImage; // get the CGImageRef
NSAssert(imageRef, @"Unable to get CGImageRef");
CGDataProviderRef provider = CGImageGetDataProvider(imageRef); // get the data provider
NSAssert(provider, @"Unable to get provider");
NSData *data = CFBridgingRelease(CGDataProviderCopyData(provider)); // get copy of the data
NSAssert(data, @"Unable to copy image data");
NSInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef); // some other interesting details about image
NSInteger bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
NSInteger bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
NSInteger bytesPerRow = CGImageGetBytesPerRow(imageRef);
NSInteger width = CGImageGetWidth(imageRef);
NSInteger height = CGImageGetHeight(imageRef);
CGColorSpaceRef colorspace = CGImageGetColorSpace(imageRef);
Dado que desea manipular esto, es de suponer que quiere un búfer de píxeles mutable. El enfoque más fácil sería hacer una mutableCopy
de ese objeto NSData
y manipularlo allí, pero en estos casos, tiendo a recurrir a C, creando un void *outputBuffer
, en el cual copio los píxeles originales y manipulo usando la matriz tradicional C técnicas.
Para crear el búfer:
void *outputBuffer = malloc(width * height * bitsPerPixel / 8);
NSAssert(outputBuffer, @"Unable to allocate buffer");
Para obtener los detalles precisos sobre cómo manipularlo, debe mirar bitmapInfo
(que le indicará si es RGBA o ARGB, si es un punto flotante o entero) y bitsPerComponent
(que le dirá si son 8 o 16 bits por componente, etc.). Por ejemplo, un formato JPEG muy común es de 8 bits por componente, cuatro componentes, RGBA (es decir, rojo, verde, azul y alfa, en ese orden). Pero realmente necesita verificar esas diversas propiedades que hemos extraído de CGImageRef
para asegurarse. Consulte la discusión en la Guía de programación en 2D de Quartz: imágenes de mapas de bits y máscaras de imágenes para obtener más información. Personalmente encuentro que la "Figura 11-2" es especialmente esclarecedora.
La siguiente pregunta lógica es cuando hayas terminado de manipular los datos de píxeles, cómo crear un UIImage
para eso. En resumen, invertirías el proceso anterior, por ejemplo, crear un proveedor de datos, crear un CGImageRef
y luego crear un UIImage
:
CGDataProviderRef outputProvider = CGDataProviderCreateWithData(NULL, outputBuffer, sizeof(outputBuffer), releaseData);
CGImageRef outputImageRef = CGImageCreate(width,
height,
bitsPerComponent,
bitsPerPixel,
bytesPerRow,
colorspace,
bitmapInfo,
outputProvider,
NULL,
NO,
kCGRenderingIntentDefault);
UIImage *outputImage = [UIImage imageWithCGImage:outputImageRef];
CGImageRelease(outputImageRef);
CGDataProviderRelease(outputProvider);
Donde releaseData
es una función C que simplemente llama al buffer de píxeles asociado con el proveedor de datos:
void releaseData(void *info, const void *data, size_t size)
{
free((void *)data);
}