1. 程式人生 > >數字影象處理筆記(二):使用OpenCV處理影象

數字影象處理筆記(二):使用OpenCV處理影象

1 - 傅立葉變換

傅立葉變換是影象處理的基礎,Joseph Fourier(約瑟夫 \cdot 傅立葉)是一維18世紀的法國數學家,他發現並推廣了很多數學概念,在數學上,他認為一切都可以用波形來描述。具體而言,他觀察到所有的波形都可以由一系列簡單且頻率不同的正弦曲線疊加得到

也就是說,原始影象由許多頻率組成,我們可以分離這些頻率來理解影象和提取感興趣的資料,傅立葉變換的概念是許多常見的影象處理操作的基礎,比如邊緣檢測或線段和形狀檢測

下面我們要先介紹兩個概念:高通濾波器和低通濾波器,上面提到的那些操作都是以這兩個概念和傅立葉變換為基礎。

2 - 高通濾波器

高通濾波器(HPF)是檢測影象的某個區域,然後根據畫素與周圍畫素亮度差值來提升(Boost)該畫素的亮度的濾波器

對濾波操作,我們首先要知道核(kernel)的概念

核是指一組權重的集合,它會應用在源影象的一個區域,並由此生成目標影象的一個畫素。與卷積神經網路中的卷積核一樣,通過選定核的權重與影象進行卷積達到對影象特徵的提取
在這裡插入圖片描述

下面我們就通過一段程式來實驗一些高通濾波器的作用

import cv2
import numpy as np
from scipy import ndimage

kernel_3x3 = np.array([[-1,-1,-1],
                      [-1,8,-1],
                      [-1,-1,-1]])
kernel_5x5 = np.array([[-1,-1,-1,-1,-1],
                       [-1,1,2,1,-1],
                       [-1,2,4,2,-1],
                       [-1,1,2,1,-1],
                       [-1,-1,-1,-1,-1]])
img = cv2.imread('images/cat.jpg',0)

k3 = ndimage.convolve(img, kernel_3x3)
k5 = ndimage.convolve(img, kernel_5x5)
blurred = cv2.GaussianBlur(img, (11,11), 0)
g_hpf = img - blurred
cv2.imshow('3x3',k3)
cv2.imshow('5x5',k5)
cv2.imshow('g_hpf',g_hpf)
cv2.waitKey(0)

原始影象:
在這裡插入圖片描述
經過高通濾波器之後的影象:
在這裡插入圖片描述
可以看到我們把影象的邊緣給提取出來了,還是能依稀的看到貓的輪廓

3 - 低通濾波器

高通濾波器是根據畫素與鄰近畫素的亮度差值來提升該畫素的亮度。低通濾波器(Low Pass Filter,LPF)則是在畫素與周圍畫素的亮度差值小於一個特定值時,平滑該畫素的亮度。它主要用於去噪和模糊化。
下面,我們在之前Cameo框架中增加我們的模組來增加一個低通濾波的功能

1 - 在filters.py檔案中編寫濾波器類

import cv2
import numpy
import utils

def strokeEdges(src, dst, blurKsize = 7, edgeKsize = 5):
    if blurKsize >= 3:
        blurredSrc = cv2.medianBlur(src, blurKsize)
        graySrc = cv2.cvtColor(blurredSrc,cv2.COLOR_BGR2GRAY)
    else:
        graySrc = cv2.cvtColor(src,cv2.COLOR_BGR2GRAY)
    cv2.Laplacian(graySrc,cv2.CV_8U,graySrc,ksize=edgeKsize)
    normalizedInverseAlpha = (1.0 / 255) * (255 - graySrc)
    channels = cv2.split(src)
    for channel in channels:
        channel[:] = channel * normalizedInverseAlpha
    cv2.merge(channels, dst)







class VConvolutionFilter(object):
    """
    對V進行卷積的濾波器
    """
    def __init__(self,kernel):
        self._kernel = kernel
    def apply(self,src,dst):
        """
        使用BGR或灰色源影象應用過濾器
        """
        cv2.filter2D(src,-1,self._kernel,dst)

class SharpenFilter(VConvolutionFilter):
    """
    具有1畫素半徑的銳化濾波器
    """
    def __init__(self):
        kernel = numpy.array([[-1,-1,-1],
                              [-1,9,-1],
                              [-1,-1,-1]])
        VConvolutionFilter.__init__(self,kernel)

class FindEdgesFilter(VConvolutionFilter):
    """
    一個半徑為1畫素的邊緣檢驗濾波器
    """
    def __init__(self):
        kernel = numpy.array([[-1,-1,-1],
                              [-1,8,-1],
                              [-1,-1,-1]])
        VConvolutionFilter.__init__(self,kernel)

class BlurFilter(VConvolutionFilter):
    """
    一個2畫素半徑的模糊濾波器
    """
    def __init__(self):
        kernel = numpy.array([[0.04,0.04,0.04,0.04,0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04],
                              [0.04, 0.04, 0.04, 0.04, 0.04]])
        VConvolutionFilter.__init__(self,kernel)

