騷操作,如何用 Python 給照片換色

  • 2019 年 10 月 28 日
  • 筆記

以下文章來源於進擊的Coder,作者崔慶才

作者:崔慶才

轉自:進擊的coder

最近遇到了一個需求,就是對圖片進行色彩風格轉換,讓一個物體可以以各種不同的色彩來呈現。

比如一個紅色的蘋果,我想把它轉化成綠色,這可怎麼辦呢?本來想的解決方案是先識別邊界,然後對邊界內區域進行色彩替換或者填充,這樣整個流程就分成了兩步,首先需要進行邊界判斷,有了邊界之後才能對某些區域顏色進行替換填充,填充的區域還是不規則的,填充的顏色也需要根據實際的情況來變化,同時還要兼顧陰影、高光等等的處理,想想真是頭大。

然後我就突然想到了之前學 PS 的時候,曾經做過對某一張圖片進行色調替換,我還記得當時調的內容是「色相」,通過調節這個值可以實現各種色調的轉換。這之後我就心想,能不能用程式來做這件事呢?

於是我就開始了對影像色彩的一些研究,研究完了之後就實現了用 Python 來更換圖片色調的功能,將這篇文章順便記錄下來。

先給大家看看效果吧,就比如我從百度上隨便搜一張圖,比如熱氣球吧,是這樣子:

原圖

我把它轉成紅色、黃色、藍色、紫色,基本就是這個樣子:

紅色風格

黃色風格

綠色圖片

藍色圖片

這裡的圖片都是經過 Python 自動轉換演算法實現的,主要調節的就是色相。其實程式碼實現是比較簡單的,但在實現之前需要了解一些影像色彩知識。了解了這些之後我們再實現才會更加遊刃有餘。

下面我們就首先來了解一下影像色彩的基本知識,然後用 Python 實現色調轉換演算法吧。

RGB

首先讓我們來了解下顏色的三原色,RGB。

RGB,其實就是三種顏色,分別代表紅色(Red)、綠色(Green)、藍色(Blue),用這三種原色顏色混合可以表示任意的顏色。RGB 是根據顏色發光的原理來設計的,比如這裡有紅綠藍三道光,當三束光混合在一起的時候,其呈現的最終的光效顏色就取決於這三種原色光的強弱了。

•比如說紅光最強,綠光和藍光幾乎沒有,那麼最後結果肯定呈現為紅光了。•如果藍光很弱,紅光和綠光非常強,那麼結果就是紅光和綠光的混合光,也就黃光。•如果紅綠藍三種光都非常強,那麼就會呈現三種光的混合光,也就是白光。•如果三種光都非常弱,那就幾乎沒有光,自然就是黑色了。

如果大家了解過 RGB 的一些知識的話,想必也見過這張混合光圖:

但是這僅僅是一張平面圖,並不能很好地體現三原色的混合關係,大家可以看這張圖:

實際上可以對 RGB 色值建立一個三維坐標系,坐標原點就在上圖中所看不見的那個正方體頂點,像左延伸為 R 紅色軸,向右延伸為 B 藍色軸,向上延伸為 G 綠色軸。

RGB 在進行色彩表示時使用了 256 階,也就是從 0-255,可以由一個位元組來表示。數值越大,RGB 三個軸,每個軸對應的數值越大,就代表其亮度越高,最亮就對應著 255,最暗就對應著 0。三個軸上的三個數字聯合起來可以用來表示一個顏色。

例如:

•(255, 255, 255) 就代表 RGB 都是滿的,組成白色。•(255, 255, 0) 就代表 R 紅色是滿的,G 綠色是滿的,B 藍色是沒有的,紅色和綠色混合為黃色,所以它就表示黃色。•(0, 0, 0) 就代表 RGB 都是沒有的,呈現黑色。

因此,通過這三個數值,我們就可以實現任何顏色的表示了。

HSV

但了解了 RGB 顯然不能應對我們的需求,它和色相也沒有什麼關係,所以這裡我們還需要研究另外一個顏色模型,叫做 HSV。

HSV,又叫做 HSB,其實也是三個參數,分別是色調(Hue)、飽和度(Saturation)、明度(Brightness),在 HSV 中 V 代表 Value,也是明度的意思。通過這三個值,我們同樣可以表示任意的顏色。

