การวางแนวภาพ iOS มีพฤติกรรมแปลก ๆ


96

ในช่วงสองสามสัปดาห์ที่ผ่านมาฉันทำงานกับรูปภาพในวัตถุประสงค์ -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);

เมื่อฉันโหลดภาพที่บันทึกใหม่นี้ลงในแอปของฉันค่าimageOrientationคือ 0 - สิ่งที่ฉันต้องการเห็นและวิธีแก้ปัญหาการหมุนของฉันไม่จำเป็นต้องเรียกใช้ (หมายเหตุ: เมื่อโหลดภาพจากอินเทอร์เน็ตเมื่อเทียบกับภาพที่ถ่ายด้วยกล้องถ่ายรูป , imageOrientationเป็น 0 เสมอทำให้เกิดพฤติกรรมที่สมบูรณ์แบบ) ด้วยเหตุผลบางประการรหัสบันทึกของฉันดูเหมือนจะลบimageOrientationค่าสถานะนี้ ฉันหวังว่าจะขโมยรหัสนั้นและใช้มันเพื่อลบภาพของฉันการวางแนวทันทีที่ผู้ใช้ถ่ายรูปและเพิ่มลงในแอพ แต่ดูเหมือนจะไม่ได้ผล ไม่UIImageWriteToSavedPhotosAlbumทำอะไรเป็นพิเศษกับimageOrientation?

วิธีแก้ไขที่ดีที่สุดสำหรับปัญหานี้คือการหายไปimageOrientationทันทีที่ผู้ใช้ถ่ายภาพเสร็จ ฉันคิดว่า Apple มีพฤติกรรมการหมุนด้วยเหตุผลใช่ไหม? มีบางคนแนะนำว่านี่เป็นข้อบกพร่องของ Apple

(... ถ้าคุณยังไม่หลงทาง ... หมายเหตุ 2: เมื่อฉันถ่ายภาพแนวนอนทุกอย่างดูเหมือนจะทำงานได้อย่างสมบูรณ์แบบเช่นเดียวกับภาพที่ถ่ายจากอินเทอร์เน็ต)

แก้ไข:

ภาพและสถานการณ์จริงบางส่วนมีลักษณะดังนี้ จากความคิดเห็นที่ผ่านมาดูเหมือนว่าพฤติกรรมแปลก ๆ นี้จะเป็นมากกว่าพฤติกรรมของ iPhone ซึ่งฉันคิดว่าเป็นสิ่งที่ดี

นี่คือภาพของภาพถ่ายที่ฉันถ่ายด้วยโทรศัพท์ของฉัน (สังเกตทิศทางที่ถูกต้อง) ซึ่งจะปรากฏในโทรศัพท์ของฉันทุกประการเมื่อฉันถ่ายภาพ:

ภาพจริงถ่ายบน iPhone

นี่คือลักษณะของภาพใน Gmail หลังจากที่ฉันส่งอีเมลถึงตัวเอง (ดูเหมือนว่า Gmail จะจัดการอย่างถูกต้อง):

รูปภาพตามที่ปรากฏใน Gmail

นี่คือลักษณะของภาพในรูปขนาดย่อใน windows (ดูเหมือนจะไม่ได้รับการจัดการอย่างถูกต้อง):

ภาพขนาดย่อของ Windows

และนี่คือลักษณะของภาพจริงเมื่อเปิดด้วย Windows Photo Viewer (ยังจัดการไม่ถูกต้อง):

Windows Photo Viewer เวอร์ชัน

หลังจากความคิดเห็นทั้งหมดเกี่ยวกับคำถามนี้นี่คือสิ่งที่ฉันคิด ... iPhone ถ่ายภาพแล้วบอกว่า "เพื่อแสดงสิ่งนี้ได้อย่างถูกต้องต้องหมุน 90 องศา" ข้อมูลนี้จะอยู่ในข้อมูล EXIF (ทำไมต้องหมุน 90 องศาแทนที่จะตั้งค่าเป็นแนวตั้งตรงฉันไม่รู้) จากที่นี่ Gmail ฉลาดพอที่จะอ่านและวิเคราะห์ข้อมูล EXIF ​​และแสดงข้อมูลได้อย่างถูกต้อง ของ Windows แต่ไม่ฉลาดพอที่จะอ่านข้อมูล EXIF และดังนั้นจึงแสดงภาพไม่ถูกต้อง สมมติฐานของฉันถูกต้องหรือไม่?


น่าสนใจ ... ถ้าเป็นจริงทำไมภาพที่ถ่ายจึงถูกตั้งค่าเป็นมุมฉาก? ฉันรู้ว่านี่ไม่ใช่ข้อบกพร่องของ iPhone เพราะฉันสังเกตเห็นพฤติกรรมคล้าย ๆ กันเมื่อเพื่อน (ที่มี BlackBerry) ถ่ายภาพและส่ง MMS มาให้ฉัน
Boeckm

ดูกล้องของคุณเขียนข้อมูลเมตาเพื่อหมุนที่ 90 องศาระบบปฏิบัติการบางระบบอ่านข้อมูลเมตานั้นและส่วนที่เหลือไม่ทำ
HarshIT

