ios objective-c ios6 uiimage nsdata

Cómo reducir la escala de un UIImage en IOS por el tamaño de los datos



objective-c ios6 (5)

Además del tamaño máximo, también debe elegir un tamaño mínimo y decidir el rendimiento. Por ejemplo, puede verificar el tamaño de UIImageJPEGRepresentation(image, 1.0) . Si es demasiado grande, ¿lo verificas a 0.95 o 0.1 ?

Un posible enfoque es obtener el tamaño de UIImageJPEGRepresentation(image, 1.0) y ver en qué porcentaje es demasiado grande. Por ejemplo, digamos que es 600kB. A continuación, debe calcular 500.0 / 600 que es aproximadamente 0.83 . Entonces, haga UIImageJPEGRepresentation(image, 0.83) . Eso no dará exactamente 500kB pero puede estar lo suficientemente cerca.

Otro enfoque sería comenzar con UIImageJPEGRepresentation(image, 1.0) . Es demasiado grande, entonces haga UIImageJPEGRepresentation(image, 0.5) Si es demasiado grande, vaya con 0.25 pero si es demasiado pequeño, vaya con 0.75 . Siga dividiendo la diferencia hasta que esté dentro de un rango aceptable de su tamaño deseado.

Estoy buscando para reducir una UIImage en iOS .

He visto otras preguntas a continuación y su enfoque sobre cómo reducir la escala de la imagen por tamaño. Redimensionar Imágenes Objective-C
Cómo redimensionar la imagen programáticamente en object-c en iphone
¿La forma más sencilla de cambiar el tamaño de un UIImage?

Todas estas preguntas se basan en cambiar el tamaño de la imagen a un tamaño específico. En mi caso, estoy buscando redimensionar / reducir la escala de la imagen en base a un tamaño máximo.

Como ejemplo, me gustaría establecer un tamaño máximo de NSData que sea de 500 KB. Sé que puedo obtener el tamaño de la imagen así: // Verificar el tamaño de la imagen devuelta

NSData *imageData = UIImageJPEGRepresentation(image, 0.5); // Log out the image size NSLog(@"%lu KB",(imageData.length/1024));

Lo que me gustaría hacer es alguna forma de bucle aquí. Si el tamaño es mayor que el tamaño máximo que configuré, me gustaría reducir la imagen ligeramente y luego verificar el tamaño. Si el tamaño aún es demasiado grande, vuelva a bajar ligeramente y luego vuelva a verificarlo, hasta que sea inferior al tamaño máximo establecido.

No estoy seguro de cuál es el mejor enfoque para esto. Idealmente, no quiero reducir la imagen a un tamaño específico todo el tiempo, sino que solo reducir ligeramente la imagen cada vez. De esa manera puedo tener el mayor (tamaño w / h) de la imagen en sí y en su tamaño máximo (bytes). Si me reduzco ligeramente solo a la vez, ¿cuál sería la mejor manera de lograr esto?

EDITAR Para confirmar, busco cambiar el tamaño de la imagen real pero cambiar el tamaño de la imagen para que sea más pequeña que la longitud máxima de NSData . Por ejemplo:
NSData longitud de los datos de NSData
-Si está por encima del máximo, quiero pasar el UIImage a un método
-A continuación, recorra este método cambiando el tamaño del tamaño de la imagen cada vez
-Hasta que esté por debajo de la longitud máxima de NSData , ¿luego devolver la imagen?


En este momento, tienes una rutina que dice:

// Check if the image size is too large if ((imageData.length/1024) >= 1024) { while ((imageData.length/1024) >= 1024) { NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024))); // While the imageData is too large scale down the image // Get the current image size CGSize currentSize = CGSizeMake(image.size.width, image.size.height); // Resize the image image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality]; // Pass the NSData out again imageData = UIImageJPEGRepresentation(image, kMESImageQuality); } }

Yo no recomendaría el tamaño recursivo de la imagen. Cada vez que cambia el tamaño, pierde algo de calidad (a menudo se manifiesta como un "suavizado" de la imagen con pérdida de detalle, con efectos acumulativos). Siempre desea volver a la imagen original y cambiar su tamaño cada vez más pequeño. (Dejando de lado a un menor, esa sentencia if es redundante).

Podría sugerir lo siguiente:

NSData *imageData = UIImageJPEGRepresentation(image, kMESImageQuality); double factor = 1.0; double adjustment = 1.0 / sqrt(2.0); // or use 0.8 or whatever you want CGSize size = image.size; CGSize currentSize = size; UIImage *currentImage = image; while (imageData.length >= (1024 * 1024)) { factor *= adjustment; currentSize = CGSizeMake(roundf(size.width * factor), roundf(size.height * factor)); currentImage = [image resizedImage:currentSize interpolationQuality:kMESImageQuality]; imageData = UIImageJPEGRepresentation(currentImage, kMESImageQuality); }

