前端使用 Canvas 進行圖像處理,將手寫簽名照片轉換成掃描件效果


為什么要在前端處理圖片

最近開發業務系統時遇到了一個不常見的需求:處理圖片使除了筆跡外的內容顯示為白色/透明

業務場景:

用戶將個人簽名圖片上傳到系統中,管理員使用這個簽名打印文件。原本打印功能需要的是白底黑字的簽名,但由於沒有特殊限制,大部分用戶就直接用筆紙簽名拍照后就上傳到系統中了(實際上應該上傳白紙黑字掃描件)。這導致管理員打印時把會把照片的背景色也打印到紙張上,影響打印效果。為了方便管理員操作(避免再去讓用戶重新上傳簽名),需要提供一個能夠將手寫簽名照處理成符合打印要求的圖片的功能

目標效果

實現原理

思路: 參考PS中的色階工具,灰度轉換 + 閾值處理(二值化)

Step 1: 使用 Canvas 讀取和修改圖片的色值

在學校時就有選修過數字圖像處理的課程,所以我可以確定這個需求簡單實現起來並不會太難(產品要求不高的情況下),但是以前學習時都是使用 MATLAB 處理圖片的,對怎么使用瀏覽器處理圖片並不清楚。

經過查詢得知,Canvas 可以獲取指定區域的像素值,張鑫旭的這篇文章就是不錯的示例,相關API:

  • canvas.drawImage: 在 Canvas 上繪制圖片

  • canvas.getImageData: 獲取 Canvas 上指定區域的像素值

  • canvas.putImageData: 修改 Canvas 上指定區域的像素值

Step 2: 將用戶上傳的彩色照片轉換成灰度圖

因為最終需要的簽名是白底黑字的,不需要彩色,所以可以將圖片轉換成灰度圖方便計算:

canvas.getImageData 返回的是 Uint8ClampedArray(960000) 數組,其中 960000 = 600(原圖寬) * 400(原圖高) * 4(每個像素對應數組中4個元素),每個像素對應數組中4個元素分別是 [R, G, B, Alpha][紅, 綠, 藍, 透明度],取值范圍均為 [0-255]

灰度計算的經驗公式: Gray = 0.299 * R + 0.587 * G + 0.114 * B

const gray =
  0.299 * imageData.data[coordinate] +
  0.587 * imageData.data[coordinate + 1] +
  0.114 * imageData.data[coordinate + 2];

// 賦值給 RGB,將彩圖轉換成灰度圖
imageData.data[coordinate] = gray;
imageData.data[coordinate + 1] = gray;
imageData.data[coordinate + 2] = gray;

Step 3: 根據閾值處理圖片(色階工具)

這里取一個上閾值 thresholdWhite 和下閾值 thresholdBlack ,均為 [0-255],對所有像素做如下處理:灰度值大於上閾值修改成白色,灰度值低於下閾值修改成黑色

通過測試得到一個適用於大部分場景的閾值計算方式(其中 avg 為灰度圖的均值):

thresholdWhite = avg - 40; // 灰度大於該閾值會變成白色(255,255,255)
thresholdBlack = avg - 80; // 灰度低於該閾值會變成純黑色(0,0,0)

灰度圖處理分為兩步:

  1. 根據閾值計算色值轉換函數,使用一個一維數組表示

    const transformMatrix = Array.from({ length: 256 }); // 色值轉換函數
    for (let i = 0; i < 256; i++) {
      if (i > thresholdWhite) {
        transformMatrix[i] = 255; // 白色
      } else if (i < thresholdBlack) {
        transformMatrix[i] = 0; // 黑色
      } else {
        transformMatrix[i] = i; // 保持不變
      }
    }
    
  2. 根據色值轉換函數處理圖片

    const gray = transformMatrix[imageData.data[coordinate]];
    imageData.data[coordinate] = gray;
    imageData.data[coordinate + 1] = gray;
    imageData.data[coordinate + 2] = gray;
    

優缺點

優點:

  • 純前端實現,無需修改服務器上存儲的原圖
  • 實現簡單,沒有復雜的邏輯,用戶使用時不需要有處理圖片的知識,只需要調節閾值即可

缺點:

  • 自動處理只是簡單計算了平均值,未必是最好的效果,有時仍需要管理員手動調整
  • 照片中的陰影過黑和筆記過淡都會影響處理效果

最終效果(生產環境)

實現代碼

源碼地址

注:由於 Canvas 對跨域的限制,需要啟動一個服務訪問 index.html,Canvas 才能正常加載圖片,推薦使用 vscode 中的 live server 插件或 webstorm 自帶的預覽功能

待改進

  • 在上述基礎上,加入一些更好的算法處理圖片中的陰影
  • 考慮以圖片增加濾波器的方式,對筆跡較細的簽名進行加粗

參考鏈接

知乎:怎么去掉文件照片中的背景雜色,把底色變白?

張鑫旭:JS檢測PNG圖片是否有透明背景、摳圖等相關處理

MDN: Canvas ImageData


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM