Python 資料分析與展示筆記2 -- 影象手繪效果
阿新 • • 發佈:2018-11-30
Python 資料分析與展示筆記2 – 影象手繪效果
Python 資料分析與展示系列筆記是筆者學習、實踐Python 資料分析與展示的相關筆記
課程連結: Python 資料分析與展示
參考文件:
Numpy 官方文件(英文)
Numpy 官方文件(中文)
PIL 官方文件
一、PIL 庫
1、安裝與匯入
# 安裝 PIL 庫
pip install pillow
# 匯入 PIL 庫
import PIL
# 匯入 PIL 庫的 Image模組/類(用讀取顯示影象)
from PIL import Image
2、簡單使用
# 開啟影象 Image.open('圖片的路徑') # 轉為灰度影象 .convert('L') # 儲存影象 .save('儲存路徑')
二、影象手繪效果
1、總體思路
- 影象中物體的邊緣用黑色畫出,而大片相同的區域用白色畫出
- 影象梯度可以求出物體的邊緣,邊緣越明顯,梯度越大
- 為了用黑色表色邊緣,圖片的畫素值應該與梯度值成反比關係,而程式碼中的核心思想也是為了求得這個關係
2、幾點理解
- depth引數:設定 x,y 梯度佔總的梯度的比例,使梯度值縮小,因為我們把z向的梯度設成了1,如果不壓縮x,y向梯度那麼這個1就起不到什麼作用
- z 方向的梯度(即梯度歸一化中的1):為了表示畫素值與梯度的反比關係,當畫素的 x,y 向梯度值都很小時,在歸一化時uni_z值就會區於1從而得到趨於255的畫素值
- 影象梯度歸一化:主要為了得到 uni_z 的值
- 光源設定:設定光源的角度只是為了得到 dx,dy,dz 三個可調係數而已,個人認為跟光源一點關係都沒有,反而不好理解
- dx,dy,dz:uni_x,uni_y,uni_z的係數,更深入的說其實目的是把 dz 設的很大,dx,dy很小,完全可以不要dx,dy 把dz設為1,效果幾乎一樣
3、程式碼
from PIL import Image import numpy as np def hand_draw_image(img_path, show=True, save_path=None): """ 把彩色圖片變為手繪風格的灰度圖片 :param img_path: 原圖片的路徑 :param show: 是否要顯示手繪圖片 :param save_path: 是否要儲存手繪圖片 :return: """ # 開啟圖片轉為灰度圖,並轉換為 float 型別的 ndarray img = np.asarray(Image.open(img_path).convert('L')).astype('float') # 求影象的梯度 depth = 10 grad_x, grad_y = np.gradient(img) grad_x = grad_x * depth / 100. grad_y = grad_y * depth / 100. # 歸一化影象的梯度 img_grad = np.sqrt(grad_x ** 2 + grad_y ** 2 + 1) uni_x = grad_x / img_grad uni_y = grad_y / img_grad uni_z = 1 / img_grad # 光源對影象明暗的影響 vec_el = np.pi / 2.2 # 光源的俯視角度,弧度值 vec_az = np.pi / 4. # 光源的方位角度,弧度值 dx = np.cos(vec_el) * np.cos(vec_az) # 光源對 x 軸的影響(投影) dy = np.cos(vec_el) * np.sin(vec_az) # 光源對 y 軸的影響(投影) dz = np.sin(vec_el) # 光源對 z 軸的影響(投影) img_res = 255 * (dx * uni_x + dy * uni_y + dz * uni_z) # 光源歸一化 img_res = img_res.clip(0, 255) # 裁剪0-255外的畫素值 im = Image.fromarray(img_res.astype(np.uint8)) # 重構影象 if show: im.show() if save_path: im.save(save_path) if __name__ == '__main__': print('running hand_painted:') hand_draw_image('圖片路徑')
原圖:
效果:
GOOD LUCK!