2017-03-18 56 views
1

我想做一些顏色轉換,例如給定的RGB通道頻道混合物與枕頭

R = G + B/2 

或其中的信道值是基於相同的像素的其他通道的值計算出的一些其他變換。

看來.point()函數只能在一個通道上運行。有沒有辦法做我想要的?

+0

順便說一句,您可能也有興趣http://stackoverflow.com/q/40233986/4014959 –

回答

1

使用PIL.ImageChops的替代方法是將圖像數據轉換爲Numpy數組。 Numpy使用本地機器數據類型,並且其編譯的例程可以非常快速地處理數組數據,與在Python數字對象上執行Python循環相比。因此Numpy代碼的速度與使用ImageChops的速度相當。你可以在Numpy中進行各種數學運算,或者使用SciPy等相關庫。

Numpy提供了一個函數np.asarray,它可以從PIL數據創建一個Numpy數組。而PIL.Image有一個.fromarray方法來加載來自Numpy陣列的圖像數據。

下面是一個腳本,顯示了兩種不同的Numpy方法,以及基於kennytm的ImageChops代碼的方法。

#!/usr/bin/env python3 

''' PIL Image channel manipulation demo 

    Replace each RGB channel by the mean of the other 2 channels, i.e., 

    R_new = (G_old + B_old)/2 
    G_new = (R_old + B_old)/2 
    B_new = (R_old + G_old)/2 

    This can be done using PIL's own ImageChops functions 
    or by converting the pixel data to a Numpy array and 
    using standard Numpy aray arithmetic 

    Written by kennytm & PM 2Ring 2017.03.18 
''' 

from PIL import Image, ImageChops 
import numpy as np 

def comp_mean_pil(iname, oname): 
    print('Loading', iname) 
    img = Image.open(iname) 
    #img.show() 

    rgb = img.split() 
    half = ImageChops.constant(rgb[0], 128) 
    rh, gh, bh = [ImageChops.multiply(x, half) for x in rgb] 
    rgb = [ 
     ImageChops.add(gh, bh), 
     ImageChops.add(rh, bh), 
     ImageChops.add(rh, gh), 
    ] 
    out_img = Image.merge(img.mode, rgb) 
    out_img.show() 
    out_img.save(oname) 
    print('Saved to', oname) 

# Do the arithmetic using 'uint8' arrays, so we must be 
# careful that the data doesn't overflow 
def comp_mean_npA(iname, oname): 
    print('Loading', iname) 
    img = Image.open(iname) 
    in_data = np.asarray(img) 

    # Halve all RGB values 
    in_data = in_data // 2 

    # Split image data into R, G, B channels 
    r, g, b = np.split(in_data, 3, axis=2) 

    # Create new channel data 
    rgb = (g + b), (r + b), (r + g) 

    # Merge channels 
    out_data = np.concatenate(rgb, axis=2) 

    out_img = Image.fromarray(out_data) 
    out_img.show() 
    out_img.save(oname) 
    print('Saved to', oname) 

# Do the arithmetic using 'uint16' arrays, so we don't need 
# to worry about data overflow. We can use dtype='float' 
# if we want to do more sophisticated operations 
def comp_mean_npB(iname, oname): 
    print('Loading', iname) 
    img = Image.open(iname) 
    in_data = np.asarray(img, dtype='uint16') 

    # Split image data into R, G, B channels 
    r, g, b = in_data.T 

    # Transform channel data 
    r, g, b = (g + b) // 2, (r + b) // 2, (r + g) // 2 

    # Merge channels 
    out_data = np.stack((r.T, g.T, b.T), axis=2).astype('uint8') 

    out_img = Image.fromarray(out_data) 
    out_img.show() 
    out_img.save(oname) 
    print('Saved to', oname) 

# Test 

iname = 'Glasses0.png' 
oname = 'Glasses0_out.png' 

comp_mean = comp_mean_npB 

comp_mean(iname, oname) 

輸入圖像

Glasses, by Gilles Tran

輸出圖像

Transformed Glasses

FWIW,該輸出圖像是使用comp_mean_npB創建。

由於它們執行計算的方式不同,3個函數產生的計算通道值可能相差1,但當然這些差異不容易看到。 :)

+0

偉大的答案,這種方法是靈活的,可重複使用。 – Paolo

1

對於這個特定的操作中,色彩變換可以寫爲矩陣乘法,所以你可以使用convert()方法與自定義矩陣(假設沒有alpha通道):

# img must be in RGB mode (not RGBA): 
transformed_img = img.convert('RGB', (
    0, 1, .5, 0, 
    0, 1, 0, 0, 
    0, 0, 1, 0, 
)) 

否則,可以split()將圖像轉化爲每個色帶的3或4張圖像,應用您喜歡的任何操作,最後將這些帶恢復爲單個圖像。同樣,原始圖像應該是RGB或RGBA模式。

(red, green, blue, *rest) = img.split() 
half_blue = PIL.ImageChops.multiply(blue, PIL.ImageChops.constant(blue, 128)) 
new_red = PIL.ImageChops.add(green, half_blue) 
transformed_img = PIL.Image.merge(img.mode, (new_red, green, blue, *rest))