iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 8
2
Modern Web

用 Javascript 當個影像魔術師系列 第 8

Day 08 - 圖片效果 - 對比

Day 8 圖片效果 - 對比度

對比度

今天介紹一下修照片時常用的功能,對比度調整。

對比度可以想像成圖片中亮部與暗部的差距,當對比度越高時,畫面中亮暗差距就越明顯,如果用直方圖來想像的話,可以想像成當調整對比度變高時,直方圖的像素應該會往左右兩側擴散,也就是已經黑的越黑,白的越白。而是調低的話,則直方圖應該會往中間集中。

算法

接下來看看演算法的部分吧,主要參考 這裡 的實現,下面是我的一些理解。

首先先看算法的這個部分

newRedColor = Factor * (oldRedColor -128) + 128

其中的 128,可以想像成是暗部與亮部的分界線。對比度調高時,代表亮部越亮,暗部越暗,而在這邊我們以 128 當做這個闕值。直接帶個值進去看看吧

假設今天這個值為 160 ( 也就是會被判定為亮部 ),當對比度增加時 ( 也就是 Factor > 1 ),最後算出來的值會比原本的還大,也就是亮部越亮,反之亦然。

也有看到其他算法的實現是先算出整張照片的平均亮度,然後以那個亮度當做闕值,當然效能上就會有差距。而以 128 則是直接對半切的方法,但通常來說如果是一張正常曝光的照片,這兩者差距應該不會差距太大。

所以接下來要控制變強跟變弱的幅度,就是靠控制 Factor 這個係數。

Factor = 259( C + 255 ) / 255 ( 259 - C )

在這個算法中,C 代表調整強度,範圍介於 -255 ~ 255。

當等於 0 時, Factor 為 1,套入上面算法部分沒問題,顏色不改變。

當等於 -255 時, Factor 為 0,套入上面算法,所有值都將成為 128,也就是全部都變成灰色。

當等於 255 時, Factor 為 129.5,套入上面算法,當原值為 64 時,結果為 -8160,原值為 196 時, 結果為 8934

透過更改 259 及 255 這兩個值,可以改變增加幅度,這個就看個人需求。如果覺得在調整上顏色太強烈了可以變更。

實作

雖然我們的範圍輸入控制在 -100 ~ 100 中間,目前來說,我不會考慮把上面的參數改成這樣

Factor = 259( C + 100 ) / 100 ( 259 - C )

因為就如剛剛所提,當輸入值為 -100 時,整個畫面都會變成灰階。這對於調整來說應該毫無意義。應該沒有人會想把照片調成一個全灰色的情況吧XD

但倒是覺得調整幅度有點太強烈了,所以稍微修改一下

Factor = 350( C + 255 ) / 255 ( 350 - C )

別問我這個數字怎麼來的XD,基本上就是看 PhotoShop 調整時的改變幅度來憑感覺調的結果,基本上這很主觀,所以這邊自行發揮

一樣傳入 Uint8ClampArray,並且將透過 amount 來調整對比度 ( 使用的區間是 -100 ~ 100 ),大於 0 就是增加對比, 反之則是降低。實做如下


export const contrast = (pixelData, amount) => {
  // const factor = (259 * (amount + 255)) / (255 * (259 - amount))
  const contrastThreshold = 128
  const factor = (350 * (amount + 255)) / (255 * (350 - amount))
  for (let i = 0; i < pixelData.length; i += 4) {
    pixelData[i] =
      factor * (pixelData[i] - contrastThreshold) + contrastThreshold
    pixelData[i + 1] =
      factor * (pixelData[i + 1] - contrastThreshold) + contrastThreshold
    pixelData[i + 2] =
      factor * (pixelData[i + 2] - contrastThreshold) + contrastThreshold
  }
  return pixelData
}


外觀 & 資料流程

新增 Slider

  <Slider
    title="對比度"
    :min="-100"
    :max="100"
    :value="contrast"
    @sliderChange="val => sliderChange(val, 'contrast')"
  ></Slider>

Store 裡面新增參數

state: {
    sliderValue: {
      brightness: 0,
      contrast: 0
    }

下面這段應該要找時間改一下了,未來參數只會更多

editImageData({ sliderValue, originalEditData }) {
      if (originalEditData.data) {
        const imageDataCopy = new ImageData(
          new Uint8ClampedArray(originalEditData.data),
          originalEditData.width,
          originalEditData.height
        )
        if (sliderValue.brightness !== 0) {
          filters.brightness(imageDataCopy.data, sliderValue.brightness)
        }
        if (sliderValue.contrast !== 0) {
          filters.contrast(imageDataCopy.data, sliderValue.contrast)
        }
        return imageDataCopy
      }
    }

效果

到這邊可以看一下效果囉!今天換張照片來調調看吧~

順便推薦一下這系列中用來示範的照片都拍攝自 拉達克,是一個美麗純樸的地方,喜好拍照的人一定會愛上。有空就在文章中介紹一下,明天見!

在旅途中,公路外面到處都是這種壯麗大山風景,喜歡拍風景的記得帶顆廣角鏡頭來唷!

小結

今天介紹了對比度調整,在調整的時候對比度調整過後,圖片給人的飽和度也相對應提升了,其實這是正常的,因為飽和度、對比、銳利度其實就是只調整一個還是會互相影響的唷~


上一篇
Day 07 - 直方圖顯示
下一篇
Day 09 - 圖片色彩模型
系列文
用 Javascript 當個影像魔術師30

尚未有邦友留言

立即登入留言