當前位置:
首頁 > 知識 > 故宮下雪了!我花了45秒,用Python給它畫了一組手繪圖

故宮下雪了!我花了45秒,用Python給它畫了一組手繪圖

(給

Python開發者

加星標,提升Python技能


作者:戀習Python / 丁彥軍 (本文來自作者投稿)

這幾天,許多城市,迎來了2019年的第一場雪


13日早晨,當北京市民拉開窗帘時發現,窗外雪花紛紛揚揚在空中飄落


而且越下越大,樹上、草地、屋頂、道路上...都落滿雪花


京城銀裝素裹,這是今冬以來北京迎來的第二場降雪



一下雪,北京就變成了北平,故宮就變成了紫禁城

八萬張門票在雪花飄下來之前,便早已預訂一空




看著朋友圈、微博好友都在紛紛晒圖,小編只能羨慕不已。

不過,戀習Python突然想到,可以通過Python將故宮的建築物圖片,轉化為手繪圖(素描效果)。效果圖如下:


一、概念與原理

我們都知道手繪圖效果的特徵主要有:




  • 黑白灰色;邊界線條較重;相同或相近色彩趨於白色;略有光源效果

核心原理:

利用像素之間的

梯度值

和虛擬

深度值

對圖像進行重構,根據

灰度變化

來模擬人類視覺的模擬程度

把圖像看成二維離散函數,灰度梯度其實就是這個二維離散函數的求導,用差分代替微分,求取圖像的灰度梯度。常用的一些灰度梯度模板有:Roberts 梯度、Sobel 梯度、Prewitt 梯度、Laplacian 梯度。

以Sobel 梯度計算來解釋:

首先計算出 

,然後計算梯度角 


梯度方向及圖像灰度增大的方向,其中梯度方向的梯度夾角大於平坦區域的梯度夾角。如下圖所示,灰度值增加的方向梯度夾角大,此時梯度夾角大的方向為梯度方向。對應在圖像中尋找某一點的梯度方向即通過計算該點與其8鄰域點的梯度角,梯度角最大即為梯度方向。


二、圖像的數組形式與變換

其中,需要用到的方法:




  • Image.open( ): 打開圖片



  • np.array( ) : 將圖像轉化為數組



  • convert("L"): 將圖片轉換成二維灰度圖片



  • Image.fromarray( ): 將數組還原成圖像uint8格式

代碼如下

from

 PIL 

import

 Image

import

 numpy 

as

 np

im = Image.open(

r"C:UsersAdministratorDesktopgugong微信圖片_20190216152248.jpg"

).convert(

"L"

)
a=np.asarray(im).astype(

"float"

)
print(a.shape,a.dtype)
(

1080

608

) float64

#(1080, 608)分別表示高度,寬度


三、圖像的手繪效果處理

實現思路步驟:

1、梯度的重構


numpy的梯度函數的介紹

np.gradient(a) : 計算數組a中元素的梯度,f為多維時,返回每個維度的梯度 


離散梯度: xy坐標軸連續三個x軸坐標對應的y軸值:a, b, c 其中b的梯度是(c-a)/2 


而c的梯度是: (c-b)/1

當為二維數組時,np.gradient(a) 得出兩個數組,第一個數組對應最外層維度的梯度,第二個數組對應第二層維度的梯度。 

代碼如下:

grad=np.gradient(a)
grad_x,grad_y=grad
grad_x = grad_x * depth / 100.

#對grad_x值進行歸一化


grad_y = grad_y * depth / 100.

#對grad_y值進行歸一化


2、構造guan光源效果


設計一個位於圖像斜上方的虛擬光源
光源相對於圖像的視角為Elevation,方位角為Azimuth
建立光源對各點梯度值的影響函數
運算出各點的新像素值



其中:


np.cos(evc.el) : 單位光線在地平面上的投射長度


dx,dy,dz :光源對x,y,z三方向的影響程度

3、梯度歸一化




  • 構造x和y軸梯度的三維歸一化單位坐標系;



  • 梯度與光源相互作用,將梯度轉化為灰度。

4、圖像生成

具體詳情代碼如下:

from

 PIL 

import

 Image

import

 numpy 

as

 np

import

 os

import

 join

import

 time

def

 

image

(sta,end,depths=

10

)

:


    a = np.asarray(Image.open(sta).convert(

"L"

)).astype(

"float"

)
    depth = depths  

# 深度的取值範圍(0-100),標準取10


    grad = np.gradient(a)  

# 取圖像灰度的梯度值


    grad_x, grad_y = grad  

# 分別取橫縱圖像梯度值


    grad_x = grad_x * depth / 

100.

#對grad_x值進行歸一化


    grad_y = grad_y * depth / 

100.

#對grad_y值進行歸一化


    A = np.sqrt(grad_x ** 

2

 + grad_y ** 

2

 + 

1.

)
    uni_x = grad_x / A
    uni_y = grad_y / A
    uni_z = 

1.

 / A
    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 軸的影響


    b = 

255

 * (dx * uni_x + dy * uni_y + dz * uni_z)  

# 光源歸一化


    b = b.clip(

0

255

)
    im = Image.fromarray(b.astype(

"uint8"

))  

# 重構圖像


    im.save(end)

def

 

main

()

:


    xs=

10


    start_time = time.clock()
    startss = os.listdir(

r"C:UsersAdministratorDesktopgugong"

)
    time.sleep(

2

)
    

for

 starts 

in

 startss:
        start = 

""

.join(starts)
        sta = 

"C:/Users/Administrator/Desktop/gugong/"

 + start
        end = 

"C:/Users/Administrator/Desktop/gugong/"

 + 

"HD_"

 + start
        image(sta=sta,end=end,depths=xs)

    end_time = time.clock()
    print(

"程序運行了  ----"

 + str(end_time - start_time) + 

"   秒"

)
    time.sleep(

3

)

main()
程序運行了  ---

-43.01828205879955

   秒  

#一共35張圖片


最終效果圖對比:



其他圖片就不一一列舉;你也可以通過此代碼為自己畫一張手繪圖;也可以為自己的家鄉或母校畫。 

參考資料:北京理工大學的嵩天老師的網路課程


http://www.icourse163.org/learn/BIT-1001870002?tid=1001963001#/learn/announce


【本文作者】

丁彥軍

 一名痴戀於 Python 的碼農,個人公號:「戀習Python」,在這裡我們一起用Python 做些有意義的事。




推薦閱讀


(點擊標題可跳轉閱讀)


啥是佩奇?用 Python 畫給你看


用 Python 全自動下載抖音小姐姐視頻

覺得本文對你有幫助?請分享給更多人


關注「Python開發者」加星標,提升Python技能



喜歡就點一下「好看」唄~

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發者 的精彩文章:

TAG:Python開發者 |