97 votes

L'orientation des images dans iOS a un comportement étrange

Ces dernières semaines, j'ai travaillé avec des images en Objective-C et j'ai remarqué un certain nombre de comportements étranges. Tout d'abord, comme beaucoup d'autres personnes, j'ai eu ce problème où les images prises avec l'appareil photo (ou prises avec l'appareil photo de quelqu'un d'autre et envoyées par MMS) sont tournées de 90 degrés. Je n'étais pas sûr de la raison pour laquelle cela se produisait (d'où le fait que je n'ai pas pu m'en rendre compte). ma question ) mais j'ai pu trouver une solution bon marché.

Ma question cette fois-ci est pourquoi cela se produit-il ? ? Pourquoi Apple fait-elle tourner les images ? Lorsque je prends une photo avec mon appareil photo à l'endroit, à moins que je n'exécute le code mentionné ci-dessus, lorsque je sauvegarde la photo, elle est enregistrée avec une rotation. Ma solution de contournement fonctionnait bien jusqu'à il y a quelques jours.

Mon application modifie les pixels individuels d'une image, en particulier le canal alpha d'un PNG (toute conversion JPEG est donc exclue pour mon scénario). Il y a quelques jours, j'ai remarqué que même si l'image s'affiche correctement dans mon application grâce à mon code de contournement, lorsque mon algorithme modifie les pixels individuels de l'image, il pense que l'image est tournée. Ainsi, au lieu de modifier les pixels en haut de l'image, il modifie les pixels sur le côté de l'image (car il pense qu'elle doit être tournée) ! Je n'arrive pas à trouver comment faire pivoter l'image en mémoire - idéalement, je préférerais simplement effacer cette image de la mémoire. imageOrientation drapeau tous ensemble.

Il y a autre chose qui m'a aussi dérouté... Quand je prends la photo, le imageOrientation Mon code de contournement est suffisamment intelligent pour s'en rendre compte et l'inverser afin que l'utilisateur ne le remarque jamais. De plus, mon code pour enregistrer l'image dans la bibliothèque s'en rend compte et l'inverse, puis l'enregistre pour qu'il apparaisse correctement dans le rouleau de l'appareil photo.

Ce code ressemble à ceci :

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

Lorsque je charge cette image nouvellement sauvegardée dans mon application, la fonction imageOrientation est 0 - exactement ce que je veux voir, et ma solution de contournement de la rotation n'a même pas besoin d'être exécutée (note : lors du chargement d'images à partir d'Internet par opposition à des images prises avec un appareil photo, la fonction imageOrientation est toujours 0, ce qui donne un comportement parfait). Pour une raison ou une autre, mon code de sauvegarde semble effacer ceci imageOrientation le drapeau. J'espérais simplement voler ce code et l'utiliser pour effacer mon imageOrientation dès que l'utilisateur prend une photo et l'ajoute à l'application, mais cela ne semble pas fonctionner. Est-ce que UIImageWriteToSavedPhotosAlbum faire quelque chose de spécial avec imageOrientation ?

Est-ce que la meilleure solution à ce problème serait de simplement souffler imageOrientation dès que l'utilisateur a fini de prendre une photo. Je suppose qu'Apple a fait ce comportement de rotation pour une raison, n'est-ce pas ? Quelques personnes ont suggéré que c'était un défaut d'Apple.

(... si vous n'êtes pas encore perdu...) Note2 : Lorsque je prends une photo horizontale, tout semble fonctionner parfaitement, tout comme les photos prises sur Internet)

EDITAR:

Voici à quoi ressemblent certaines des images et des scénarios. D'après les commentaires reçus jusqu'à présent, il semble que ce comportement étrange soit plus qu'un simple comportement de l'iPhone, ce qui est une bonne chose.

Voici une image de la photo que j'ai prise avec mon téléphone (notez la bonne orientation), elle apparaît exactement comme elle était sur mon téléphone lorsque j'ai pris la photo :

Actual Photo taken on iPhone

Voici à quoi ressemble l'image dans Gmail après que je me la sois envoyée par courriel (on dirait que Gmail la traite correctement) :

Photo as it appears in Gmail

Voici à quoi ressemble l'image en tant que vignette dans Windows (on dirait qu'elle n'est pas traitée correctement) :

Windows Thumbnail

Et voici à quoi ressemble l'image réelle lorsqu'elle est ouverte avec Windows Photo Viewer (toujours pas traitée correctement) :

Windows Photo Viewer Version

Après tous les commentaires sur cette question, voici ce que je pense... L'iPhone prend une image, et dit "pour l'afficher correctement, il faut la faire pivoter de 90 degrés". Cette information serait dans les données EXIF. (Je ne sais pas pourquoi il faut la faire pivoter de 90 degrés, plutôt que de la mettre à la verticale par défaut). À partir de là, Gmail est suffisamment intelligent pour lire et analyser ces données EXIF, et les afficher correctement. Windows, en revanche, n'est pas assez intelligent pour lire les données EXIF et affiche donc l'image. incorrectement . Mes hypothèses sont-elles correctes ?

0 votes

Intéressant... si c'est vrai, pourquoi les images prises sont-elles réglées sur un angle droit ? Je sais que ce n'est pas un défaut de l'iPhone, car j'ai remarqué un comportement similaire lorsqu'un ami (avec un BlackBerry) prend une photo et me l'envoie par MMS.

0 votes

Vous voyez que votre appareil photo écrit des métadonnées pour le faire pivoter à 90 degrés, certains OS lisent ces métadonnées et d'autres non.

0 votes

Très bonne lecture (voir la réponse de @Hadley ci-dessous - elle était auparavant dans un commentaire). Donc mon application doit clairement être assez intelligente pour gérer les images qui contiennent des données EXIF, ainsi que les images qui n'en contiennent pas... Je ne comprends toujours pas pourquoi lorsque je prends une photo à l'endroit, le drapeau d'orientation dit qu'elle doit être tournée de 90 degrés ?

0voto

Sandip Mane Points 59

Voici la version Swift3 de la réponse géniale de Dilip.

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: width, height: height)

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

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

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

        case .downMirrored :
            transform = CGAffineTransform(translationX: 0.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.0, y: imageSize.width);
            transform = transform.rotated(by: 3.0 * CGFloat(Double.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(Double.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.0);
            transform = transform.rotated(by: CGFloat(Double.pi) / 2.0);

        case .rightMirrored :
            let storedHeight = bounds.size.height
            bounds.size.height = bounds.size.width;
            bounds.size.width = storedHeight;
            transform = CGAffineTransform(scaleX: -1.0, y: 1.0);
            transform = transform.rotated(by: CGFloat(Double.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!;
}

0voto

Muhammad Numan Points 71

Vous pouvez corriger cette image de rotation HECI par ce code

Extension de Swift 5 :

extension UIImage {
    /// Fix image orientaton to protrait up
    func fixedOrientation() -> UIImage? {
        guard imageOrientation != UIImage.Orientation.up else {
            // This is default orientation, don't need to do anything
            return self.copy() as? UIImage
        }

        guard let cgImage = self.cgImage else {
            // CGImage is not available
            return nil
        }

        guard let colorSpace = cgImage.colorSpace, let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else {
            return nil // Not able to create CGContext
        }

        var transform: CGAffineTransform = CGAffineTransform.identity

        switch imageOrientation {
        case .down, .downMirrored:
            transform = transform.translatedBy(x: size.width, y: size.height)
            transform = transform.rotated(by: CGFloat.pi)
        case .left, .leftMirrored:
            transform = transform.translatedBy(x: size.width, y: 0)
            transform = transform.rotated(by: CGFloat.pi / 2.0)
        case .right, .rightMirrored:
            transform = transform.translatedBy(x: 0, y: size.height)
            transform = transform.rotated(by: CGFloat.pi / -2.0)
        case .up, .upMirrored:
            break
        @unknown default:
            fatalError("Missing...")
            break
        }

        // Flip image one more time if needed to, this is to prevent flipped image
        switch imageOrientation {
        case .upMirrored, .downMirrored:
            transform = transform.translatedBy(x: size.width, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        case .leftMirrored, .rightMirrored:
            transform = transform.translatedBy(x: size.height, y: 0)
            transform = transform.scaledBy(x: -1, y: 1)
        case .up, .down, .left, .right:
            break
        @unknown default:
            fatalError("Missing...")
            break
        }

        ctx.concatenate(transform)

        switch imageOrientation {
        case .left, .leftMirrored, .right, .rightMirrored:
            ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
        default:
            ctx.draw(cgImage, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
            break
        }

        guard let newCGImage = ctx.makeImage() else { return nil }
        return UIImage.init(cgImage: newCGImage, scale: 1, orientation: .up)
    }
}

Code Objective C :

-(UIImage *)scaleAndRotateImage:(UIImage *)image{
        // No-op if the orientation is already correct
        if (image.imageOrientation == UIImageOrientationUp) return image;

        // We need to calculate the proper transformation to make the image upright.
        // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
        CGAffineTransform transform = CGAffineTransformIdentity;

        switch (image.imageOrientation) {
            case UIImageOrientationDown:
            case UIImageOrientationDownMirrored:
                transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height);
                transform = CGAffineTransformRotate(transform, M_PI);
                break;

            case UIImageOrientationLeft:
            case UIImageOrientationLeftMirrored:
                transform = CGAffineTransformTranslate(transform, image.size.width, 0);
                transform = CGAffineTransformRotate(transform, M_PI_2);
                break;

            case UIImageOrientationRight:
            case UIImageOrientationRightMirrored:
                transform = CGAffineTransformTranslate(transform, 0, image.size.height);
                transform = CGAffineTransformRotate(transform, -M_PI_2);
                break;
            case UIImageOrientationUp:
            case UIImageOrientationUpMirrored:
                break;
        }

        switch (image.imageOrientation) {
            case UIImageOrientationUpMirrored:
            case UIImageOrientationDownMirrored:
                transform = CGAffineTransformTranslate(transform, image.size.width, 0);
                transform = CGAffineTransformScale(transform, -1, 1);
                break;

            case UIImageOrientationLeftMirrored:
            case UIImageOrientationRightMirrored:
                transform = CGAffineTransformTranslate(transform, image.size.height, 0);
                transform = CGAffineTransformScale(transform, -1, 1);
                break;
            case UIImageOrientationUp:
            case UIImageOrientationDown:
            case UIImageOrientationLeft:
            case UIImageOrientationRight:
                break;
        }

        // Now we draw the underlying CGImage into a new context, applying the transform
        // calculated above.
        CGContextRef ctx = CGBitmapContextCreate(NULL, image.size.width, image.size.height,
                                                 CGImageGetBitsPerComponent(image.CGImage), 0,
                                                 CGImageGetColorSpace(image.CGImage),
                                                 CGImageGetBitmapInfo(image.CGImage));
        CGContextConcatCTM(ctx, transform);
        switch (image.imageOrientation) {
            case UIImageOrientationLeft:
            case UIImageOrientationLeftMirrored:
            case UIImageOrientationRight:
            case UIImageOrientationRightMirrored:
                // Grr...
                CGContextDrawImage(ctx, CGRectMake(0,0,image.size.height,image.size.width), image.CGImage);
                break;

            default:
                CGContextDrawImage(ctx, CGRectMake(0,0,image.size.width,image.size.height), image.CGImage);
                break;
        }

        // And now we just create a new UIImage from the drawing context
        CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
        UIImage *img = [UIImage imageWithCGImage:cgimg];
        CGContextRelease(ctx);
        CGImageRelease(cgimg);
        return img;
}

Utilisation du code

 UIImage *img=[info objectForKey:UIImagePickerControllerOriginalImage];

 img=[self scaleAndRotateImage:img];
 NSData *image = UIImageJPEGRepresentation(img, 0.1);

-3voto

Victor Rius Points 2430

Essayez de changer le format de l'image en .jpeg. Cela a fonctionné pour moi

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X