class EmbossFilter(VConvolutionFilter):
    """
    具有1畫素半徑的浮雕過濾器(銳化、邊緣檢測、模糊等濾波器都使用了高度對稱的核,而不對稱的核會得到一些有趣的效果,
    它同時具有模糊和銳化的作用,這會產生一種脊狀或者浮雕的效果)
    """
    def __init__(self):
        kernel = numpy.array([[-2,-1,0],
                              [-1,-1,-1],
                              [0,1,2]])
        VConvolutionFilter.__init__(self,kernel)

class BGRFuncFilter(object):

    def __init__(self, vFunc=None, bFunc=None, gFunc=None, rFunc=None, dtype=numpy.uint8) :

        length = numpy.iinfo(dtype).max + 1
        self._bLookupArray = utils.createLookupArray(utils.createCompositeFunc(bFunc, vFunc), length)
        self._gLookupArray = utils.createLookupArray(utils.createCompositeFunc(gFunc, vFunc), length)
        self._rLookupArray = utils.createLookupArray(utils.createCompositeFunc(rFunc, vFunc), length)

    def apply(self, src, dst) :

        """應用濾波器在RGB影象上"""
        b, g, r = cv2.split(src)
        utils.applyLookupArray(self._bLookupArray, b, b)
        utils.applyLookupArray(self._gLookupArray, g, g)
        utils.applyLookupArray(self._rLookupArray, r, r)
        cv2.merge([ b, g, r ], dst)



class BGRCurveFilter(BGRFuncFilter):

    def __init__(self, vPoints=None, bPoints=None, gPoints=None, rPoints=None, dtype=numpy.uint8):
        BGRFuncFilter.__init__(self, utils.createCurveFunc(vPoints), utils.createCurveFunc(bPoints),
                               utils.createCurveFunc(gPoints), utils.createCurveFunc(rPoints), dtype)


class BGRPortraCurveFilter(BGRCurveFilter):
    def __init__(self, dtype = numpy.uint8):
        BGRCurveFilter.__init__(
            self,
            vPoints = [ (0, 0), (23, 20), (157, 173), (255, 255) ],
            bPoints = [ (0, 0), (41, 46), (231, 228), (255, 255) ],
            gPoints = [ (0, 0), (52, 47), (189, 196), (255, 255) ],
            rPoints = [ (0, 0), (69, 69), (213, 218), (255, 255) ],
            dtype = dtype)

定義完濾波器後編寫一些框架所需的工具類

2 - 在utils.py編寫常用的工具類

import cv2, numpy, scipy.interpolate


def createCurveFunc(points):
    """Return a function derived from control points."""
    if points is None:
        return None
    num_points = len(points)
    if num_points < 2:
        return None
    xs, ys = zip(*points)
    if num_points < 4:
        kind = 'linear'
        # 'quadratic' is not implemented.
    else:
        kind = 'cubic'
    return scipy.interpolate.interp1d(xs, ys, kind, bounds_error=False)


def createLookupArray(func, length = 256):
    """Return a lookup for whole-number inputs to a function. The lookup values are clamped to [0, length - 1]."""
    if func is None:
        return None
    lookup_array = numpy.empty(length)
    i = 0
    while i < length:
        func_i = func(i)
        lookup_array[i] = min(max(0, func_i), length - 1)
        i += 1
    return lookup_array


def applyLookupArray(lookup_array, src, dst):
    """Map a source to a destination using a lookup."""
    if lookup_array is None:
        return
    dst[:] = lookup_array[src]


def createCompositeFunc(func0, func1):
    """Return a composite of two functions."""
    if func0 is None:
        return func1
    if func1 is None:
        return func0
    return lambda x: func0(func1(x))


def createFlatView(array):
    """Return a 1D view of an array of any dimensionality."""
    flat_view = array.view()
    flat_view.shape = array.size
    return flat_view

3 - 相應的修改Cameo框架,增加濾波器顯示

import cv2
import filters
from managers import WindowManager, CaptureManager
class Cameo(object):
    def __init__(self):
        self._windowManager = WindowManager('Cameo', self.onKeypress)
        self._captureManager = CaptureManager(cv2.VideoCapture(0), self._windowManager, True)
        self._curveFilter = filters.BGRPortraCurveFilter()

    def run(self):
        self._windowManager.createWindow()
        while self._windowManager.isWindowCreated:
            self._captureManager.enterFrame()
            frame = self._captureManager.frame
            filters.strokeEdges(frame,frame)
            self._curveFilter.apply(frame, frame)
            self._captureManager.exitFrame()
            self._windowManager.processEvents()
    def onKeypress(self,keycode):
        if keycode == 32:  # space
            self._captureManager.writeImage('cameo/screenshot.png')
        elif keycode == 9:  # tab
            if not self._captureManager.isWritingVideo:
                self._captureManager.startWritingVideo('cameo/screenshot.avi')
            else:
                self._captureManager.stopWritingVideo()
        elif keycode == 27:    # escape
            self._windowManager.destoryWindow()
        # 讀幀

if __name__ == "__main__":
    Cameo().run()

4 - 實驗效果

上面3個檔案編寫好後,就可以實現我們的效果:描繪邊緣並模擬肖像膠捲色彩,並且可以通過修改程式碼更換所需要的濾波器來實現不同的效果
在這裡插入圖片描述
感覺把現實風格轉換成了美漫風格有木有。