傅里叶变换是图像处理的基础,Joseph Fourier(约瑟夫 傅里叶)是一维18世纪的法国数学家,他发现并推广了不少数学概念,在数学上,他认为一切均可以用波形来描述。具体而言,他观察到全部的波形均可以由一系列简单且频率不一样的正弦曲线叠加获得html
也就是说,原始图像由许多频率组成,咱们能够分离这些频率来理解图像和提取感兴趣的数据,傅里叶变换的概念是许多常见的图像处理操做的基础,好比边缘检测或线段和形状检测web
下面咱们要先介绍两个概念:高通滤波器和低通滤波器,上面提到的那些操做都是以这两个概念和傅里叶变换为基础。网络
高通滤波器(HPF)是检测图像的某个区域,而后根据像素与周围像素亮度差值来提高(Boost)该像素的亮度的滤波器app
对滤波操做,咱们首先要知道核(kernel)的概念框架
核是指一组权重的集合,它会应用在源图像的一个区域,并由今生成目标图像的一个像素。与卷积神经网络中的卷积核同样,经过选定核的权重与图像进行卷积达到对图像特征的提取
ide
下面咱们就经过一段程序来实验一些高通滤波器的做用svg
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)
原始图像:
通过高通滤波器以后的图像:
能够看到咱们把图像的边缘给提取出来了,仍是能依稀的看到猫的轮廓工具
高通滤波器是根据像素与邻近像素的亮度差值来提高该像素的亮度。低通滤波器(Low Pass Filter,LPF)则是在像素与周围像素的亮度差值小于一个特定值时,平滑该像素的亮度。它主要用于去噪和模糊化。
下面,咱们在以前Cameo框架中增长咱们的模块来增长一个低通滤波的功能ui
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)
定义完滤波器后编写一些框架所需的工具类spa
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
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()
上面3个文件编写好后,就能够实现咱们的效果:描绘边缘并模拟肖像胶卷色彩,而且能够经过修改代码更换所须要的滤波器来实现不一样的效果
感受把现实风格转换成了美漫风格有木有。