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

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

  1、不同色彩空间的的转换

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

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

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

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

    BGR的简短说明

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

  这是因为计算所使用的颜色模型具有可加性并且处理的是光照,而绘画不是这样(它遵从建色模型(subtractive color model))。计算机使用显示器发光来做颜色的媒介,

  因此运行在计算机的软件所使用的色彩模型是加色模型。

  2、佛里叶变换

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

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

  人们看到的波形都是由其它波形叠加得到的。这个概念对操作图像非常有帮助,因为这样我们就可以区分图像里哪些区域的信号(比如图像像素)变化特别强,哪些区域的信号变化不那么强,

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

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

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

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

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

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

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

  2.1、高通滤波器

    高通滤波器(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.2、低通滤波器

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

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

  3、边缘检测

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

  它们通常传达了原型(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以下。

  4、用定制内核做卷积

    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)

 

posted on 2019-02-19 15:15  shaomine  阅读(986)  评论(0编辑  收藏  举报