อ่านดีทีเดียว (ดูคำตอบของ @ Hadley ด้านล่าง - เคยอยู่ในความคิดเห็น) ดังนั้นแอปของฉันจึงต้องฉลาดพอที่จะจัดการรูปภาพที่มีข้อมูล EXIF ​​ได้อย่างชัดเจนรวมถึงรูปภาพที่ไม่มีข้อมูล ... ฉันยังไม่เข้าใจว่าทำไมเมื่อฉันถ่ายภาพโดยหันด้านขวาขึ้นธงการวางแนว บอกว่าควรหมุน 90 องศา?
Boeckm

คุณตั้งค่าโหมดภาพถ่ายเป็นแนวตั้งและคุณไม่ได้หมุนกล้องไปที่มุมขวาหรือตั้งโหมดแนวนอนและกล้องจับภาพในตำแหน่งมุมขวาเซ็นเซอร์จะตั้งค่าการวางแนวให้เหมาะสมโดยอัตโนมัติ ลองดู
HarshIT

1
วิธีแก้ปัญหาส่วนใหญ่แอปของคุณจำเป็นต้องอ่านข้อมูลเมตาและหมุนภาพและ / หรือแก้ไขข้อมูลเมตาตามความต้องการ
HarshIT

คำตอบ:


50

ฉันทำการวิจัยและพัฒนาและค้นพบไฟล์รูปภาพทุกไฟล์มีคุณสมบัติเมทาดาทา หากข้อมูลเมตาระบุการวางแนวของภาพซึ่งโดยทั่วไปแล้ว OS อื่นจะไม่สนใจ แต่ Mac ภาพส่วนใหญ่ที่ถ่ายจะมีคุณสมบัติข้อมูลเมตาตั้งเป็นมุมฉาก ดังนั้น 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 /


1
ว้าว! ฉันไม่รู้ว่ามีข้อมูลมากมายในข้อมูล EXIF! regex.info/exif.cgiเป็นเว็บไซต์ที่น่าทึ่ง! ฉันจะเล่นกับภาพคืนนี้บนเว็บไซต์นี้อย่างแน่นอน ฉันจะต้องทดสอบทุกสถานการณ์ภาพจะตรงภาพที่ถ่ายจากอินเทอร์เน็ตภาพที่หมุน ฯลฯ ขอบคุณมาก!
Boeckm

ลิงก์ exif แรกใช้งานไม่ได้ในขณะนี้และลิงก์ที่สองได้รับการอัปเดตเป็นexif.regex.info/exif.cgiฉันพยายามแก้ไขคำตอบ แต่ @ cj-dennis ปฏิเสธด้วยเหตุผลบางประการ
Ryan

55

ฉันมีปัญหาเดียวกันเมื่อฉันได้รับภาพจากกล้องฉันใส่รหัสต่อไปนี้เพื่อแก้ไข .. เพิ่มวิธี 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;
}

3
รหัส scaleAndRotateImage ของคุณมีลักษณะอย่างไร หลังจากทำ google แล้วมันเหมือนกับที่นี่ไหม discussions.apple.com/thread/1537011?start=0&tstart=0
Boeckm

2
ว้าว ... ฮา! มันทำงานได้ดีมาก! ฉันใช้รหัสในความคิดเห็นด้านบน ฉันได้รับคำตอบที่น่าทึ่งสองข้อสำหรับคำถามของฉัน! ฉันคิดว่านั่นหมายความว่าฉันถามคำถามใหญ่เกินไป ... ขอบคุณมาก ฉันจะต้องทำการทดสอบเชิงลึก แต่จนถึงขณะนี้ปัญหาการวางแนวใช้งานได้ดีกับภาพถ่ายที่ถ่ายในแนวตั้งและแนวนอน
Boeckm

3
ฉันคิดว่ามีส่วนหนึ่งของวิธีการของคุณหายไป แต่นั่นและลิงก์ก็ยังเพียงพอสำหรับฉันที่จะหาส่วนที่เหลือและในที่สุดก็ทำให้มุมมองการครอบตัดรูปภาพของฉันทำงานได้อย่างถูกต้องขอบคุณ :)
Vic Smith

ฉันดีใจที่ได้ช่วย .. :)
Dilip Rajkumar

บล็อกโค้ดนี้รักษาลักษณะของภาพต้นฉบับ แต่เปลี่ยนการวางแนวเป็นขึ้นหรือไม่?
Junchao Gu

20

คำถามของฉันในครั้งนี้คือเหตุใดจึงเกิดขึ้น ทำไม Apple จึงหมุนภาพ

คำตอบนี้ง่ายมาก Apple ไม่ได้หมุนภาพ นั่นคือจุดที่สับสน

กล้อง CCD ไม่หมุนดังนั้นจึงถ่ายภาพในโหมดแนวนอนเสมอ

Apple ทำสิ่งที่ชาญฉลาดมาก - แทนที่จะใช้เวลาทั้งหมดในการหมุนภาพ - สับข้อมูลเมกะไบต์รอบ ๆ - เพียงแค่แท็กด้วยวิธีถ่ายภาพ

