數字影象處理筆記(二):使用OpenCV處理影象
阿新 • • 發佈:2018-12-15
1 - 傅立葉變換
傅立葉變換是影象處理的基礎,Joseph Fourier(約瑟夫 傅立葉)是一維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個檔案編寫好後,就可以實現我們的效果:描繪邊緣並模擬肖像膠捲色彩,並且可以通過修改程式碼更換所需要的濾波器來實現不同的效果
感覺把現實風格轉換成了美漫風格有木有。