Tenga en cuenta que no estoy tocando la image , la imagen original, sino que asigno currentImage haciendo un cambio de tamaño de la imagen original cada vez, con una escala decreciente cada vez.

Por cierto, si te estás preguntando por mi cryptic 1.0 / sqrt(2.0) , estaba tratando de llegar a un compromiso entre tu factor iterativo del 80% y mi deseo de favorecer el cambio de tamaño con una potencia de 2 donde puedo (porque una reducción retiene más nitidez cuando se hace por una potencia de 2). Pero usa el factor de adjustment que quieras.

Finalmente, si está haciendo esto en imágenes grandes, podría pensar en usar los bloques @autoreleasepool . Querrá crear un perfil de su aplicación en Asignaciones en instrumentos y ver dónde está su marca de límite, ya que, en ausencia de grupos de autorelease, esto puede constituir un uso bastante agresivo de la memoria.


En su pregunta revisada, aclaró que su objetivo era decir dentro de las limitaciones de tamaño de archivo al cargar imágenes. En ese caso, jugar con las opciones de compresión JPEG está bien, como sugiere rmaddy.

La pregunta interesante es que tienes dos variables para jugar, la compresión JPEG y las dimensiones de la imagen (también hay otras, pero lo haré simple). ¿Cómo quieres priorizar uno sobre el otro? Por ejemplo, no creo que tenga sentido mantener una resolución completa, una imagen comprimida absurdamente (por ejemplo, un factor de calidad de 0.1). Tampoco tiene sentido mantener una resolución pequeña, una imagen sin comprimir. Personalmente, ajusté de manera iterativa la calidad según lo sugerido por rmaddy, pero establecí un nivel razonable (p. Ej., Calidad JPEG no menor que, por ejemplo, 0.70). En ese momento, podría considerar cambiar las dimensiones de la imagen (y eso también cambia el tamaño del archivo con bastante rapidez) y modificar las dimensiones hasta que el NSData resultante NSData tamaño adecuado.

De todos modos, en mi respuesta original, me centré en el consumo de memoria dentro de la aplicación (a diferencia del tamaño del archivo). Para la posteridad, vea esa respuesta a continuación:

Si está tratando de controlar la cantidad de memoria que se usa cuando carga las imágenes en los objetos UIImage que se usarán en los objetos UIKit , entonces la compresión JPEG no le ayudará mucho, ya que la representación interna de las imágenes una vez que las cargue. en objetos UIKit está sin comprimir. Por lo tanto, en ese escenario, las opciones de compresión JPEG no logran mucho (aparte de sacrificar la calidad de la imagen).

Para ilustrar la idea, tengo una imagen de 1920 x 1080. La tengo en formato PNG (el archivo es de 629 kb), un formato JPEG comprimido (217 kb) y un formato JPEG mínimamente comprimido (1.1 mb). Pero cuando carga esas tres imágenes diferentes en objetos UIImageView (incluso si tienen un frame muy pequeño), la herramienta "Asignaciones" del Instrumento me muestra que cada una está ocupando 7.91mb:

Esto se debe a que cuando carga la imagen en una vista de imagen, la representación interna sin comprimir de estas tres imágenes es de cuatro bytes por píxel (un byte para el rojo, uno para el verde, uno para el azul y otro para el alfa). Por lo tanto, mis imágenes de 1920 x 1080 ocupan 1920 x 1080 x 4 = 8,249,400 = 7.91mb.

Por lo tanto, si no desea que ocupen más de 500 kb en la memoria al cargarlos en los objetos de vista de imagen, eso significa que desea cambiarles el tamaño de tal manera que el producto del ancho multiplicado por el alto sea de 128,000 o menos (es decir, Si es cuadrado, menos de 358 x 358 pixeles).

Pero, si su preocupación es el ancho de banda de la red a medida que sube imágenes o la capacidad de almacenamiento persistente, continúe con los valores de compresión JPEG como lo sugiere la excelente respuesta de rmaddy. Pero si está tratando de resolver los problemas de consumo de memoria mientras las imágenes se cargan en objetos UIKit, no se centre en la compresión, sino en cambiar el tamaño de la imagen.


Este fue mi enfoque:

// Check if the image size is too large if ((imageData.length/1024) >= 1024) { while ((imageData.length/1024) >= 1024) { NSLog(@"While start - The imagedata size is currently: %f KB",roundf((imageData.length/1024))); // While the imageData is too large scale down the image // Get the current image size CGSize currentSize = CGSizeMake(image.size.width, image.size.height); // Resize the image image = [image resizedImage:CGSizeMake(roundf(((currentSize.width/100)*80)), roundf(((currentSize.height/100)*80))) interpolationQuality:kMESImageQuality]; // Pass the NSData out again imageData = UIImageJPEGRepresentation(image, kMESImageQuality); } }

El método de redimensionar la imagen es el siguiente:

// Returns a rescaled copy of the image, taking into account its orientation // The image will be scaled disproportionately if necessary to fit the bounds specified by the parameter - (UIImage *)resizedImage:(CGSize)newSize interpolationQuality:(CGInterpolationQuality)quality { BOOL drawTransposed; switch (self.imageOrientation) { case UIImageOrientationLeft: case UIImageOrientationLeftMirrored: case UIImageOrientationRight: case UIImageOrientationRightMirrored: drawTransposed = YES; break; default: drawTransposed = NO; } return [self resizedImage:newSize transform:[self transformForOrientation:newSize] drawTransposed:drawTransposed interpolationQuality:quality]; }

Seguido por:

// Returns a copy of the image that has been transformed using the given affine transform and scaled to the new size // The new image''s orientation will be UIImageOrientationUp, regardless of the current image''s orientation // If the new size is not integral, it will be rounded up - (UIImage *)resizedImage:(CGSize)newSize transform:(CGAffineTransform)transform drawTransposed:(BOOL)transpose interpolationQuality:(CGInterpolationQuality)quality { CGRect newRect = CGRectIntegral(CGRectMake(0, 0, newSize.width, newSize.height)); CGRect transposedRect = CGRectMake(0, 0, newRect.size.height, newRect.size.width); CGImageRef imageRef = self.CGImage; // Build a context that''s the same dimensions as the new size CGContextRef bitmap = CGBitmapContextCreate(NULL, newRect.size.width, newRect.size.height, CGImageGetBitsPerComponent(imageRef), 0, CGImageGetColorSpace(imageRef), CGImageGetBitmapInfo(imageRef)); // Rotate and/or flip the image if required by its orientation CGContextConcatCTM(bitmap, transform); // Set the quality level to use when rescaling CGContextSetInterpolationQuality(bitmap, quality); // Draw into the context; this scales the image CGContextDrawImage(bitmap, transpose ? transposedRect : newRect, imageRef); // Get the resized image from the context and a UIImage CGImageRef newImageRef = CGBitmapContextCreateImage(bitmap); UIImage *newImage = [UIImage imageWithCGImage:newImageRef]; // Clean up CGContextRelease(bitmap); CGImageRelease(newImageRef); return newImage; }

// Métodos adicionales para referencia

// Returns an affine transform that takes into account the image orientation when drawing a scaled image - (CGAffineTransform)transformForOrientation:(CGSize)newSize { CGAffineTransform transform = CGAffineTransformIdentity; switch (self.imageOrientation) { case UIImageOrientationDown: // EXIF = 3 case UIImageOrientationDownMirrored: // EXIF = 4 transform = CGAffineTransformTranslate(transform, newSize.width, newSize.height); transform = CGAffineTransformRotate(transform, M_PI); break; case UIImageOrientationLeft: // EXIF = 6 case UIImageOrientationLeftMirrored: // EXIF = 5 transform = CGAffineTransformTranslate(transform, newSize.width, 0); transform = CGAffineTransformRotate(transform, M_PI_2); break; case UIImageOrientationRight: // EXIF = 8 case UIImageOrientationRightMirrored: // EXIF = 7 transform = CGAffineTransformTranslate(transform, 0, newSize.height); transform = CGAffineTransformRotate(transform, -M_PI_2); break; default: break; } switch (self.imageOrientation) { case UIImageOrientationUpMirrored: // EXIF = 2 case UIImageOrientationDownMirrored: // EXIF = 4 transform = CGAffineTransformTranslate(transform, newSize.width, 0); transform = CGAffineTransformScale(transform, -1, 1); break; case UIImageOrientationLeftMirrored: // EXIF = 5 case UIImageOrientationRightMirrored: // EXIF = 7 transform = CGAffineTransformTranslate(transform, newSize.height, 0); transform = CGAffineTransformScale(transform, -1, 1); break; default: break; } return transform; }


-(NSData*)testData { UIImage *imageToUpload=[UIImage imageNamed:@"images/lifestyle2.jpg"]; NSData *imgData=UIImageJPEGRepresentation(imageToUpload,1.0); float compressionRate=10; while (imgData.length>1024) { if (compressionRate>0.5) { compressionRate=compressionRate-0.5; imgData=UIImageJPEGRepresentation(imageToUpload,compressionRate/10); } else { return imgData; } } return imgData; }

Mantiene una calidad de imagen no inferior a 1 MB.

Llamalo con

NSData *compressedImageData=[self testData]; NSLog(@"%lu KB",compressedImageData.length);