OpenGL แปลได้อย่างง่ายดาย - ดังนั้นข้อมูลจึงไม่ถูกสับ - เพียงแค่วาดอย่างไร

ดังนั้นข้อมูลเมตาการวางแนว

สิ่งนี้จะกลายเป็นปัญหาหากคุณต้องการครอบตัดปรับขนาดและอื่น ๆ - แต่เมื่อคุณรู้ว่าเกิดอะไรขึ้นคุณก็แค่กำหนดเมทริกซ์ของคุณและทุกอย่างก็เป็นไปได้


1
ขอบคุณสำหรับคำอธิบายนี้ สามัญสำนึกเมื่อคุณคิดเกี่ยวกับเรื่องนี้ แต่ไม่มีผลการค้นหาอื่นใดที่อธิบายได้
Dakine83

คำอธิบายที่ดีเช่นศาสตราจารย์ ขอบคุณ!
vkGunasekaran

18

คัดลอก / วางคำตอบที่ยอดเยี่ยมของ 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;
}

1
ขอบคุณ แต่ "let ratio = width / height;" คืออะไร ใช้สำหรับ? ฉันไม่เห็นสถานที่ใดที่ใช้อัตราส่วนในนั้น {}
Pengzhi Zhou

16

สวิฟท์ 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
}

คุณสามารถทำได้(bounds.size.height, bounds.size.width) = (bounds.size.width, bounds.size.height)โดยไม่ต้องสร้างตัวแปรเป็นที่เก็บข้อมูล
Almas Sapargali

บางครั้งฉันก็มีปัญหาเส้นสีขาวปรากฏขึ้นที่ด้านล่างของภาพหลังจากปรับขนาดแล้ว เนื่องจาก CGFloat ดูเหมือนจะไม่แม่นยำพอ ฉันแก้ไขสิ่งนี้ด้วยbounds.size.height.round()และbounds.size.width.round()
ndreisg

13

ภาพใด ๆ ที่สร้างโดย iPhone / iPad จะบันทึกเป็นแนวนอนด้านซ้ายพร้อมกับแท็ก EXIF ​​Orientation (Exif.Image.Orientation) ซึ่งระบุการวางแนวจริง

มีค่าดังต่อไปนี้: 1: แนวนอนซ้าย 6: แนวตั้งปกติ 3: แนวนอนขวา 4: แนวตั้งกลับหัว

ใน IOS ข้อมูล EXIF ​​จะถูกอ่านอย่างถูกต้องและภาพจะแสดงในลักษณะเดียวกับที่ถ่าย แต่ใน Windows จะไม่ใช้ข้อมูล EXIF

หากคุณเปิดรูปภาพเหล่านี้ใน GIMP ระบบจะบอกว่ารูปภาพนั้นมีข้อมูลการหมุน


8

สำหรับคนอื่นใช้ Xamarin นี่คือการแปล C # ของดิลลิปของคำตอบที่ดีและขอบคุณที่ 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;
}

สิ่งนี้ช่วยแก้ปัญหาของฉันได้หลังจากใช้เวลาหลายชั่วโมงในการเล่นเกมนี้! ไม่น่าเชื่อว่า Xamarin ไม่มีการสนับสนุนในตัวเนื่องจากตอนนี้เป็นผลิตภัณฑ์ของ Microsoft แล้ว
Sami.C

4

ฉันเจอคำถามนี้เพราะฉันมีปัญหาคล้าย ๆ กัน แต่ใช้ Swift แค่ต้องการเชื่อมโยงไปยังคำตอบที่เหมาะกับฉันสำหรับนักพัฒนา Swift คนอื่น ๆ : https://stackoverflow.com/a/26676578/3904581

นี่คือตัวอย่างข้อมูล Swift ที่แก้ไขปัญหาได้อย่างมีประสิทธิภาพ:

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

ง่ายสุด ๆ รหัสหนึ่งบรรทัด แก้ไขปัญหา.


สิ่งนี้ไม่ได้ผลสำหรับฉันฉันใช้วิธีนี้: stackoverflow.com/a/27775741/945247
Leon

0

ฉันรู้ว่าปัญหาของคุณคืออะไร คุณกำลังใช้ UIImagePicker ซึ่งแปลกในทุกแง่ ฉันขอแนะนำให้คุณใช้ AVFoundation สำหรับกล้องที่ให้ความยืดหยุ่นในการวางแนวและคุณภาพ ใช้ AVCaptureSession คุณสามารถรับรหัสได้ที่นี่จะบันทึกรูปภาพที่ถ่ายโดยใช้ AVFoundation ไปยัง Photo Album ได้อย่างไร?


0

ปรับโครงสร้างใหม่อย่างรวดเร็วสำหรับ Swift 3 (มีใครสามารถทดสอบและยืนยันว่าทุกอย่างทำงานได้หรือไม่):

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

0

นี่คือคำตอบที่ยอดเยี่ยมของ Dilip เวอร์ชัน Swift3

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.