iOS手機(jī)豎著拍的照片經(jīng)過前端處理之后被旋轉(zhuǎn)了90°的原因以及解決方案

在網(wǎng)頁應(yīng)用中上傳圖片的操作是非常廣泛的,比如近期我公司上線的一個(gè)小游戲“專業(yè)審核夫妻相”:

專業(yè)審核夫妻相

它的主要功能就是上傳兩張人像,通過算法進(jìn)行分析對(duì)比,最后得出一個(gè)相似度的分?jǐn)?shù),以驗(yàn)證你們是天造地設(shè)還是顏值互補(bǔ)。

但是,當(dāng)我們把上傳的圖片轉(zhuǎn)換成base64格式,發(fā)送給后臺(tái)時(shí),會(huì)發(fā)現(xiàn)偶爾會(huì)出現(xiàn)問題,有一些圖片本來是這樣的:

柴犬

處理之后卻變成了這樣:

柴犬2

經(jīng)過測(cè)試發(fā)現(xiàn),只有iOS手機(jī)豎著拍的照片才會(huì)出現(xiàn)這樣的問題,而iOS手機(jī)橫著拍的照片、Android手機(jī)拍的照片以及通過屏幕截圖、網(wǎng)絡(luò)下載等途徑獲得的圖片都不會(huì)產(chǎn)生這個(gè)問題。

那么,這到底是為什么呢?

在開發(fā)過程中,由于時(shí)間緊迫,未求甚解,使用了github上的一個(gè)開源項(xiàng)目 lrz.js 來解決此問題,這個(gè)工具的主要用途是在盡量保證圖片質(zhì)量的前提下壓縮圖片的大小,但同時(shí)也附帶了圖片旋轉(zhuǎn)角度糾正的功能。

通過閱讀 lrz.js 的源代碼,我發(fā)現(xiàn)它引入了一個(gè)叫做 exif.js 的庫(kù)來實(shí)現(xiàn)旋轉(zhuǎn)角度的糾正,它提供了js讀取圖像的原始數(shù)據(jù)的功能擴(kuò)展,例如:拍照方向、相機(jī)設(shè)備型號(hào)、拍攝時(shí)間、ISO 感光度、GPS 地理位置等數(shù)據(jù)。而拍照方向就是關(guān)鍵所在!

exif.js 獲取圖像的拍照方向的代碼如下:

EXIF.getData(IMG_FILE, function () { // IMG_FILE為圖像數(shù)據(jù)
  var orientation = EXIF.getTag(this, "Orientation");
  console.log("Orientation:" + orientation); // 拍照方向
});

獲取拍照方向的結(jié)果為1-8的數(shù)字:

拍照方向信息

注意:對(duì)于上面的八種方向中,加了*的并不常見,因?yàn)樗鼈兇淼氖晴R像方向,如果不做任何的處理,不管相機(jī)以任何角度拍攝,都無法出現(xiàn)鏡像的情況。

這個(gè)表格代表什么意義?我們來看第一行,值為1時(shí),右邊兩列的值分別為:Row #0 is Top,Column #0 is Left side,其實(shí)很好理解,它表示照片的第一行位于頂端,而第一列位于左側(cè),那么這張照片自然就是以正常角度拍攝的。

而這8種結(jié)果,就是第一行與第一列所在的位置的8種組合。

那么,我們來測(cè)試一下iOS手機(jī)橫著拍的照片,來看看它的拍照方向是什么呢?

測(cè)試1

結(jié)果是1,即以正常角度拍攝的,其實(shí)也就是原圖啦~

那么,我們?cè)贉y(cè)試一下iOS手機(jī)豎著拍的照片,來看看它的拍照方向是什么呢?

測(cè)試2

原來是6!即第一行位于右側(cè),第一列位于頂端,其實(shí)相當(dāng)于將照片順時(shí)針旋轉(zhuǎn)了90度!

所以,實(shí)際上iOS手機(jī)豎著拍出的照片與橫著拍出的照片其本質(zhì)上是一樣的,只不過豎著拍出的照片被添加了一個(gè)順時(shí)針旋轉(zhuǎn)90°拍照方向,所以顯示的時(shí)候,就變成了上下邊窄左右邊寬的狀態(tài),其實(shí)也就是橫著拍的照片順時(shí)針旋轉(zhuǎn)90°而成的~

那么明白了這些,文章開頭所說的照片旋轉(zhuǎn)bug的原因,也就很簡(jiǎn)單啦~

其實(shí)就是當(dāng)我們?cè)谇岸藢?duì)圖片進(jìn)行像素處理或者drawInRect等操作之后,照片的Orientaion信息,即為拍照方向信息被刪除了,所以iOS手機(jī)豎著拍的照片又回到了橫著的狀態(tài),看起來也就是逆時(shí)針旋轉(zhuǎn)了90°!

那么如何糾正這個(gè)旋轉(zhuǎn)角度呢?

其實(shí)思路也很簡(jiǎn)單:在處理圖片之前,先讀取并保存圖片的拍照方向信息,然后在處理圖片之后,再根據(jù)拍照方向,對(duì)圖片進(jìn)行相應(yīng)的調(diào)整,lrz.js 中的代碼如下:

switch (orientation) {
    case 3:
        ctx.rotate(180 * Math.PI / 180);
        ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height);
        break;
    case 6:
        ctx.rotate(90 * Math.PI / 180);
        ctx.drawImage(img, 0, -resize.width, resize.height, resize.width);
        break;
    case 8:
        ctx.rotate(270 * Math.PI / 180);
        ctx.drawImage(img, -resize.height, 0, resize.height, resize.width);
        break;
    case 2:
        ctx.translate(resize.width, 0);
        ctx.scale(-1, 1);
        ctx.drawImage(img, 0, 0, resize.width, resize.height);
        break;
    case 4:
        ctx.translate(resize.width, 0);
        ctx.scale(-1, 1);
        ctx.rotate(180 * Math.PI / 180);
        ctx.drawImage(img, -resize.width, -resize.height, resize.width, resize.height);
        break;
    case 5:
        ctx.translate(resize.width, 0);
        ctx.scale(-1, 1);
        ctx.rotate(90 * Math.PI / 180);
        ctx.drawImage(img, 0, -resize.width, resize.height, resize.width);
        break;
    case 7:
        ctx.translate(resize.width, 0);
        ctx.scale(-1, 1);
        ctx.rotate(270 * Math.PI / 180);
        ctx.drawImage(img, -resize.height, 0, resize.height, resize.width);
        break;
    default:
        ctx.drawImage(img, 0, 0, resize.width,resize.height);
}

其中,translate是平移變換,scale(-1,1)是向左翻轉(zhuǎn),rotate是順時(shí)針旋轉(zhuǎn)。

舉例說明 case 2,當(dāng)圖片的拍照方向?yàn)?時(shí),即第一行位于頂端,而第一列位于右側(cè),其實(shí)相當(dāng)于把照片進(jìn)行了左右的翻轉(zhuǎn)。所以,這里對(duì)圖片的操作是,先向右平移等于圖片寬度的距離,再向左翻轉(zhuǎn),這相當(dāng)于以圖片水平方向的對(duì)稱軸為軸進(jìn)行了左右翻轉(zhuǎn),然后再以(0,0)為起始點(diǎn)繪制原寬高的圖片,即完成了對(duì)拍照方向的糾正。

最后

經(jīng)過一系列的測(cè)試,發(fā)現(xiàn)確實(shí)只有iOS手機(jī)的豎拍照片與橫拍照片是通過拍照方向來區(qū)別的,Android手機(jī)無論豎拍還是橫拍的照片,拍照方向都為1,也就是說即使丟失了拍照方向這一信息,也不會(huì)影響到圖片的旋轉(zhuǎn)角度。而手機(jī)或電腦的屏幕截圖、網(wǎng)絡(luò)上的圖片、通過PS制作的圖片等也是如此。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容