[iphone] iOS 이미지 방향에 이상한 동작이 있습니다.

지난 몇 주 동안 저는 Objective-C에서 이미지 작업을하면서 많은 이상한 동작을 발견했습니다. 첫째, 다른 많은 사람들과 마찬가지로 카메라로 찍은 이미지 (또는 다른 사람의 카메라로 찍은 이미지와 나에게 MMS로 찍은 이미지)가 90도 회전되는 문제가 발생했습니다. 나는 왜 이것이 세계에서 일어나고 있는지 확신하지 못했지만 (그러므로 내 질문 ) 저렴한 해결책을 찾을 수있었습니다.

이번에는 왜 이런 일이 발생 합니까? Apple이 이미지를 회전시키는 이유는 무엇입니까? 카메라를 똑바로 세우고 사진을 찍을 때 위에서 언급 한 코드를 수행하지 않으면 사진을 저장하면 회전하여 저장됩니다. 이제 내 해결 방법은 며칠 전까지 만해도 괜찮 았습니다.

내 응용 프로그램은 이미지의 개별 픽셀, 특히 PNG의 알파 채널을 수정합니다 (따라서 모든 JPEG 변환은 내 시나리오에서 창 밖으로 던져집니다). 며칠 전 해결 코드 덕분에 앱에서 이미지가 제대로 표시되지만 알고리즘이 이미지의 개별 픽셀을 수정할 때 이미지가 회전 된 것으로 생각한다는 것을 알게되었습니다. 따라서 이미지 상단의 픽셀을 수정하는 대신 이미지 측면의 픽셀을 수정합니다 (회전해야한다고 생각하기 때문입니다)! 메모리에서 이미지를 회전하는 방법을 알 수 없습니다. 이상적으로는 해당 imageOrientation플래그를 모두 함께 지우는 것이 좋습니다.

여기 저를 당혹스럽게하는 또 다른 것이 있습니다 … 제가 사진을 찍을 때, imageOrientation가 3으로 설정되어 있습니다. 제 해결 방법 코드는 이것을 깨닫고 뒤집을 수있을만큼 똑똑해서 사용자가 알아 차리지 못하도록합니다. 또한 이미지를 라이브러리에 저장하는 코드는이를 인식하고 뒤집은 다음 저장하여 카메라 롤에 제대로 표시되도록합니다.

해당 코드는 다음과 같습니다.

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

이 새로 저장된 이미지를 내 앱에로드하면 imageOrientation0이됩니다-정확히 내가보고 싶은 것이므로 회전 해결 방법을 실행할 필요도 없습니다 (참고 : 카메라로 찍은 이미지와 달리 인터넷에서 이미지를로드 할 때 , imageOrientation는 항상 0이므로 완벽한 동작이됩니다.) 어떤 이유로 내 저장 코드가이 imageOrientation플래그 를 지우는 것 같습니다 . 사용자가 사진을 찍고 앱에 추가하자마자 해당 코드를 훔쳐 내 imageOrientation을 지우고 싶었지만 작동하지 않는 것 같습니다. 않습니다 UIImageWriteToSavedPhotosAlbum와 함께 뭔가 특별한을 imageOrientation?

이 문제에 대한 최선의 해결책 imageOrientation은 사용자가 이미지를 찍는 즉시 날려 버리는 것입니다. 나는 Apple이 이유 때문에 회전 동작을 수행했다고 가정합니다. 몇몇 사람들은 이것이 Apple 결함이라고 제안했습니다.

(… 아직 길을 잃지 않았다면 … Note2 : 가로 사진을 찍으면 인터넷에서 찍은 사진처럼 모든 것이 완벽하게 작동하는 것 같습니다)

편집하다:

다음은 일부 이미지와 시나리오의 실제 모습입니다. 지금까지의 의견을 살펴보면이 이상한 동작은 단순한 iPhone 동작 이상인 것 같습니다.

이것은 내가 휴대 전화로 찍은 사진입니다 (올바른 방향에 유의). 사진을 찍었을 때 휴대 전화에서했던 것과 똑같이 나타납니다.

iPhone에서 찍은 실제 사진

본인에게 이메일을 보낸 후 Gmail에 표시되는 이미지는 다음과 같습니다 (Gmail에서 제대로 처리하는 것처럼 보임).

Gmail에 표시되는 사진

다음은 이미지가 창에서 썸네일로 보이는 것입니다 (올바르게 처리되지 않은 것 같습니다).

Windows 썸네일

그리고 다음은 Windows 사진 뷰어로 열었을 때의 실제 이미지 모습입니다 (여전히 제대로 처리되지 않음).

Windows 사진 뷰어 버전

이 질문에 대한 모든 코멘트를 마친 후, 제가 생각하는 것은 다음과 같습니다. iPhone은 이미지를 찍고 “이를 제대로 표시하려면 90도 회전해야합니다”라고 말합니다. 이 정보는 EXIF ​​데이터에 있습니다. (기본적으로 수직 수직이 아닌 90도 회전해야하는 이유는 모르겠습니다.) 여기에서 Gmail은 EXIF ​​데이터를 읽고 분석하고 올바르게 표시 할 수있을만큼 똑똑합니다. 그러나 Windows는 EXIF ​​데이터를 읽을만큼 똑똑하지 않기 때문에 이미지가 부적절하게 표시됩니다 . 내 가정이 맞습니까?



