1. 程式人生 > >Python 資料分析與展示筆記2 -- 影象手繪效果

Python 資料分析與展示筆記2 -- 影象手繪效果

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!