引言
熟悉PS的人也許知道,PS濾鏡中有一個Redfield Fractalius,可以讓圖片展現出魔幻的效果,比如一直可愛的喵星人,經過魔幻光影濾鏡處理之後就會展現出下面威武霸氣的樣子:
還有這樣
這樣
那麼我們能不能用OpenCV來實現這樣的效果呢?答案顯然是可以的。而且OpenCV還提供了相應的例子,這就大大簡化了我們學習的負擔。今天我們就來看看這個程序吧。
程序要點
這是OpenCV的自帶例程,代碼可以在如下路徑找到:\opencv\sources\samples\python\gabor_threads.py
。
所以代碼放在最後,我對代碼加了部分註釋。先說下代碼中比較重要的幾點。代碼裏需要注意的有三點,分別是定義的三個函數:
build_filters()
:代碼如下:# 創建濾波器(們) def build_filters(): filters = [] ksize = 31 # 此處創建16個濾波器,只有getGaborKernel的第三個參數theta不同。 for theta in np.arange(0, np.pi, np.pi / 16): kern = cv.getGaborKernel((ksize, ksize), 4.0, theta, 10.0, 0.5, 0, ktype=cv.CV_32F) kern /= 1.5*kern.sum() filters.append(kern) return filters
注意到
filters
後面加s
了,即濾波器不止一個。實際上,這個函數通過調用函數getGaborKernel()
一共創建了16個Gabor濾波器,不同之處只在於getGaborKernel()
函數的第三個參數theta
不同。process()
:單線程處理函數。這個函數也表明了魔幻濾鏡效果的處理過程:# 單線程處理 def process(img, filters): # zeros_like:返回和輸入大小相同,類型相同,用0填滿的數組 accum = np.zeros_like(img) for kern in filters: fimg = cv.filter2D(img, cv.CV_8UC3, kern) # maximum:逐位比較取其大 np.maximum(accum, fimg, accum) return accum
總結一下,魔幻濾鏡效果可以分爲以下幾步:
- 創建多個Gabor濾波器
- 創建一個與原圖大小相同的0值矩陣accum
- 分別用不同的濾波器對原圖進行濾波
- 濾波後的圖像與accum逐位比較取其大者,再複製給accum
- 重複3、4兩步。
process_threaded()
:多線程處理函數。可以通過這個函數大致學習下Python中多線程的用法:# 多線程處理,threadn = 8 def process_threaded(img, filters, threadn = 8): accum = np.zeros_like(img) def f(kern): return cv.filter2D(img, cv.CV_8UC3, kern) pool = ThreadPool(processes=threadn) for fimg in pool.imap_unordered(f, filters): np.maximum(accum, fimg, accum) return accum
其中
pool = ThreadPool(processes=threadn)
即建立一個八個線程的線程池。後面的for循環兩句:for fimg in pool.imap_unordered(f, filters): np.maximum(accum, fimg, accum)
pool.imap_unordered(f, filters)
即把filters作爲參數迭代地傳遞給函數f並執行。imap_unordered的意思是,不保證返回結果與迭代傳入的順序一致。不過對於這個魔幻濾鏡的處理過程來說,順序不重要。
效果圖
程序的效果顯然比不了PS各種調整之後的效果。我們調整合適的參數看看,雖然有所不及,但是也有了點魔幻的感覺。
處理之後
再來一張
處理之後
需要說明的是:
- 也許不是所有的圖都能做出魔幻的效果。
- 這個處理過程參數衆多,有時或者大部分時候需要同時調整幾個參數才能達到理想的
註釋過的代碼
完整的並且被注視過的代碼如下:
#!/usr/bin/env python
'''
gabor_threads.py
=========
Sample demonstrates:
- use of multiple Gabor filter convolutions to get Fractalius-like image effect (http://www.redfieldplugins.com/filterFractalius.htm)
- use of python threading to accelerate the computation
Usage
-----
gabor_threads.py [image filename]
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
from multiprocessing.pool import ThreadPool
# 創建濾波器(們)
def build_filters():
filters = []
ksize = 31
# 此處創建16個濾波器,只有getGaborKernel的第三個參數theta不同。
for theta in np.arange(0, np.pi, np.pi / 16):
kern = cv.getGaborKernel((ksize, ksize), 4.0, theta, 10.0, 0.5, 0, ktype=cv.CV_32F)
kern /= 1.5*kern.sum()
filters.append(kern)
return filters
# 單線程處理
def process(img, filters):
# zeros_like:返回和輸入大小相同,類型相同,用0填滿的數組
accum = np.zeros_like(img)
for kern in filters:
fimg = cv.filter2D(img, cv.CV_8UC3, kern)
# maximum:逐位比較取其大
np.maximum(accum, fimg, accum)
return accum
# 多線程處理,threadn = 8
def process_threaded(img, filters, threadn = 8):
accum = np.zeros_like(img)
def f(kern):
return cv.filter2D(img, cv.CV_8UC3, kern)
pool = ThreadPool(processes=threadn)
for fimg in pool.imap_unordered(f, filters):
np.maximum(accum, fimg, accum)
return accum
if __name__ == '__main__':
import sys
from common import Timer
# 輸出文件開頭由''' '''包含的註釋內容
print(__doc__)
try:
img_fn = sys.argv[1]
except:
img_fn = 'cat1.jpg'
img = cv.imread(img_fn)
# 判斷圖片是否讀取成功
if img is None:
print('Failed to load image file:', img_fn)
sys.exit(1)
filters = build_filters()
with Timer('running single-threaded'):
res1 = process(img, filters)
with Timer('running multi-threaded'):
res2 = process_threaded(img, filters)
print('res1 == res2: ', (res1 == res2).all())
cv.imshow('img', img)
cv.imshow('result', res2)
cv.waitKey()
cv.destroyAllWindows()
公衆號CVPy,分享有意思的,不僅限於OpenCV和Python的內容。