大家好,我是小小明,在學(xué)習(xí) 好友葉庭云 介紹的一門中國(guó)大學(xué)MOOC的課程中,學(xué)到手繪圖像,下面我測(cè)試并總結(jié)一下。
課程鏈接是:https://www.icourse163.org/course/BIT-1001870002?from=searchPage
下面使用Python Imaging Library ( PIL ) 進(jìn)行圖像處理,安裝方式:
pip install pillow
彩色圖片轉(zhuǎn)手繪線稿的原理簡(jiǎn)述
對(duì)于一張手繪圖,其特征主要是邊界線條較重、相同或相近色彩趨于白色、略有光源效果。
將彩色圖片轉(zhuǎn)換為手繪線稿,首先需要將圖片轉(zhuǎn)換為灰度圖片。
灰度值實(shí)際代表了圖像明暗的變化,而梯度代表了灰度的變化率。
調(diào)整像素的梯度值可以間接改變圖像的明暗程度。
下面以如下圖片為例進(jìn)行測(cè)試:
from PIL import Image
import numpy as np
im = Image.open('001.jpg')
im
轉(zhuǎn)換為灰度模式:
im = im.convert("L")
im
獲取灰度圖片像素點(diǎn)數(shù)據(jù)的numpy數(shù)組:
data = np.array(im)
data.shape, data.dtype
((300, 300), dtype('uint8'))
調(diào)整像素的梯度值
調(diào)整像素的梯度值間接改變圖像的明暗程度
首先提取出x和y方向的梯度:
grad_x, grad_y = np.gradient(data)
print(grad_x.shape, grad_y.shape)
(300, 300) (300, 300)
深度值的取值范圍為0-100,決定了梯度的縮放比例,現(xiàn)在預(yù)設(shè)深度值為10:
depth = 10
grad_x /= 100/depth
grad_y /= 100/depth
光源效果
根據(jù)灰度變化來模擬人類視覺的遠(yuǎn)近程度
- 設(shè)計(jì)一個(gè)位于圖像斜上方的虛擬光源
- 光源相對(duì)于圖像的俯視角為 eLevation,方位角為 Azimuth
- 建立光源對(duì)個(gè)點(diǎn)梯度值的影響函數(shù)
- 運(yùn)算出各點(diǎn)的新像素值
]
假設(shè)俯視角eLevation=
π
2.2
\frac \pi {2.2}
2.2π?,方位角Azimuth=
π
4
\frac \pi {4}
4π?:
eLevation = np.pi / 2.2
azimuth = np.pi / 4
# 光源對(duì)x/y/z軸三個(gè)方向的影響程度
dx = np.cos(eLevation) * np.cos(azimuth)
dy = np.cos(eLevation) * np.sin(azimuth)
dz = np.sin(eLevation)
梯度歸一化
構(gòu)造x和y軸梯度的三維歸一化單位坐標(biāo)系:
A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A
梯度與光源相互作用,將梯度轉(zhuǎn)化為灰度:
b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)
最終看看轉(zhuǎn)換效果:
Image.fromarray(b.clip(0, 255).astype('uint8'))
代碼封裝
from PIL import Image
import numpy as np
from PIL.ImageFile import ImageFile
from typing import Union
def img2sketch(img: Union[str, ImageFile], depth=10, eLevation=np.pi / 2.2, azimuth=np.pi / 4):
assert isinstance(img, str) or isinstance(img, ImageFile)
assert 0 < depth <= 100
if isinstance(img, str):
im = Image.open(img)
else:
im = img
data = np.array(im.convert("L"))
grad_x, grad_y = np.gradient(data)
# 根據(jù)深度縮放梯度
grad_x /= 100/depth
grad_y /= 100/depth
# 光源對(duì)x/y/z軸三個(gè)方向的影響程度
dx = np.cos(eLevation) * np.cos(azimuth)
dy = np.cos(eLevation) * np.sin(azimuth)
dz = np.sin(eLevation)
# 構(gòu)造x和y軸梯度的三維歸一化單位坐標(biāo)系
A = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1.)
uni_x = grad_x / A
uni_y = grad_y / A
uni_z = 1. / A
# 梯度與光源相互作用,將梯度轉(zhuǎn)化為灰度
b = 255 * (dx * uni_x + dy * uni_y + dz * uni_z)
return Image.fromarray(b.clip(0, 255).astype('uint8'))
調(diào)整一下深度和光源方向測(cè)試一下:
img2sketch('001.jpg', 5, azimuth=-5/4*np.pi)
圖像處理中的三個(gè)基本操作
反相:
對(duì)于三通道的RGB圖片:
t = [255, 255, 255] - data # 計(jì)算RGB三個(gè)通道的補(bǔ)值
Image.fromarray(t.astype('uint8'))
對(duì)于單通道的灰度圖片:
t = 255 - data
Image.fromarray(t.astype('uint8'))
區(qū)間變換(灰度圖像):
t = (100 / 255) * data + 150 # 區(qū)間變換
Image.fromarray(t.astype('uint8'))
像素取平方(灰度圖像):
t = 255 * (data / 255) ** 2 # 像素平方
Image.fromarray(e.astype('uint8'))