魔幻光影濾鏡(2):仿Redfield Fractalius濾鏡

引言

熟悉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

    總結一下,魔幻濾鏡效果可以分爲以下幾步:

    1. 創建多個Gabor濾波器
    2. 創建一個與原圖大小相同的0值矩陣accum
    3. 分別用不同的濾波器對原圖進行濾波
    4. 濾波後的圖像與accum逐位比較取其大者,再複製給accum
    5. 重複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的內容。

這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章