답변

나는 그것에 대해 R & D를했고 모든 이미지 파일에 메타 데이터 속성이 있다는 것을 발견했습니다. 메타 데이터가 일반적으로 Mac이 아닌 다른 OS에서 무시되는 이미지의 방향을 지정하는 경우. 촬영 된 대부분의 이미지는 메타 데이터 속성이 직각으로 설정되어 있습니다. 따라서 Mac은 90도 회전 방식을 보여줍니다. Windows OS에서 동일한 이미지를 적절한 방식으로 볼 수 있습니다.

자세한 내용은이 답변을 읽으십시오. http://graphicssoft.about.com/od/digitalphotography/f/sideways-pictures.htm을

여기에 이미지의 Exif를 읽어보세요 http://www.exifviewer.org/ , 또는 http://regex.info/exif.cgi , 또는 http://www.addictivetips.com/internet-tips/view-complete-exif -metadata-information-of-any-jpeg-image-online /


답변

카메라에서 이미지를 가져올 때도 같은 문제가 발생했습니다. 다음 코드를 입력하여 수정했습니다. 여기에서 scaleAndRotateImage 메소드를 추가했습니다.

- (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;
}


답변

빠른 복사 / 붙여 넣기 Dilip의 탁월한 답변 의 신속한 번역 .

import Darwin

class func rotateCameraImageToProperOrientation(imageSource : UIImage, maxResolution : CGFloat) -> UIImage {

    let imgRef = imageSource.CGImage;

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

    var bounds = CGRectMake(0, 0, width, 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 = CGAffineTransformIdentity
    let orient = imageSource.imageOrientation
    let imageSize = CGSizeMake(CGFloat(CGImageGetWidth(imgRef)), CGFloat(CGImageGetHeight(imgRef)))


    switch(imageSource.imageOrientation) {
    case .Up :
        transform = CGAffineTransformIdentity

    case .UpMirrored :
        transform = CGAffineTransformMakeTranslation(imageSize.width, 0.0);
        transform = CGAffineTransformScale(transform, -1.0, 1.0);

    case .Down :
        transform = CGAffineTransformMakeTranslation(imageSize.width, imageSize.height);
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI));

    case .DownMirrored :
        transform = CGAffineTransformMakeTranslation(0.0, imageSize.height);
        transform = CGAffineTransformScale(transform, 1.0, -1.0);

    case .Left :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransformMakeTranslation(0.0, imageSize.width);
        transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);

    case .LeftMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransformMakeTranslation(imageSize.height, imageSize.width);
        transform = CGAffineTransformScale(transform, -1.0, 1.0);
        transform = CGAffineTransformRotate(transform, 3.0 * CGFloat(M_PI) / 2.0);

    case .Right :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransformMakeTranslation(imageSize.height, 0.0);
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);

    case .RightMirrored :
        let storedHeight = bounds.size.height
        bounds.size.height = bounds.size.width;
        bounds.size.width = storedHeight;
        transform = CGAffineTransformMakeScale(-1.0, 1.0);
        transform = CGAffineTransformRotate(transform, CGFloat(M_PI) / 2.0);

    default : ()
    }

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

    if orient == .Right || orient == .Left {
        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);

    let imageCopy = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return imageCopy;
}


답변

이번에는 왜 이런 일이 발생합니까? Apple이 이미지를 회전시키는 이유는 무엇입니까?

이에 대한 답은 매우 간단합니다. Apple은 이미지를 회전하지 않습니다. 그것이 혼란이있는 곳입니다.

CCD 카메라는 회전하지 않으므로 항상 가로 모드로 사진을 찍습니다.

애플은 이미지를 회전하는 데 시간을 낭비하는 대신 메가 바이트의 데이터를 섞는 대신 매우 현명한 작업을 수행했습니다. 사진을 촬영 한 방법을 태그 만 지정하면됩니다.

OpenGL은 번역을 매우 쉽게 수행하므로 데이터가 섞이지 않습니다.

따라서 방향 메타 데이터입니다.

자르기, 크기 조정 등을 원할 경우 문제가됩니다.하지만 무슨 일이 일어나고 있는지 알고 나면 매트릭스를 정의하기 만하면 모든 것이 작동합니다.


답변

Dilip의 답변에 대한 안전 점검이 포함 된 Swift 4 버전 .

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
}


답변

iPhone / iPad에서 생성 된 모든 이미지는 실제 방향을 지정하는 EXIF ​​Orientation 태그 (Exif.Image.Orientation)와 함께 Landscape Left로 저장됩니다.

값은 다음과 같습니다. 1 : 가로 왼쪽 6 : 세로 보통 3 : 가로 오른쪽 4 : 세로 거꾸로

IOS에서는 EXIF ​​정보가 제대로 읽히고 촬영 한 것과 같은 방식으로 이미지가 표시됩니다. 그러나 Windows에서는 EXIF ​​정보가 사용되지 않습니다.

김프에서 이러한 이미지 중 하나를 열면 이미지에 회전 정보가 있다고 표시됩니다.


답변

Xamarin을 사용하는 다른 사람을 위해 Dilip의 훌륭한 답변에 대한 C # 번역 과 Swift 번역에 대한 thattyson에게 감사드립니다 .

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;
}