利用 Canvas 將白色背景的 JPEG 圖片轉成透明的 PNG 圖片

做者 @zwhu
源碼 @raw
原文章 @github前端

使用場景

老闆讓我這個不會P圖的僞前端把公司的Logo放到公司網站上,結果給了我一張 JPEG 格式的圖片,做爲一個有追求的碼農,怎麼能現學 ps,因而利用一點HTML5+NODE的知識寫了個轉換的腳本。node

此腳本的功能是將 JPEG 格式的圖片中的白色背景轉成透明,而後再保存成PNG格式的圖片;若是圖片自己有不想被轉換的白色區域,使用其餘方法吧。git

實現原理

原理很簡單,只有兩點:github

RGBA

RGBA的解釋 咱們知道圖片由不少個像素點組成的,每一個像素點都有顏色,而顏色是由三基色RGB構成。而A是Alpha通道,用做不透明度參數,0%爲徹底透明,100%是徹底不透明。因此說若是咱們想實現白色背景的JPEG 圖片轉成透明的 PNG 圖片,只須要將白色背景對應的像素點得Alpha值變成0就行了。canvas

canvas

HTML5新增長了canvas,能夠用來繪製圖形,也能夠對圖片的像素進行操做。經過 getImageData() 方法能夠返回原始的像素信息 ImageData 對象。ImageData 對象中的像素是可寫的(由 RGBA 組成),所以咱們能夠修改像素的Alpha通道值,而後再經過 putImageData() 方法將這些像素複製到畫布中。數組

部分代碼

有了上面的知識,咱們能夠很輕鬆的經過查 canvas 的 API 來寫出轉換的代碼(ES6),代碼在下面,代碼不難,也寫了很詳細的註釋,雖然我是用 node-canvas 實現的,可是改爲瀏覽器版本的話,也不須要幾行代碼,原理是想通的:瀏覽器

import Canvas  from 'canvas'
import fs from 'fs'

const Image = Canvas.Image

// 初始化 img 和 start time
// 獲取命令行輸入的源圖片和保存的圖片地址
let img = new Image
  , start = new Date()
  , rawPath =  process.argv[2]
  , savePath = process.argv[3]

// 在命令行中沒有輸入圖片地址,拋錯
if(!rawPath)
  throw new Error('input raw image path')

if(!savePath)
  throw new Error('input save image path')

img.onerror = function(err){
  throw err
}

// 圖片加載完成
img.onload = function(){
    //    獲取圖片的width和height
  let width = img.width
    , height = img.height
    , canvas = new Canvas(width, height)
    , ctx = canvas.getContext('2d')

  // 將源圖片複製到畫布上
  // canvas 全部的操做都是在 context 上,因此要先將圖片放到畫布上才能操做
  ctx.drawImage(img, 0, 0, width, height)

  let imageData = ctx.getImageData(0, 0, width, height)
  
  // 獲取畫布的像素信息
  // 是一個一維數組,包含以 RGBA 順序的數據,數據使用  0 至 255(包含)的整數表示
  // 如:圖片由兩個像素構成,一個像素是白色,一個像素是黑色,那麼 data 爲
  // [255,255,255,255,0,0,0,255] 
  // 這個一維數組能夠當作是兩個像素中RBGA通道的數組的集合即:
  // [R,G,B,A].concat([R,G,B,A])
    , data = imageData.data

 // 對像素集合中的單個像素進行循環,每一個像素是由4個通道組成,因此 i=i+4
  for(let i = 0; i < data.length; i+=4) {
      // 獲得 RGBA 通道的值
    let r = data[i]
      , g = data[i+1]
      , b = data[i+2]

    // 咱們從最下面那張顏色生成器中能夠看到在圖片的右上角區域,有一小塊在
    // 肉眼的觀察下基本都是白色的,因此我在這裏把 RGB 值都在 245 以上的
    // 的定義爲白色
    // 你們也能夠本身定義的更精確,或者更寬泛一些
    if([r,g,b].every(v => v < 256 && v > 245)) data[i+3] = 0
  }
  
  // 將修改後的代碼複製回畫布中
  ctx.putImageData(imageData, 0, 0)

  // 將修改後的圖片保存
  let out = fs.createWriteStream(`${__dirname}/${savePath}`)
    , stream = canvas.pngStream()

  stream.on('data', function (chunk) {
    out.write(chunk)
  })

  stream.on('end', function () {
    console.log(`保存到 ${__dirname}/${savePath}`)
    console.log(`耗時: ${new Date()-start}ms`)
  })
}

img.src = `${__dirname}/${rawpath}`

src

相關文章
相關標籤/搜索