前端影像處理指南

動感小前端發表於2019-03-02

計算機影像處理是一門很成熟的技術,任何一門可作業系統介面的語言都能很輕易的實現各種處理操作。但是前端限於瀏覽器環境和介面限制,處理起來會有諸多不便,這裡所說的前端影像處理,是真的指不借助任何後端服務純前端實現的影像處理。本文會介紹canvas點陣圖處理,SVG向量圖和CSS3影像處理,重點是canvas,並且最後會附上一個TrimPNG小應用

canvas點陣圖處理

HTML5 canvas為我們提供了一塊畫布,讓前端也有了操作點陣圖的功能:圖片旋轉、縮放、濾鏡、壓縮等都可以通過JS來實現。

影像基本處理

通過設定drawImage引數可以實現圖片繪製、縮放、拉伸和裁剪等操作(注意canvas無法繪製體積過大圖片,否則會卡甚至崩掉,大圖可以分塊讀取繪製):

圖片描述
圖片描述

詳細用法參考 drawImage(),DEMO原始碼戳這裡 JS Bin.

只需要drawImage一個方法,就可以實現基本圖形處理功能,再結合滑鼠或滾輪事件,就可以實現更復雜的區域性放大預覽,縮放等功能了。

影像濾鏡處理

現在的朋友圈發個圖都要用濾鏡美一下,復古清純膠片LOMO各種風格應有盡有。canvas提供了getImageData方法來獲取影像上每一個畫素點的RGBA資訊,這樣我們就能對圖片進行畫素級處理了。通過特定演算法來重寫imageData中的畫素資訊,然後用setImageData方法把新的資料重新繪製在canvas上,這樣就可以實現影像濾鏡打碼加特效等一系列功能。

比如我們現在要實現一個復古濾鏡:

// 復古濾鏡處理演算法:獲取每個畫素的RGB資訊,並按特定權重返回其加權平均值
let sepiaFilter = function(imgData) {
  let d = imgData.data
  for (let i = 0; i < d.length; i += 4) {
    let r = d[i]
    let g = d[i + 1]
    let b = d[i + 2]
    d[i] = (r * 0.393) + (g * 0.769) + (b * 0.189) // red
    d[i + 1] = (r * 0.349) + (g * 0.686) + (b * 0.168) // green
    d[i + 2] = (r * 0.272) + (g * 0.534) + (b * 0.131) // blue
  }
  return imgData
}
//影像地址必須和當前頁面同域,否則會報cross-origin錯誤 
img.src = `/img/logo@2x.png` 
img.onload = () => {
  ctx.drawImage(img, 0, 0) // 繪製原圖
  let imgData = ctx.getImageData(0, 0, img.width, img.height) // 獲取圖片資訊
  ctx.putImageData(sepiaFilter(imgData), 100, 0) // 繪製處理後圖片
}複製程式碼
圖片描述
圖片描述

詳細用法參考 getImageData()putImageData(),DEMO原始碼戳這裡 JSFiddle.

濾鏡處理關鍵在於濾鏡演算法,要想寫出更高階的特效需要有計算機圖形學基礎,對卷積矩陣、拉普拉斯變換、傅立葉變換等數學知識也要了解,這個坑很大我就不挖了。

影像base64儲存

加完特效後如果我們想把影像儲存下來,就可以用toDataURL方法來對圖片進行格式轉換、壓縮儲存了。

// 支援三種檔案型別:image/png(預設) | image/jpeg | image/webp(僅Chrome)
canvas.toDataURL() // 預設儲存為png
// 僅jpeg/webp支援質量引數(0~1,預設0.92)
canvas.toDataURL("image/jpeg", 0.1) // 儲存為質量為0.1的jpeg複製程式碼

由於儲存形式是base64編碼,原來圖片的每三個位元組都會被擴充套件成4位元組,所以整體上編碼後資料會比原來多約1/3。以下是通過toDataURL儲存後的圖片和原圖大小相關對比資料(資料僅供參考,不具通用性):

圖片描述
圖片描述

可見儲存後圖片體積並不是原來的4/3,實際上處理後的圖片都會比原圖大好幾倍,並且不同的圖片增大的體積也是不確定的。如果是要上傳圖片到伺服器,可以把base64轉化成Blob二進位制資料壓縮上傳;如果要直接在前端顯示或供使用者下載,jpg還好可以設定質量引數,要是png就沒法壓縮了。