首先我們看看 HSV (HSB) 顏色模型的坐標軸圖吧,它可以用這麼一個錐形的坐標來表示:

色調,Hue,它是一種角度度量,就是圖中 Hue 所示的角度,繞圓錐體一周。它的值是從紅色開始逆時針方向計算的,紅色是 0,綠色是 120,藍色為 240,形成一個紅綠藍三原色組成的色帶。

飽和度,Saturation,它是圖中 Saturation 所示的方向,由最上面圓的圓心向外擴散。它表示顏色接近光譜色的程度。一種顏色,可以看成是某種光譜色與白色混合的結果。其中光譜色所佔的比例愈大,顏色接近光譜色的程度就愈高,顏色的飽和度也就愈高。飽和度高,顏色則深而艷。光譜色的白光成分為 0,飽和度達到最高。通常取值範圍為 0-100,值越大,顏色越飽和。也就是說圖中,圓心處飽和度為 0,越靠近邊,飽和度越高,最高達到 100。

明度,Brightness,它表示顏色明亮的程度,對於光源色,明度值與發光體的光亮度有關,對於物體色,此值和物體的透射比或反射比有關。通常取值範圍為 0-100,0 對應黑色,100 對應白色。

另外大家如果用到取色板的話,它是這樣子的:

你可以看到左邊有個大的矩形,右邊有個豎直的彩虹帶。由於剛才所說的圓錐坐標不好表示,在調色板裡面就把圓錐坐標拆成了兩部分。

•右邊的彩虹帶就可以選擇色調 Hue,拖動彩虹帶的箭頭位置就可以調整 Hue 的值。•左邊的正方形就和飽和度 Saturation、明度 Brightness 有關,橫坐標表示飽和度 Saturation,縱坐標表示 Brightness,從左到右,飽和度 Saturation 變高,從下到上,明度 Brightness 變高。•左下角,由於飽和度 Saturation 和明度 Brightness 都為 0,就呈現黑色。•左上角,由於飽和度 Saturation 為 0,所以相當於沒有顏色,顏色最淺,但是它的明度 Brightness 達到了最高,所以它就顯示沒有顏色的明度最高的色,即白色。•右上角,由於飽和度 Saturation 和明度 Brightness 都為最高,那就顯示最純的 Hue 值。

這也就是 HSB 顏色模型的原理。

同樣地,我們可以用 HSB 的三個值來表示任意的顏色,因此 HSB 也成為表示顏色的基本標準之一。

HSB 和 RGB 的轉換

HSB 和 RGB 都能表示一個顏色,它們之間也是可以相互轉換的,可以一一對應。

他們之間的轉換邏輯這裡就不再專門對其公式展開詳解了,公式總結如下:

RGB to HSB

HSV to RGB

大家如果感興趣想要研究的話也可以去:https://www.rapidtables.com/convert/color/hsv-to-rgb.html 來了解一下詳情,這裡還有詳細的轉換表格以及實時轉換實現。

另外對於它們之間的轉換演算法,很多庫都已經封裝好了。我們可以直接調用,比如 Python 中的 colorsys 模組,就實現了 rgb_to_hsv 和 hsv_to_rgb 演算法,我們也可以直接使用。

色調轉換

了解了以上內容之後,我們就可以使用程式來實現色調轉換了。相比我們已經知道應該改什麼內容了,那就是修改 HSV 中的 H 值,通過不同的 H 值我們就可以將圖片轉換為不同的色調了。

具體思路是怎樣的呢:

•首先獲取影像每個像素的的 RGB 色值。•將 RGB 色值轉化為 HSV 色值。•調整 HSV 色值中的 H。•將 HSV 色值轉回 RGB 色值。•輸出影像。

一共就是這麼五步,這裡的兩個轉換色值的操作我們就可以藉助於 colorsys 模組。另外需要安裝一個 pillow 模組:

pip3 install pillow

安裝完畢之後,我們準備任意一張圖片,然後實現上面的五個步驟。

最終的程式碼實現如下:

