背景
最近在QQ群里看見別人發一張圖,縮略圖看着是一個樣子,但打開大圖以后卻發現是另一張圖,仔細觀察這兩張圖發現縮略圖是白色背景,大圖是黑色背景,結合縮略圖所在的對話框是白色的,打開大圖的UI是黑色的,很容易猜到應該是依靠透明度實現的功能,因此研究了一下怎么用代碼通過將兩張圖片混合來構造出滿足要求的圖片。
思路
首先,混合的圖片必須是灰度圖(黑白圖),理由之后再講。灰度圖的每個像素都是一個0到255的整數,分別表示從純黑到純白的顏色。設想要構造出的灰度圖每個像素顏色為c,每個像素都有一個透明度,是一個0到1的小數,設為a。當我們將一張半透明的圖片覆蓋在背景上時,得到的圖片每個像素的顏色既不是這個像素的本來顏色,也不是背景的顏色,而是介於這兩種顏色之間的顏色,即將這兩個顏色按一定比例進行混合,這個混合比例就是透明度,也稱為Alpha,因此這一過程稱為Alpha混合。如果設背景顏色為b,混合得到的顏色為n,則有公式:
那么,我們就可以列兩個方程了:
這樣,我們給出兩張灰度圖,它們就是我們想看到的n黑和n白,只要解出c和a,就可以構造出想要的圖片了。但是這里有一個問題,因為255×(1-a)始終是大於0的,所以n白將始終大於n黑,換言之,想在白色背景下顯示的圖片的每個像素值必須大於想在黑色背景下顯示的圖片對應位置的像素值。為了滿足這個需求,同時保證同一圖片各個像素值的相對大小,可以將黑色背景下顯示的圖片的像素顏色從0-255映射到0-127,把白色背景下顯示的圖片的像素顏色從0-255映射到128-255,這樣就可以了。映射可以這樣計算:
為什么混合的圖片不能是彩色圖呢,彩色圖的每個像素都是一個三維向量,分別表示紅色的強度,藍色的強度和綠色的強度。代入到方程中會發現n白和n黑已經變成三維向量了,但它們的差值仍是一個標量,也就是說,白色背景下顯示的圖片的每個像素值和黑色背景下顯示的圖片對應位置的像素值的紅色、藍色、綠色強度差值完全相同。如果用HSV色域來解釋,就是必須保證兩張圖片的每個像素的色相和飽和度完全相等,只有亮度不一樣,而這肯定不能保證,因此混合的圖片不能是彩色圖。
代碼
import cv2
import numpy as np
imgb = cv2.imread('test1.png', 0)
imgw = cv2.imread('test2.png', 0)
imgb = imgb // 2
imgw = imgw // 2 + 128
a = 255 - (imgw - imgb) # 因為實際圖片格式中Alpha是一個0-255的整數,所以這里計算的實際是上面公式中的a*255的值。
c = np.uint8(imgb / (a / 255 + 1e-3)) # 加1e-3防止當Alpha等於0的時候除0錯誤
imgn = cv2.cvtColor(c, cv2.COLOR_GRAY2BGRA)
imgn[:, :, 3] = a
cv2.imwrite('testo.png', imgn)