只是用toDataURL還不夠,使用者需要通過手動點選圖片-右鍵圖片儲存為來儲存圖片,如果要實現點選下載按鈕自動下載圖片還需要修改圖片型別為octet-stream,然後利用HTML5的download屬性強制讓瀏覽器下載。

詳細用法參考toDataURL(). 自動下載圖片DEMO原始碼快戳我 JSFiddle.

程式設計師的備胎

有了以上基礎,再結合成熟的圖形處理演算法,我們可以完成日常工作中大部分影像處理需求,以下列出了一些相關輪子可做備胎。備胎這種東西多多益善,萬一以後用到了呢?

注意:本人很專一。

html2canvas

將web頁面通過canvas來實現截圖,其原理就是遍歷DOM結構和樣式,然後在canvas中繪製出來,通過toDataURL輸出圖片。但由於canvas圖片的同源策略限制,如果圖片和網站不同源的話會擷取不出來的。另外在微信中測試時,即使用同源的圖片擷取出來的圖片也有問題,所以要想將其用於生產環境,還是得看場景,有很多坑要踩。

code-to-image

程式碼轉圖片工具。有時候你在不同的平臺寫文章貼程式碼,由於不同平臺程式碼格式化規範不一,所以經常會出現程式碼排版問題,通過這個工具將程式碼轉成圖片就可以避免排版問題了。

Cropper

一個專門用來做圖片裁剪的應用。

tracking.js

這是一個專業的計算機視覺處理JS庫,包含了大量圖形處理演算法,可用來做人臉識別,色彩追蹤等酷炫功能。

qrcode2

用JS動態生成二維碼,這個庫還是很實用的,原理就是qrcode演算法+canvas繪圖,不支援canvas的用table相容。

AlloyImage

騰訊出的基於HTML5的專業級影像處理開源引擎,功能很強大,簡直就是Web版的PS。

以上列舉部分,更多備胎在此: github.com/0326/canvas

SVG向量圖處理

講完點陣圖再說向量圖。向量圖在繪製圖示、商業LOGO、動畫元素上應用非常廣範。Web最開始支援的向量圖形並不是現在的SVG,而是微軟主推的VML,所以在低版本IE下面只支援VML而不支援SVG,直到後來SVG成為W3C標準並被普及,微軟才在IE9中支援SVG。

SVG遵循XML規範,可以很好的整合在HTML裡面,同時支援JS指令碼控制,還有基於SMIL標準的動態內容支援,做起動畫來也是非常方便。目前基於SVG的JS圖形庫輪子也是非常多,如svg.js, Snap.svg, Velocity.js, D3.js等等,目前暫無SVG應用需求,等用的時候再翻牌子吧。

CSS影像處理

如果你的影像特效只是用來給使用者展示,並不需要儲存的話,可以直接用CSS處理,基本的調整圖片大小、拉伸、旋轉、裁切等操作直接幾個CSS屬性width/height/skew/rotate/clip-path等就能搞定了。如果想加特效,使用CSS濾鏡Duang的一下就出來了,不需要任何圖形學基礎和數學知識:

詳細說明見 CSS filter – MDN, DEMO 原始碼見JS Bin.

總結

最後介紹個小工具TrimPNG。大家都知道TinyPNG可以壓縮PNG, TrimPNG顧名思義就是去除圖片空白和白邊的,實現了自動摳圖和切圖的功能(logo我是直接抄的TinyPNG,都是熊貓,應該不要緊?)

圖片描述
圖片描述

用到的技術點上面都說到了,裁切實現原理就是橫向縱向分別掃描兩次畫素點陣,找出上下左右最外面的非透明點,然後定位出有效影像區域。去除白邊演算法目前還比較傻逼,只要是接近白色的點都被我幹掉了,後面再完善好了 去白邊演算法優化了一下,採用標記清除策略,先掃描一遍標記出可疑白點,然後再掃描一遍,凡是與透明區域相鄰的可疑白點就清除,當然這個演算法也比較死,如果有更專業更智慧的請務必告訴我。

體驗地址:quanfeng.tech/trimpng/

本文只拋磚,作為工作時技能儲備,如有任何補充歡迎留言交流:)

相關文章