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 ?

13voto

ATOzTOA Points 9045

Toute image générée par l'iPhone/iPad est enregistrée en tant que Paysage à gauche avec la balise EXIF Orientation (Exif.Image.Orientation) spécifiant l'orientation réelle.

Il a les valeurs suivantes : 1 : Paysage Gauche 6 : Portrait Normal 3 : Paysage à droite 4 : Portrait à l'envers

Dans IOS, les informations EXIF sont correctement lues et les images sont affichées de la même manière qu'elles ont été prises. Mais sous Windows, les informations EXIF ne sont PAS utilisées.

Si vous ouvrez l'une de ces images dans GIMP, le logiciel indiquera que l'image possède des informations sur la rotation.

9voto

tetowill Points 91

Pour tous ceux qui utilisent Xamarin, voici une traduction en C# de l'article de Dilip. excellente réponse et un remerciement à Thattyson pour l'image de marque. Traduction rapide .

public static UIImage RotateCameraImageToProperOrientation(UIImage imageSource, nfloat maxResolution) {

    var imgRef = imageSource.CGImage;

    var width = (nfloat)imgRef.Width;
    var height = (nfloat)imgRef.Height;

    var bounds = new CGRect(0, 0, width, height);

    nfloat scaleRatio = 1;

    if (width > maxResolution || height > maxResolution) 
    {
        scaleRatio = (nfloat)Math.Min(maxResolution / bounds.Width, maxResolution / bounds.Height);
        bounds.Height = bounds.Height * scaleRatio;
        bounds.Width = bounds.Width * scaleRatio;
    }

    var transform = CGAffineTransform.MakeIdentity();
    var orient = imageSource.Orientation;
    var imageSize = new CGSize(imgRef.Width, imgRef.Height);
    nfloat storedHeight;

    switch(imageSource.Orientation) {
        case UIImageOrientation.Up:
            transform = CGAffineTransform.MakeIdentity();
            break;

        case UIImageOrientation.UpMirrored :
            transform = CGAffineTransform.MakeTranslation(imageSize.Width, 0.0f);
            transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f);
            break;

        case UIImageOrientation.Down :
            transform = CGAffineTransform.MakeTranslation(imageSize.Width, imageSize.Height);
            transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI);
            break;

        case UIImageOrientation.DownMirrored :
            transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Height);
            transform = CGAffineTransform.Scale(transform, 1.0f, -1.0f);
            break;

        case UIImageOrientation.Left:
            storedHeight = bounds.Height;
            bounds.Height = bounds.Width;
            bounds.Width = storedHeight;
            transform = CGAffineTransform.MakeTranslation(0.0f, imageSize.Width);
            transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f);
            break;

        case UIImageOrientation.LeftMirrored :
            storedHeight = bounds.Height;
            bounds.Height = bounds.Width;
            bounds.Width = storedHeight;
            transform = CGAffineTransform.MakeTranslation(imageSize.Height, imageSize.Width);
            transform = CGAffineTransform.Scale(transform, -1.0f, 1.0f);
            transform = CGAffineTransform.Rotate(transform, 3.0f * (nfloat)Math.PI / 2.0f);
            break;

        case UIImageOrientation.Right :
            storedHeight = bounds.Height;
            bounds.Height = bounds.Width;
            bounds.Width = storedHeight;
            transform = CGAffineTransform.MakeTranslation(imageSize.Height, 0.0f);
            transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f);
            break;

        case UIImageOrientation.RightMirrored :
            storedHeight = bounds.Height;
            bounds.Height = bounds.Width;
            bounds.Width = storedHeight;
            transform = CGAffineTransform.MakeScale(-1.0f, 1.0f);
            transform = CGAffineTransform.Rotate(transform, (nfloat)Math.PI / 2.0f);
            break;

        default :
            break;
    }

    UIGraphics.BeginImageContext(bounds.Size);
    var context = UIGraphics.GetCurrentContext();

    if (orient == UIImageOrientation.Right || orient == UIImageOrientation.Left) {
        context.ScaleCTM(-scaleRatio, scaleRatio);
        context.TranslateCTM(-height, 0);
    } else {
        context.ScaleCTM(scaleRatio, -scaleRatio);
        context.TranslateCTM(0, -height);
    }

    context.ConcatCTM(transform);
    context.DrawImage(new CGRect(0, 0, width, height), imgRef);

    var imageCopy = UIGraphics.GetImageFromCurrentImageContext();
    UIGraphics.EndImageContext();

    return imageCopy;
}

4voto

ClareHuxtable Points 635

Je suis tombé sur cette question parce que j'avais un problème similaire, mais en utilisant Swift. Je voulais juste faire un lien vers la réponse qui a fonctionné pour moi pour tous les autres développeurs Swift : https://stackoverflow.com/a/26676578/3904581

Voici un extrait de Swift qui résout efficacement le problème :

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

C'est très simple. Une ligne de code. Problème résolu.

0voto

Gautam Jain Points 491

Je sais exactement quel est votre problème. Vous utilisez UIImagePicker, ce qui est bizarre dans tous les sens du terme. Je vous suggère d'utiliser AVFoundation pour la caméra, qui offre une certaine flexibilité en termes d'orientation et de qualité. Utilisez AVCaptureSession. Vous pouvez obtenir le code ici Comment enregistrer les photos prises à l'aide d'AVFoundation dans l'album photo ?

0voto

sabiland Points 421

Rapidement remanié pour Swift 3 (quelqu'un peut-il le tester et confirmer que tout fonctionne 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!
}

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