OpenCV3 for python3 学习笔记3-----用OpenCV3处理图像一

  本文的内容都与图像处理有关,这时须要修改图像,好比要使用具备艺术性的滤镜、外插(extrapolate)某些部分、分割、粘贴或其余须要的操做。算法

  一、不一样色彩空间的的转换

    OpenCV有数百种关于在不一样色彩空间之间转换的方法。当前,计算机视觉中有三种经常使用的色彩空间:灰度、BGR以及HSV(Hue,Saturation,Value)。数组

    灰度色彩空间是经过去除彩色信息来将其转换成灰阶,灰度色彩空间对中间处理特别有效,好比人脸检测。app

    BGR,即蓝-绿-红色彩空间,每个像素点都由一个三元数组来表示,分别表明蓝、绿、红三种颜色。函数

    HSV,H(Hue)是色调、S(Saturation)是饱和度、V(Value)表示黑暗的程度(或光谱另外一端的明亮程度)。性能

    BGR的简短说明spa

    当第一次处理BGR色彩空间的时候,能够不要其中的一个色彩份量,好比像素值(0,255,255)(没有蓝色,绿色份量取最大值,红色份量取最大值)表示黄色,绿色和红色混合产生浑浊的褐色,code

  这是由于计算所使用的颜色模型具备可加性而且处理的是光照,而绘画不是这样(它听从建色模型(subtractive color model))。计算机使用显示器发光来作颜色的媒介,orm

  所以运行在计算机的软件所使用的色彩模型是加色模型。视频

  二、佛里叶变换

    在OpenCV中,对图像和视频的大多数处理都或多或少会涉及佛里叶变换的概念。Joseph Fourier(约瑟夫.佛里叶)是一位18实际的法国数学家,他发现并推广了不少数学概念,对象

  主要研究热学规律,在数学上,他认为一切均可以用波形来描述。具体而言,他观察到的全部波形均可以由一系列简单其频率不一样的正弦曲线叠加获得。也就是说,

  人们看到的波形都是由其它波形叠加获得的。这个概念对操做图像很是有帮助,由于这样咱们就能够区分图像里哪些区域的信号(好比图像像素)变化特别强,哪些区域的信号变化不那么强,

  从而能够任意地标记噪声区域、感兴趣区域、前景和背景等。原始图像有许多频率组成,人们可以分离这些频率来处理图像和提取感兴趣的数据。

    注意:在OpenCV环境中,有许多实现了算法让咱们可以处理图像,理解图像中所包含的含义。这些算法在 NumPy中也有实现,并且更容易使用。NumPy有快速佛里叶变换(FFT)的包,

  它包含了fft2()函数,此函数能够计算一副图像的离散佛里叶变换(DFT)。

    下面经过介绍佛里叶变换来介绍图像的幅度谱(magnitude spectrum)。图像的幅度谱是另外一种图像,幅度谱图像呈现了原始图像在变化方面的一种表示:把一副图像中最明亮的像素放到图中央,

  而后逐渐变暗,在边缘的像素最暗。这样能够发现图像中有多少亮的像素和暗的像素,以及他们分布的百分比。

    佛里叶变换的概念是许多常见的图像处理操做的基础,好比边缘检测或线段的和形状检测。

    下面先介绍两个概念:高通滤波器和低通滤波器,上面提到那些操做都是以这两个概念和佛里叶变换为基础。

  2.一、高通滤波器

    高通滤波器(High-pass filter:HPF)是监测图像的某个区域,而后根据像素与周围像素的亮度差值来提示(boost)该像素的亮度的滤波器。

    以以下的核(kernel)为例:

    注意:核是指一组权重的结合,它会应用在源图像的一个区域,并由此产生目标图像的一个像素。好比,大小为7的核意味着每49(7*7)个源图像的像素会产生目标图像的一个像素。

  能够把核看做一块覆盖在源图像上能够移动的毛玻璃片,玻璃片覆盖区域的光线会按某种方式进行扩散混合后投过去。

    在计算完中央像素和周围临近像素的亮度差值之和之后,若是亮度变化很大,中央像素的亮度会增长(反之则不会)。换句话说,若是一个像素比它周围的像素突出,就会提示它的高度。

  这在边缘检测上尤为有效,它会采用一种称为高频提示滤过器(high boost filger)的高通滤波器。

    高通和低通滤波器都有一个称为半径(radius)的属性,它决定了多大面积的临近像素参与滤波运算。

    下面是一个高通滤波器的例子:

import cv2
import numpy as np
import os
from scipy import ndimage

kernal_3x3 = np.array([[-1, -1, -1],
                       [-1, 8, -1],
                       [-1, -1, -1]])
kernal_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]])
# 注意这些滤波器里面的值加起来等于0,之后会解释这个缘由
# 使用函数cv2.imread() 读入图像。这幅图像应该在此程序的工做路径,或者给函数提供完整路径,第二个参数是要告诉函数应该如何读取这幅图片。

#    • cv2.IMREAD_COLOR:读入一副彩色图像。图像的透明度会被忽略,这是默认参数。
  #    • cv2.IMREAD_GRAYSCALE:以灰度模式读入图像

img = cv2.imread('flower.jpg',0)       # 注:此处后面要加上0,表示已灰度模式读入图像
k3 = ndimage.convolve(img, kernal_3x3)  # 注:使用ndimage.convolve()时,滤波核的维度应与原始图像的维度相同,故此采用灰度图
k5 = ndimage.convolve(img, kernal_5x5)

blurred = cv2.GaussianBlur(img, (11, 11), 0)
g_hpf = img - blurred
cv2.imshow("flower", img)
cv2.imshow("flower:3x3", k3)
cv2.imshow("flower:5x5", k5)
cv2.imshow("flower:g_hpf", g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()

 

  运行效果图以下:

  

    导入模块之后,咱们定义了一个3*3和一个5*5的核,而后将读入的图像转换为灰度格式。一般大多数的图像处理会用NumPy来完成,可是这里的状况比较特殊,

  由于须要一个给定的核与图像进行“卷积”(convolve),可是NumPy碰巧只接受一维数组。可是并非说不能用NumPy完成多维数组的卷积运算,只是有些复杂。而ndimage(它是SciPy的

  一部分)的convolve()函数能够解决这个问题,该函数支持经典的NumPy数组,cv2模块用这种数组来存储图像。

  上面的代码用了两个自定义的卷积核来实现两个高通滤波器。最后会用一周不一样的方法来实现一个高通滤波器:经过对象图像应用低通滤波器以后,与原始图像计算差值。第三种方法获得的效果最好。

 

  2.二、低通滤波器

     高通滤波器是根据像素与周围像素的亮度差值来提示该像素的亮度,低通滤波器(Low Pass Filter,LPF)则是在像素与周围像素的亮度差值小于一个特定值时,平滑该像素的亮度。

  它主要用于去噪和模糊化,好比数,高斯模糊是最经常使用的模糊滤波器(平滑滤波器)之一,它是一个削弱高频信号强度的低通滤波器。

  三、边缘检测

    边缘在人类视觉和计算机视觉中均起着重要的的做用。人类可以凭借一张背景剪影或一个草图就识别出物体的类型和姿态。事实上,艺术强调边缘和姿态,

  它们一般传达了原型(archetype)的思想,好比Rodin的《思考者》和Joe Shuster的《超人》。软件也同样,它能够推理出边缘、姿态以及原型。

    OpenCV提供了许多边缘检测滤波函数,包括Laplacian()、Sobel()以及Scharr()。这些滤波函数都会将边缘区域转为黑色,将边缘区域转为白色或其它饱和的颜色。可是,

  这些函数很容易将噪声错误地识别为边缘。缓解这个问题的办法是在找到边缘以前对图像进行模糊处理。OpenCV也提供了许多模糊滤波函数,包括blur()(简单是算术平均),medianBlur()以及

  GaussianBlur()。边缘检测函数和模糊滤波函数有不少参数,但总会有一个ksize参数,它是一个奇数,表示高滤波的宽和高(以像素为单位)。

    这里使用medianBlur()做为模糊函数,它对去除数字化的视频噪声很是有效,特别是去除彩色图像的噪声;使用Laplacian()做为边缘检测函数,它会产生明显的边缘线条,

  灰度图像更是如此。在使用medianBlur()函数以后,将要使用Laplacian()函数以前,须要将图像从BGR色彩空间灰度色彩空间。

    在获得Laplacian()函数的结果以后,须要将其转换成黑色边缘和黑色背景的图像。而后将其归一化(使它的像素值在0到1之间),并乘以源图像以便将边缘变黑。

    代码实现以下:

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)

 

    注意,核的大小可由strokeEdges()函数的参数来指定。 blurKsize参数会做为medianBlur()函数的ksize参数,edgeKsize参数会做为Laplacian()函数的ksize参数。这里将blurKsize值设为7,

  将edgeKsize值设为5会获得更好的效果,不幸的是,对于较大的ksize(好比7),使用medianBlur()的代价很高。

  提示,若是你在使用strokeEdges()时遇到性能问题,能够试着减小blurKsize的值,要关闭模糊效果,能够将blurKsize的值设为3如下。

  四、用定制内核作卷积

    OpenCV预约义的许多滤波器(滤波函数)都使用核。其实核是一组权重,它决定如何经过临近像素点来计算新的像素点。核也称为卷积矩阵,它对一个区域的像素作调和

  (mix up)或卷积运算,一般基于核的滤波器被称为卷积滤波器。

    OpenCV提供了一个很是通用的filter2D(),它运用由用户指定的任意核或卷积矩阵。为了理解这个函数的使用方法,首先来了解卷积矩阵的的格式。卷积矩阵是一个二维数组,

  有奇数行、奇数列,中心的元素对应于感兴趣的像素,其它的元素对应于这个像素周围的临近像素,每一个元素都有一个整数或浮点数的值,这些值就是应用在像素上的权重。

