[Iphone] La orientación de la imagen de iOS tiene un comportamiento extraño


Answers

Tuve el mismo problema cuando recibí la imagen de la cámara, puse el siguiente código para arreglarlo. Agregué el método scaleAndRotateImage desde aquí

- (void) imagePickerController:(UIImagePickerController *)thePicker didFinishPickingMediaWithInfo:(NSDictionary *)imageInfo {
            // Images from the camera are always in landscape, so rotate
                    UIImage *image = [self scaleAndRotateImage: [imageInfo objectForKey:UIImagePickerControllerOriginalImage]];
    //then save the image to photo gallery or wherever  
        }


- (UIImage *)scaleAndRotateImage:(UIImage *) image {
    int kMaxResolution = 320;

    CGImageRef imgRef = image.CGImage;

    CGFloat width = CGImageGetWidth(imgRef);
    CGFloat height = CGImageGetHeight(imgRef);


    CGAffineTransform transform = CGAffineTransformIdentity;
    CGRect bounds = CGRectMake(0, 0, width, height);
    if (width > kMaxResolution || height > kMaxResolution) {
        CGFloat ratio = width/height;
        if (ratio > 1) {
            bounds.size.width = kMaxResolution;
            bounds.size.height = bounds.size.width / ratio;
        }
        else {
            bounds.size.height = kMaxResolution;
            bounds.size.width = bounds.size.height * ratio;
        }
    }

    CGFloat scaleRatio = bounds.size.width / width;
    CGSize imageSize = CGSizeMake(CGImageGetWidth(imgRef), CGImageGetHeight(imgRef));
    CGFloat boundHeight;
    UIImageOrientation orient = image.imageOrientation;
    switch(orient) {

        case UIImageOrientationUp: //EXIF = 1
            transform = CGAffineTransformIdentity;
            break;

        case UIImageOrientationUpMirrored: //EXIF = 2
            transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            break;

        case UIImageOrientationDown: //EXIF = 3
            transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

        case UIImageOrientationDownMirrored: //EXIF = 4
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
            transform = CGAffineTransformScale(transform, 1.0, -1.0);
            break;

        case UIImageOrientationLeftMirrored: //EXIF = 5
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
            transform = CGAffineTransformScale(transform, -1.0, 1.0);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationLeft: //EXIF = 6
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
            transform = CGAffineTransformRotate(transform, 3.0 * M_PI / 2.0);
            break;

        case UIImageOrientationRightMirrored: //EXIF = 7
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeScale(-1.0, 1.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        case UIImageOrientationRight: //EXIF = 8
            boundHeight = bounds.size.height;
            bounds.size.height = bounds.size.width;
            bounds.size.width = boundHeight;
            transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
            transform = CGAffineTransformRotate(transform, M_PI / 2.0);
            break;

        default:
            [NSException raise:NSInternalInconsistencyException format:@"Invalid image orientation"];

    }

    UIGraphicsBeginImageContext(bounds.size);

    CGContextRef context = UIGraphicsGetCurrentContext();

    if (orient == UIImageOrientationRight || orient == UIImageOrientationLeft) {
        CGContextScaleCTM(context, -scaleRatio, scaleRatio);
        CGContextTranslateCTM(context, -height, 0);
    }
    else {
        CGContextScaleCTM(context, scaleRatio, -scaleRatio);
        CGContextTranslateCTM(context, 0, -height);
    }

    CGContextConcatCTM(context, transform);

    CGContextDrawImage(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, width, height), imgRef);
    UIImage *imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}
Question

Durante las últimas semanas he estado trabajando con imágenes en Objective-C y noté un montón de comportamientos extraños. En primer lugar, como muchas otras personas, he tenido este problema en el que las imágenes tomadas con la cámara (o tomadas con la cámara de otra persona y MMS) se giran 90 grados. No estaba seguro de por qué en el mundo esto estaba sucediendo (de ahí mi pregunta ) pero pude encontrar un trabajo barato.

Mi pregunta esta vez es por qué está pasando esto ? ¿Por qué Apple está rotando imágenes? Cuando tomo una foto con mi cámara con el lado derecho hacia arriba, a menos que realice el código mencionado anteriormente, cuando guardo la foto, se guarda rota. Ahora, mi solución estuvo bien hasta hace unos días.

Mi aplicación modifica los píxeles individuales de una imagen, específicamente el canal alfa de un archivo PNG (por lo que cualquier conversión JPEG se sale de la ventana para mi escenario). Hace unos días noté que a pesar de que la imagen se muestra correctamente en mi aplicación gracias a mi código de solución, cuando mi algoritmo modifica los píxeles individuales de la imagen, cree que la imagen se rota. Por lo tanto, en lugar de modificar los píxeles en la parte superior de la imagen, modifica los píxeles en el costado de la imagen (¡porque cree que debería girarse)! No puedo imaginar cómo rotar la imagen en la memoria. Idealmente, preferiría simplemente borrar esa bandera de imageOrientation .