import colorsys  from PIL import Image    # 輸入文件  filename = 'input.jpg'  # 目標色值  target_hue = 0    # 讀入圖片,轉化為 RGB 色值  image = Image.open(filename).convert('RGB')    # 將 RGB 色值分離  image.load()  r, g, b = image.split()  result_r, result_g, result_b = [], [], []  # 依次對每個像素點進行處理  for pixel_r, pixel_g, pixel_b in zip(r.getdata(), g.getdata(), b.getdata()):      # 轉為 HSV 色值      h, s, v = colorsys.rgb_to_hsv(pixel_r / 255., pixel_b / 255., pixel_g / 255.)      # 轉回 RGB 色系      rgb = colorsys.hsv_to_rgb(target_hue, s, v)      pixel_r, pixel_g, pixel_b = [int(x * 255.) for x in rgb]      # 每個像素點結果保存      result_r.append(pixel_r)      result_g.append(pixel_g)      result_b.append(pixel_b)    r.putdata(result_r)  g.putdata(result_g)  b.putdata(result_b)    # 合併圖片  image = Image.merge('RGB', (r, g, b))  # 輸出圖片  image.save('output.png')

具體的實現在程式碼注釋裡面已經很清楚了。

在這裡我們將 target_hue 定義為 0。通過前文我們知道,Hue 為 0 代表紅色,120 代表綠色,240 代表藍色。我們可以自定義 0-355 這 360 個數值,實現不同的色調轉換。

如果想輸出其他顏色的圖片,就把 target_hue 這個值改一下就好了。

看看最後的運行效果,輸入是一張原圖:

所以最後的輸出效果就是如下的結果:

如果將程式碼中的 target_hue 值進行更改,就會呈現不同的顏色風格了,就像文中開頭所示的一樣。

比如把飛豬的 Logo 由黃色變為紅色,都是可以做到的:

處理透明像素

上面的演算法僅僅考慮了 RGB,如果有些圖包含了透明像素,上面的程式對於透明像素是無法處理的,最後輸出的結果會帶有某種顏色的背景。

對於透明像素的處理,我們可以增加一個維度的值,就是 A,即 Alpha 透明度。

所以使用 RGBA 和 HSV 的轉換我們就可以實現透明像素的處理了,程式碼實現如下:

import colorsys  from PIL import Image    # 輸入文件  filename = 'input.png'  # 目標色值  target_hue = 0    # 讀入圖片,轉化為 RGB 色值  image = Image.open(filename).convert('RGBA')    # 將 RGB 色值分離  image.load()  r, g, b, a = image.split()  result_r, result_g, result_b, result_a = [], [], [], []  # 依次對每個像素點進行處理  for pixel_r, pixel_g, pixel_b, pixel_a in zip(r.getdata(), g.getdata(), b.getdata(), a.getdata()):      # 轉為 HSV 色值      h, s, v = colorsys.rgb_to_hsv(pixel_r / 255., pixel_b / 255., pixel_g / 255.)      # 轉回 RGB 色系      rgb = colorsys.hsv_to_rgb(target_hue, s, v)      pixel_r, pixel_g, pixel_b = [int(x * 255.) for x in rgb]      # 每個像素點結果保存      result_r.append(pixel_r)      result_g.append(pixel_g)      result_b.append(pixel_b)      result_a.append(pixel_a)    r.putdata(result_r)  g.putdata(result_g)  b.putdata(result_b)  a.putdata(result_a)    # 合併圖片  image = Image.merge('RGBA', (r, g, b, a))  # 輸出圖片  image.save('output.png')

在這裡就是增加了對透明像素的處理,在影像 convert 和 merge 的時候都使用 RGBA 模式,就可以保留原有圖片中的透明像素了。

以上便是使用 Python 程式自動調整色調的實現。

其他應用

另外對於影像調色的應用還有很多,比如我們可以不直接指定 Hue 的值,而是將 Hue 的值在原來的基礎上增加或減少,則可以實現彩虹變色,如:

原圖是這樣子:

經過處理之後我們可以得到這樣的效果:

這樣我們使用程式處理出各種風格的好看的照片了,是不是很有意思呢?具體的實現可以參考:https://stackoverflow.com/questions/27041559/rgb-to-hsv-python-change-hue-continuously。

大家如果覺得有意思,就快來試試吧~

參考鏈接:

•https://blog.csdn.net/weixin_43280713/article/details/84133915•https://stackoverflow.com/questions/27041559/rgb-to-hsv-python-change-hue-continuously•https://blog.csdn.net/qq_33759946/article/details/90295377

圖片來源於互聯網公開內容,如有圖片侵權,請聯繫我刪除,謝謝。

Tags: