iOS模仿系統(tǒng)相機拍照你不曾注意過的細節(jié)

距離上次寫博客竟然過了一個月了,一方面是最近項目比較忙昏翰,另一方面是實在是有點兒懈怠了苍匆,強烈譴責一下自己。其實我最近在看一些技術書籍棚菊,發(fā)現(xiàn)一些好的書真心對自己幫助很大浸踩,看書的過程,好多原來模糊的概念统求、問題检碗,都能感覺恍然大悟。當提筆想總結成一篇文章的時候码邻,發(fā)現(xiàn)網(wǎng)上早已經(jīng)有大量的優(yōu)秀文章出現(xiàn)折剃,所以就不敢獻丑了。今天寫的一篇文章像屋,是最近自己項目中用到的怕犁,不算什么難點,只是感覺有必要記錄一下己莺。

需求

由于我們APP集成了有道翻譯的SDK奏甫,需要將拍出來的圖片翻譯成對應的語言,但是有道的SDK目前還做的不是很完善(比如:照片傾斜的時候凌受,返回的角度不是很對阵子,有道的技術說下個版本可能會更新)。于是產(chǎn)品要求拍照頁面做成跟系統(tǒng)相機類似胜蛉,當用戶橫屏拍攝的時候款筑,需要客戶端自己將圖片糾正回來智蝠,倒著拍的時候亦然。

自定義相機功能就不多說了奈梳,網(wǎng)上有大量的優(yōu)秀文章杈湾,這里隨便從網(wǎng)上找了一個,需要的可以參考下

基礎知識

首先我們需要知道每一個UIImage對象攘须,都有一個imageOrientation屬性漆撞,里面保存著方向信息:

typedef NS_ENUM(NSInteger, UIImageOrientation) {
    UIImageOrientationUp,            // default orientation
    UIImageOrientationDown,          // 180 deg rotation
    UIImageOrientationLeft,          // 90 deg CCW
    UIImageOrientationRight,         // 90 deg CW
    UIImageOrientationUpMirrored,    // as above but image mirrored along other axis. horizontal flip
    UIImageOrientationDownMirrored,  // horizontal flip
    UIImageOrientationLeftMirrored,  // vertical flip
    UIImageOrientationRightMirrored, // vertical flip
};

根據(jù)這個屬性信息,我們便可以對圖像進行相應的旋轉(zhuǎn)于宙,將圖片轉(zhuǎn)到正確的方向浮驳,如何旋轉(zhuǎn)?捞魁?有兩種解決方案:

第一種:給UIImage添加Category

- (UIImage *)fixOrientation {

    // No-op if the orientation is already correct
    if (self.imageOrientation == UIImageOrientationUp) return self;

    // 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 (self.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;

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

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

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

        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, self.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, self.size.width, self.size.height,
                                             CGImageGetBitsPerComponent(self.CGImage), 0,
                                             CGImageGetColorSpace(self.CGImage),
                                             CGImageGetBitmapInfo(self.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.height,self.size.width), self.CGImage);
            break;

        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,self.size.width,self.size.height), self.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;
}

第二種:利用drawInRect方法將圖像畫到畫布上

- (UIImage *)normalizedImage {
    if (self.imageOrientation == UIImageOrientationUp) return self; 

    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    [self drawInRect:(CGRect){0, 0, self.size}];
    UIImage *normalizedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return normalizedImage;
}

通過上面兩種方式轉(zhuǎn)換之后的UIImage對象至会,其imageOrientation屬性,都會被修改成UIImageOrientationUp谱俭,這樣將圖片傳到后臺奉件,或者導出相冊的時候,就不會出現(xiàn)照片旋轉(zhuǎn)90度的問題昆著。

但是有時候我們希望圖片該旋轉(zhuǎn)的時候县貌,按照我們的意愿旋轉(zhuǎn)(比如橫評拍攝的時候),豎直拍攝的時候凑懂,圖像正常顯示煤痕,這時候我們就不能直接用上面的方法來判斷了。仔細觀察系統(tǒng)相機的拍攝接谨,我發(fā)現(xiàn)除了豎直拍攝以外摆碉,別的情況下拍攝,圖片都會自動旋轉(zhuǎn)脓豪,這個時候就需要我們利用iPhone手機自帶的硬件傳感器對方向進行判斷巷帝,以達到我們想要的結果,這里主要用到加速儀

加速儀(類型:CMAcceleration)

加速儀可以檢測三維空間中的加速度 跑揉,坐標對應如下:

o_coremotion1

例如:當垂直手持手機且頂部向上,Y坐標上回收到 -1G的加速度埠巨,(0历谍,-1,0)辣垒,當手機頭部朝下望侈,得到的各個坐標為:(0,1勋桶,0)