Aquí hay algo más que también me ha estado desconcertando ... Cuando tomo la foto, la imageOrientation la imageOrientation se establece en 3. Mi código de solución es lo suficientemente inteligente como para darme cuenta de esto y voltearlo para que el usuario nunca lo note. Además, mi código para guardar la imagen en la biblioteca se da cuenta de esto, lo voltea y luego lo guarda para que aparezca en el carrete de la cámara correctamente.

Ese código se ve así:

NSData* pngdata = UIImagePNGRepresentation (self.workingImage); //PNG wrap 
UIImage* img = [self rotateImageAppropriately:[UIImage imageWithData:pngdata]];   
UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);

Cuando cargo esta imagen recién guardada en mi aplicación, imageOrientation es 0, exactamente lo que quiero ver, y mi solución de rotación ni siquiera necesita ejecutarse (nota: al cargar imágenes de Internet en lugar de imágenes tomadas con un cámara, la imageOrientation la imageOrientation siempre es 0, lo que resulta en un comportamiento perfecto). Por alguna razón, mi código de guardado parece borrar este indicador de imageOrientation . Esperaba robar ese código y usarlo para eliminar mi orientación de imagen tan pronto como el usuario toma una foto y la agrega a la aplicación, pero parece que no funciona. ¿ UIImageWriteToSavedPhotosAlbum hace algo especial con imageOrientation ?

La mejor solución para este problema sería simplemente eliminar la imageOrientation tan pronto como el usuario imageOrientation tomar una imagen. Supongo que Apple tiene el comportamiento de rotación hecho por una razón, ¿verdad? Algunas personas sugirieron que este es un defecto de Apple.

(... si aún no estás perdido ... Nota 2: cuando tomo una foto horizontal, todo parece funcionar perfectamente, al igual que las fotos tomadas de Internet)

EDITAR:

Estas son algunas de las imágenes y escenarios que realmente se ven. Basado en los comentarios hasta el momento, parece que este comportamiento extraño es más que solo un comportamiento de iPhone, que creo que es bueno.

Esta es una foto de la foto que tomé con mi teléfono (tenga en cuenta la orientación correcta), aparece exactamente como lo hizo en mi teléfono cuando tomé la foto:

Así es como se ve la imagen en Gmail después de que me lo envié por correo electrónico (parece que Gmail lo maneja correctamente):

Aquí se muestra cómo se ve la imagen como una miniatura en las ventanas (no parece que se maneje correctamente):

Y así es como se ve la imagen real cuando se abre con Windows Photo Viewer (aún no se maneja correctamente):

Después de todos los comentarios sobre esta pregunta, esto es lo que estoy pensando ... El iPhone toma una imagen y dice "para que se muestre correctamente, debe girarse 90 grados". Esta información estaría en los datos EXIF. (Por qué necesita girarse 90 grados, en lugar de predeterminarlo a vertical, no lo sé). Desde aquí, Gmail es lo suficientemente inteligente como para leer y analizar los datos EXIF ​​y mostrarlos correctamente. Sin embargo, Windows no es lo suficientemente inteligente como para leer los datos EXIF ​​y, por lo tanto, muestra la imagen incorrectamente . ¿Son mis suposiciones correctas?




Intente cambiar el formato de la imagen a .jpeg. Esto funcionó para mí




Me encontré con esta pregunta porque estaba teniendo un problema similar, pero usando Swift. Solo quería vincular la respuesta que funcionó para mí para cualquier otro desarrollador de Swift: https://.com/a/26676578/3904581

Aquí hay un fragmento de Swift que soluciona el problema de manera eficiente:

let orientedImage = UIImage(CGImage: initialImage.CGImage, scale: 1, orientation: initialImage.imageOrientation)!

Súper simple. Una linea de codigo Problema resuelto.




Refactorizado rápidamente para Swift 3 (¿alguien puede probarlo y confirmar que todo funciona bien?):

static func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage {
    let imgRef = imageSource.cgImage

    let width = CGFloat(imgRef!.width)
    let height = CGFloat(imgRef!.height)

    var bounds = CGRect(x: 0, y: 0, width: width, height: height)

    var scaleRatio : CGFloat = 1
    if width > maxResolution || height > maxResolution {

        scaleRatio = min(maxResolution / bounds.size.width, maxResolution / bounds.size.height)
        bounds.size.height = bounds.size.height * scaleRatio
        bounds.size.width = bounds.size.width * scaleRatio
    }

    var transform = CGAffineTransform.identity
    let orient = imageSource.imageOrientation
    let imageSize = CGSize(width: imgRef!.width, height: imgRef!.height)

    switch imageSource.imageOrientation {
    case .up :
        transform = CGAffineTransform.identity

    case .upMirrored :
        transform = CGAffineTransform(translationX: imageSize.width, y: 0)
        transform = transform.scaledBy(x: -1, y: 1)

    case .down :
        transform = CGAffineTransform(translationX: imageSize.width, y: imageSize.height)
        transform = transform.rotated(by: CGFloat.pi)

    case .downMirrored :
        transform = CGAffineTransform(translationX: 0, y: imageSize.height)
        transform = transform.scaledBy(x: 1, y: -1)

    case .left :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: 0, y: imageSize.width)
        transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0)

    case .leftMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: imageSize.width)
        transform = transform.scaledBy(x: -1, y: 1)
        transform = transform.rotated(by: 3.0 * CGFloat.pi / 2.0)

    case .right :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(translationX: imageSize.height, y: 0)
        transform = transform.rotated(by: CGFloat.pi / 2.0)

    case .rightMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width
        bounds.size.width = storedHeight
        transform = CGAffineTransform(scaleX: -1, y: 1)
        transform = transform.rotated(by: CGFloat.pi / 2.0)

    }

    UIGraphicsBeginImageContext(bounds.size)
    let context = UIGraphicsGetCurrentContext()

    if orient == .right || orient == .left {

        context!.scaleBy(x: -scaleRatio, y: scaleRatio)
        context!.translateBy(x: -height, y: 0)
    } else {
        context!.scaleBy(x: scaleRatio, y: -scaleRatio)
        context!.translateBy(x: 0, y: -height)
    }

    context!.concatenate(transform)
    context!.draw(imgRef!, in: CGRect(x: 0, y: 0, width: width, height: height))

    let imageCopy = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return imageCopy!
}



Versión Swift 3 con controles de seguridad de la respuesta de Dilip .

public static func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat = 320) -> UIImage? {

    guard let imgRef = imageSource.cgImage else {
        return nil
    }

    let width = CGFloat(imgRef.width)
    let height = CGFloat(imgRef.height)

    var bounds = CGRect(x: 0, y: 0, width: width, height: height)

    var scaleRatio : CGFloat = 1
    if (width > maxResolution || height > maxResolution) {

        scaleRatio = min(maxResolution / bounds.size.width, maxResolution / bounds.size.height)
        bounds.size.height = bounds.size.height * scaleRatio
        bounds.size.width = bounds.size.width * scaleRatio
    }

    var transform = CGAffineTransform.identity
    let orient = imageSource.imageOrientation
    let imageSize = CGSize(width: CGFloat(imgRef.width), height: CGFloat(imgRef.height))

    switch(imageSource.imageOrientation) {
    case .up:
        transform = .identity
    case .upMirrored:
        transform = CGAffineTransform
            .init(translationX: imageSize.width, y: 0)
            .scaledBy(x: -1.0, y: 1.0)
    case .down:
        transform = CGAffineTransform
            .init(translationX: imageSize.width, y: imageSize.height)
            .rotated(by: CGFloat.pi)
    case .downMirrored:
        transform = CGAffineTransform
            .init(translationX: 0, y: imageSize.height)
            .scaledBy(x: 1.0, y: -1.0)
    case .left:
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransform
            .init(translationX: 0, y: imageSize.width)
            .rotated(by: 3.0 * CGFloat.pi / 2.0)
    case .leftMirrored:
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransform
            .init(translationX: imageSize.height, y: imageSize.width)
            .scaledBy(x: -1.0, y: 1.0)
            .rotated(by: 3.0 * CGFloat.pi / 2.0)
    case .right :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransform
            .init(translationX: imageSize.height, y: 0)
            .rotated(by: CGFloat.pi / 2.0)
    case .rightMirrored:
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransform
            .init(scaleX: -1.0, y: 1.0)
            .rotated(by: CGFloat.pi / 2.0)
    }

    UIGraphicsBeginImageContext(bounds.size)
    if let context = UIGraphicsGetCurrentContext() {
        if orient == .right || orient == .left {
            context.scaleBy(x: -scaleRatio, y: scaleRatio)
            context.translateBy(x: -height, y: 0)
        } else {
            context.scaleBy(x: scaleRatio, y: -scaleRatio)
            context.translateBy(x: 0, y: -height)
        }

        context.concatenate(transform)
        context.draw(imgRef, in: CGRect(x: 0, y: 0, width: width, height: height))
    }

    let imageCopy = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return imageCopy
}



Mi pregunta esta vez es por qué está pasando esto? ¿Por qué Apple está rotando imágenes?

La respuesta a esto es muy simple. Apple NO está rotando la imagen. Ahí es donde yace la confusión.

La cámara CCD no gira, por lo que siempre toma la foto en modo horizontal.

Apple hizo algo muy inteligente: en lugar de gastar todo el tiempo para rotar la imagen, mezclando megabytes de datos, simplemente marque con CÓMO se tomó la fotografía.

OpenGL hace traducciones muy fácilmente, por lo que los DATOS nunca se barajan, simplemente CÓMO SE SORTEO.

De ahí los metadatos de orientación.

Esto se convierte en un problema si quieres recortar, cambiar el tamaño, etc., pero una vez que sabes lo que está sucediendo, solo defines tu matriz y todo funciona.




Links