《实用OpenCV》<五> 图像滤波(1)

来源:互联网 发布:win10 数据丢失 编辑:程序博客网 时间:2024/05/16 07:50

图像滤波

        这一章我们将继续讨论图像的基本操作。将讨论一些滤波理论和一些从图像中提取特征或抑制图像噪声的方法。

        图形处理和计算机视觉之间有一条华丽丽的分割线。图形处理主要是通过不同的变换来呈现图形的不同表现。其通常呢,但不总是,是为了”显示”的目的,包括图像色彩空间的转换,锐化或模糊,改变对比度,仿射变换,裁剪,缩放等等。相比之下,计算机视觉所关心的是从图像中提取信息从而可以做出决策。通常呢,我们需要从噪声图里提取信息,所以就需要分析噪声并想方设法在不太影响图像重要信息内容的情况下抑制噪声。

        譬如说,你要做一个能单向移动,跟踪并截获一个红色球的简单轮式自动机器人。

        这里就包含两部分计算机视觉的问题:判断摄像机图像中是否有红球,如果有,则获得基于机器人朝向上的相对坐标。

滤波是从图像提取信息的重要的基础操作。

图像滤波器      

       滤波器不过是一个接受局部信号值的函数,以某种方式按信号中包含的信息来输出。为了更明白一些,看一下接下来的一维时间变化信号(可能是一个城市每天的温度)。
       我们想提取的信息是温度波动。明确的说,分析日常温度的变化。于是我们做一个得到昨天和今天温差绝对值的函数,表达式为 y[n] = |x[n] − x[n−1]|, y[n] 是滤波器在天数n的输出,x[n]是信号,也就是城市在天数n的温度。
       滤波器(长度为2)“划过”信号,然后输出就好像图5-1那样子。


表-5-1 一个简单的信号(上)和该信号差异过滤器的输出(下)
        正如你看到的,滤波器放大了信号的差异。简言之,如果今天的温度比昨天差很多,今天的滤波器输出就会高。如果今天温度几乎和昨天一样,那么输出差不多为0。希望这个简单的例子能让你了解滤波器设计其实是找到一个接受信号参数然后放大其中的选定信息内容的函数。
       继续讨论图像滤波器,会和刚刚讨论的一维滤波器有所不同,因为图像信号是二维的,因此滤波器也应该是二维的(如果我们要考虑像素四个周围方向的话)。一个检测图像垂直边缘的滤波器例子会帮助你更好地理解。第一步要确立一个滤波器矩阵,滤波器矩阵是滤波器函数的一个离散版本。使得可以在计算机上来应用滤波器。它们的长宽通常是奇数,为了可以明确中心元素。我们检测垂直边缘案例用到的矩阵非常简单:

0    0    0

-1   2   -1

0    0    0

或者要考虑两相邻像素的话:

0   0   0   0   0
0   0   0   0   0
-1 -2  6  -2  -1
0   0   0   0   0
0   0   0   0   0
现在,让我们将滤波器应用到图像看看是否可以。在这之前,我要详细说明一下什么叫“应用一个滤波器矩阵(或核矩阵)到图像上”。核矩阵是放置在图像上,通常从图像左上角开始。每次迭代将执行以下步骤:
      • 对核矩阵和核矩阵覆盖的图像像素做元素相乘
     •  对这些相乘的结果将使用一个函数计算得到一个单独的数值。这个函数可以是相加,求平均,求最大值、最小值,或更复杂的。计算的值称为图像该步迭代中对滤波器的“响应”。
     • 核矩阵中心元素下的像素取得该响应值。
     •将 核转向右边或者下边。
对只有水平和垂直边缘线的一张图采用该滤波矩阵(也称核矩阵)将得到图5-2中的滤波图像。


图 5-2 一张水平和垂直边缘线的简单图像(左)和通过核矩阵输出的图像(右)

   OpenCV有个叫filter2D()的函数可以高效用作基于核矩阵的滤波。见例5-1

例5-1 通过一个简单滤波矩阵应用到图像来检测水平边缘

// Program to apply a simple filter matrix to an image// Author: Samarth Manoj Brahmbhatt, University of Pennsylvania#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>using namespace std;using namespace cv;int main() {   Mat img = imread("image.jpg", CV_LOAD_IMAGE_GRAYSCALE), img_filtered;   // Filter kernel for detecting vertical edges   float vertical_fk[5][5] = {{0,0,0,0,0}, {0,0,0,0,0}, {-1,-2,6,-2,-1}, {0,0,0,0,0}, {0,0,0,0,0}};   // Filter kernel for detecting horizontal edges   float horizontal_fk[5][5] = {{0,0,-1,0,0}, {0,0,-2,0,0}, {0,0,6,0,0}, {0,0,-2,0,0}, {0,0,-1,0,0}};   Mat filter_kernel = Mat(5, 5, CV_32FC1, horizontal_fk);   // Apply filter   filter2D(img, img_filtered, -1, filter_kernel);   namedWindow("Image");   namedWindow("Filtered image");   imshow("Image", img);   imshow("Filtered image", img_filtered);   // imwrite("filtered_image.jpg", img_filtered);   while(char(waitKey(1)) != 'q') {}   return 0;}

正如你猜到的,检测垂直边缘的核矩阵是:

0   0   -1   0   0

0   0   -2   0   0

0   0    6   0   0

0   0   -2   0   0

0   0   -1   0   0

图5-3 显示了它很好地检测到了垂直边缘

图 5-3 检测图像的垂直边缘

      使用不同的检测矩阵并应用到不同图像是非常有趣的!

       如果你要用一个多通道的彩色图像当作filter2D()的输入的话,他会全部应用到所有通道。有时你可能想检测某一个特定通道的边缘,或者对不同通道使用不同的核矩阵然后选择其中最好的边缘的话。你应该使用split()方法来分隔图像并分别应用核矩阵。

      因为边缘检测在计算机视觉中是非常重要的,已经有很多设计方法的研究和可以检测任意方向的智能滤波器矩阵。OpenCV已经提供了很多相关的算法实现,这些将在之后提到。



0 1
原创粉丝点击