主要代碼如下:

- (void)startDeviceMotion{
    if (![self.motionManager isDeviceMotionAvailable]) {return;}

    [self.motionManager setDeviceMotionUpdateInterval:1.f];
    [self.motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
        
        double gravityX = motion.gravity.x;
        double gravityY = motion.gravity.y;
        
        if (fabs(gravityY)>=fabs(gravityX)) {
            
            if (gravityY >= 0) {
                
                // UIDeviceOrientationPortraitUpsideDown
                [self setDeviceDirection:SSDeviceDirectionDown];
                NSLog(@"頭向下");
                
            } else {
                
                // UIDeviceOrientationPortrait
                [self setDeviceDirection:SSDeviceDirectionUp];
                NSLog(@"豎屏");
            }
            
        } else {
            
            if (gravityX >= 0) {
                // UIDeviceOrientationLandscapeRight
                [self setDeviceDirection:SSDeviceDirectionRight];
                NSLog(@"頭向右");
            } else {
                
                // UIDeviceOrientationLandscapeLef
                [self setDeviceDirection:SSDeviceDirectionLeft];
                NSLog(@"頭向左");
            }
        }
    }];
}

獲取到方向信息脱衙,下面就可以對圖片進行對應的處理了侥猬,主要用到了下面的這個方法:

- (instancetype)initWithCIImage:(CIImage *)ciImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(6_0);

該方法的作用是:

Creates and returns an image object with the specified scale and orientation factors.
創(chuàng)建并返回具有指定比例和方向特征的image對象。

最后對拍攝的圖片進行處理:

UIImage *transImage = [rsltImage fixOrientation];
        
        switch (self.deviceDirection) {
            case SSDeviceDirectionUp:
                transImage = [rsltImage fixOrientation];
                break;
            case SSDeviceDirectionLeft:
                transImage = [rsltImage fixImageByOrientation:UIImageOrientationLeft];
                break;
            case SSDeviceDirectionRight:
                transImage = [rsltImage fixImageByOrientation:UIImageOrientationRight];
                break;
            case SSDeviceDirectionDown:
                transImage = [rsltImage fixImageByOrientation:UIImageOrientationDown];
                break;
            default:
                break;
        }

最終效果圖

take_pic_ocr

總結

功能實現(xiàn)起來其實并不難捐韩,當時和同事糾結的地方在于退唠,到底是采用支持橫豎屏還是采用加速度傳感器上面,最后經(jīng)過分析系統(tǒng)相機荤胁,我還是采用了利用傳感器做判斷瞧预,期間也是查閱了很多的技術文章,無意中發(fā)現(xiàn)了一篇真心值得仔細閱讀的關于圖片解壓縮的文章仅政。最后垢油,再次對最近的松懈進行反思,繼續(xù)擼起袖子圆丹,加油干L渤睢!辫封!

Refrence

https://www.cnblogs.com/sunyanyan/p/5213854.html

http://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末硝枉,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子秸讹,更是在濱河造成了極大的恐慌檀咙,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璃诀,死亡現(xiàn)場離奇詭異弧可,居然都是意外死亡,警方通過查閱死者的電腦和手機劣欢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門棕诵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凿将,你說我怎么就攤上這事校套。” “怎么了牧抵?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵笛匙,是天一觀的道長。 經(jīng)常有香客問我犀变,道長妹孙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任获枝,我火速辦了婚禮蠢正,結果婚禮上,老公的妹妹穿的比我還像新娘省店。我一直安慰自己嚣崭,他們只是感情好笨触,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著雹舀,像睡著了一般芦劣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上葱跋,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天持寄,我揣著相機與錄音,去河邊找鬼娱俺。 笑死稍味,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的荠卷。 我是一名探鬼主播模庐,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼油宜!你這毒婦竟也來了掂碱?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤慎冤,失蹤者是張志新(化名)和其女友劉穎疼燥,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚁堤,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡醉者,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了披诗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撬即。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖呈队,靈堂內(nèi)的尸體忽然破棺而出剥槐,到底是詐尸還是另有隱情,我是刑警寧澤宪摧,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布粒竖,位于F島的核電站,受9級特大地震影響几于,放射性物質(zhì)發(fā)生泄漏蕊苗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一孩革、第九天 我趴在偏房一處隱蔽的房頂上張望岁歉。 院中可真熱鬧得运,春花似錦膝蜈、人聲如沸锅移。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽非剃。三九已至,卻和暖如春推沸,著一層夾襖步出監(jiān)牢的瞬間备绽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工鬓催, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肺素,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓宇驾,卻偏偏與公主長得像倍靡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子课舍,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容