kernel = numpy.array([[-1, -1 , -1],
                      [-1,  9,  -1],
                      [-1, -1, -1]])

  好比:上面实例在感兴趣的像素权重是9,其临近像素权重为-1。对感兴趣的像素来讲,新像素值是用当前像素值乘以9,而后减去8个临近像素值。若是感兴趣的像素已经与临近

  像素有一点差异,那么这个差异会增长,这些会让图像锐化,由于该像素的值与临近像素的之间的差距拉大了。注意权重加起来为1,若是不想改变图像的亮度就应该这样。

  若是稍微修改一下锐化核使它的权重加起来为0,就会获得一个边缘检测核,把边缘转为白色,把非边缘区域转为黑色。

    

    在源图像和目标图像上分别使用卷积矩阵:cv2.filter2D(src, -1, kernel, dst). 第二个参数指定了目标图像每一个通道的位深度(好比,位深度cv2.CV_8U表示每一个通道为8位),

  若是为负值,则表示目标图像和源图像有一样的位深度。

 

    注:对彩色图像来讲,filter2D()会对每一个通道都用一样的核。若是要对每一个通道使用不一样的核,就必须用split()函数和merge()函数。

 

    对于模糊滤波器,为了达到模糊效果,一般权重和应该为1,并且邻近像素的权重全为正。

# VConvolutionFilter 表示通常的滤波器
class VConvolutionFilter(object):
    """A filter that applies a convolution to V(or all of BGR)."""

    def __init__(self, kernel):
        self._kernel = kernel

    def apply(self, src, dst):
        """Apply the filter with a BGR or gray source/destination."""
        cv2.filter2D(src, -1, self._kernel, dst)

# SharpenFilter 表示特定的锐化滤波器
class SharpenFilter(VConvolutionFilter):
    """A sharpen filter with a 1-pixel radius."""
    def __init__(self):
        kernel = numpy.array([[-1, -1, -1],
                              [-1, 9, -1],
                              [-1, -1, -1]])
        VConvolutionFilter.__init__(self, kernel)

# 边缘检测滤波器
class FindEdgesFilter(VConvolutionFilter):
    """A edge-finding filter with a 1-pixel radius."""
    def __init__(self):
        kernel = numpy.array([[-1, -1, -1],
                              [-1, 8, -1],
                              [-1, -1, -1]])
        VConvolutionFilter.__init__(self, kernel)

# 邻近平均滤波器
class BlurFilter(VConvolutionFilter):
    """A edge-finding filter with a 1-pixel radius."""
    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):
    """A edge-finding filter with a 1-pixel radius."""

    def __init__(self):
        kernel = numpy.array([[-2, -1, 0],
                              [-1, 1, 1],
                              [0, 1, 2]])
        VConvolutionFilter.__init__(self, kernel)
相关文章
相关标签/搜索