opencv学习笔记(二)

opncv 读书笔记(2)

  1. (接上)数学形态学用于分析和处理离散图像。它定义了一系列运算,用预先定义的形状元素探测图像,从而实现图像的转换。

  2. 形态学滤波器腐蚀和膨胀图像 腐蚀和膨胀是最基本的形态学运算。

  3. 数学形态学中最基本的概念是结构元素(卷积?)形态学滤波器的应用过程就包含了用这个结构元素探测图像中每个像素的操作过程。结构元素原则上可以是任何形状,但通常是一个简单形状,并且把中心点作为远点。自定义结构元素可用于强化或消除特殊形状。

  4. 形态学滤波器通常作用于二值图像。习惯用高像素值(白色)表示前景物体,用低像素值(黑色)表示背景物体,因此通过阈值化创建的二值图像后,要进行依次反向处理(原图像的补码)。

  5. 腐蚀就是把当前像素替换成所定义像素集合中的最小像素值;膨胀是腐蚀的反运算,它把当前像素替换成所定义像素集中的最大像素值。腐蚀时,如果结构元素放到某个像素位置时碰到了背景(即交集中有一个像素是黑色的),那么这个像素就变为背景;膨胀时,如果结构元素放到某个背景像素位置时,碰到了前景物体,那么这个像素就被标为白色。因此,图像腐蚀后物体尺寸会缩小(形状被腐蚀),而图像膨胀后物体会扩大。在腐蚀图像中,有些面积较小的物体(可看作背景中的”噪声“像素)会彻底消失。与之类似,膨胀后的物体会变大,而物体中一些”空隙“会被填满。

  6. 使用自定义的结构元素腐蚀图像:

    // 创建7x7的mat变量,其中全部元素都为1
    cv::Mat element(7,7,CV_8U,cv::Scalar(1));
    // 用这个结构元素腐蚀图像
    cv::erode(image, eroeded, element);
    与一下操作结果一致:即在图像上反复应用同一个结构元素
    cv::erode(image, eroded, cv::Mat(), cv::Point(-1,-1), 3); // 没有cv::Mat()后的两个参数时,使用的是默认的结构元素。
    

    参数cv::Point(-1,-1)表示原点是矩阵的中心点(默认值),也可以定义在结构元素上的其他位置。由此得到的图像与使用7x7结构元素得到的图像是一样的。实际上,对图像腐蚀两次相当于对结构元素自身膨胀后的图像进行腐蚀。这个规则也适用于膨胀。

  7. 用结构元素腐蚀前景物体可看作对图像背景部分的膨胀,也就是说:
    • 腐蚀图像相当于对其反色图像膨胀后再取反色;
    • 膨胀图像相当于对其反色图像腐蚀后再取反色。
  8. 形态学滤波器应用在了二值图像上,但这些滤波器也能应用再灰度图像,甚至彩色图像上,并且方法的定义是相同的。另外,OpenCV的形态学函数支持就地处理。这意味着输入图像和输出图像可以采用同一个变量,

    cv::erode(image, image, cv::Mat());
    

    OpenCV会创建必须的临时图像,从而保证这种方法能正常运行。

  9. 闭合的定义是对图像先膨胀后腐蚀,白色前景物体中的小空隙已经被填满。闭合滤波器也会把邻近的物体连接起来。基本上,所有小到不能容纳完整结构元素的空隙或间隙都会被闭合滤波器消除。

  10. 开启的定义是对图像先腐蚀后膨胀。开启滤波器消除了背景中的几个小物体。所有小到不能容纳完整结构元素的物体都会被移除。

  11. 这些滤波器常用于目标检测。闭合滤波器可把错误分裂成小碎片的物体连接起来,而开启滤波器可以移除因图像噪声产生的斑点。

  12. 在提取图像中的连通组件前,通常要开启和闭合运算来清理图像。

  13. 形态学梯度运算可以提取出图像的边缘。

    // 用3x3结构元素得到梯度图像
    cv::Mat result;
    cv::morphologyEx(image, result, cv::MORPH_GRADIENT, cv::Mat());
    

    得到图像中物体的轮廓(为方便观察,对图像做了反色处理)。

  14. 顶帽(hat-top)变换,它可以从图像中提取出局部的小型前景物体。可以调用黑帽变换提取出页面上的文字(作为前景物体):

    // 使用7x7结构元素做黑帽变换
    cv::Mat element7(7,7, CV_8U, cv::Scalar(1));
    cv::morphologyEx(image, result, cv::MORPH_BLACKHAT, element7);
    
  15. 通过计算膨胀后的图像与腐蚀后的图像之间的差距得到边缘。因为这两种转换后图像的差别主要在边缘地带,所以相减后突出边缘。结构元素越大,检测到的边缘就越宽。这种边缘检测运算称为Beucher梯度。以下两种方法也能得到类似的结果:用膨胀后的图像减去原始图像,或者用原始图像减去腐蚀后的图像,那样得到的边缘会更窄。

  16. 分水岭变换是一种流行的图像处理算法,用于快速将图像分割册灰姑娘多个同质区域。基于这样的思想:如果把图像看作一个拓扑地貌,那么同类区域就相当于陡峭边缘内相对平坦的盆地。分水岭算法通过逐步增高水位,把地貌分割成多个部分。cv::watershed函数。

  17. 用MSER算法提取特征区域(最大稳定外部区域算法),从图像中提取有意义的区域。创建这些区域时也使用逐步提高水位的方法,但是这次关注的是在水淹过程中的某段时间内,保持相对稳定的盆地,可以发现,这些区域对应着图像中某些物体的特殊部分。cv::MSER 继承自cv::Feature2D类。

    //基本的MSER检测器
    cv::Ptr<cv::MSER> ptrMSER = cv::MSER::create(
    											5,	// 局部检测时使用的增量值
    											200,	// 允许的最小面积
    										2000); 	// 允许的最大面积
    // 点集的容器
    std::vector<std::vector<cv::Point>> points;
    // 矩形的容器
    std::vector<cv:Rect> rects;
    // 检测MSER特征
    ptrMSER->detectRegions(image, points, rects);
    

    检测结果放在两个容器中,第一个区域的容器,每个区域用组成它的像素点表示;第二个是矩形的容器,每个矩形包围一个区域。

  18. MSER 的原理与分水岭算法相同,即高度为 0~255,逐渐淹没图像。在图像处理技术中,通常把高于某个阈值的像素集合称为高度集。随着水位的升高,颜色较黑并且边界陡峭的区域会形成盆地 ,并且在一段时间内有相对稳定的形状(用水位表示颜色,水位高低代表了像素值的强度)。这些稳定的盆地就是 MSER。检测它们的方法是,观察每个水位连通的区域(即盆地)并测量它们的稳定性。测量稳定性的方法是:计算区域的当前面积以及该区域原先的面积(比当前水位低一个特定值的时候),并比较这两个面积。如果相对变化达到局部最小值,就认为这个区域是 MSER。增量值将作为 cv::MSER 类构造函数的第一个参数,用以测量相对稳定性,默认值为 5。另外要注意,区域面积必须在预定义的范围内。构造函数中后面两个参数就是允许的最小和最大区域尺寸。另外必须确保 MSER 是稳定的(第四个参数),即形状的相对变化必须足够小。一个稳定区域可以属于另一个更大的区域(称为父区域)。

  19. 为了确保有效性,一个父MSER和它的子区域必须有足够大的差别,即差异限度,由cv::MSER类构造函数的第五个参数指定。最后两个参数都使用了默认值。(MSER允许的最大相对变化的默认值为0.25,父MSER与子区域的最小差别的默认值为0.2。)

  20. 对于有父子关系的MSER,表示他们的椭圆通常比较类似。在某些情况下,可以施加一个约束条件,要求椭圆之间的差距不低于某个特定值,以免显示重复的椭圆。

图像滤波

  1. 滤波是信号和图像处理中的一种基本操作。它的目的是选择性地提取图像中某些方面地内容,这些内容在特定应用环境下传达了重要信息。滤波可去除图像中的噪声,提取有用的视觉特征,对图像重采样。

  2. 通过观察灰度分布来描述图像特征,称为空域。通过观察图像中灰度级的变化的频域,这种特征称为频域

  3. 频域分析把图像分解成从低频到高频的频率成分。图像强度值变化慢的区域只包含低频率,而强度值变化快的区域产生高频率傅里叶变换余弦变换可以清楚地显示图像地频率成分。频率分垂直频率(垂直方向的变化)和水平频率(水平方向的变化)。

  4. 在频域分析框架下,滤波器是一种放大(也可以不改变)图像中某些频段,同时滤掉(或减弱)其他频段的算子。如,低通滤波器的作用是消除图像中的高频部分;高通滤波器刚好相反,用来i消除图像中的低频部分。高频成分正好对应了物体边缘处的快速视觉变化。

  5. 应用一个线性滤波器相当于将内核移动到图像的每个像素上,并将每个对应像素乘以它的权重。这个运算在数学上称为卷积。 $I_{out}(x,y)=\sum{i}{\sum{j}{I_{im}(x-i)(y-j)K(i,j)}}$

  6. 高斯滤波器,像素对应的权重与它到中心像素之间的距离成正比。 $G(x)=Ae^{\frac{-x^2}{\sigma^2}}$

    使用归一化系数A是为了确保高斯曲线下方的面积等于1。符号\sigma的值决定了高斯函数曲线的宽度。这个值越大,函数曲线就越扁平。

    不同\sigma

  7. 均值滤波器并没有消除全部高频成分。

  8. 在图像上应用二维高斯滤波器,只需要现在横向线条上应用一维高斯滤波器(过滤水平方向的频率),然后在纵向线条上应用另一个一维高斯滤波器(过滤垂直方向的频率)。高斯绿泥器是一种可分离滤波器(二维内核可分解成两个一维滤波器)。由于可分离滤波器所用的乘法运算更少,因此它的计算速度通常比不可分离滤波器要快。cv::sepFilter2D普通的可分离滤波器,cv::filter2D函数之间应用二维内核。

  9. 如果图像中一个较暗的区域中出现了一个亮点,那么用拉普拉斯运算就会使这个亮点变得更亮。因为图像中的边缘就是那些灰度发生跳变的区域,所以拉普拉斯锐化模板在边缘检测中很有用。一般增强技术对于陡峭的边缘和缓慢变化的边缘很难确定其边缘的位置。但此算子却可用二次微分正峰和负峰之间的过零点来确定,对孤立点或端点更为敏感,因此特别适用于以突出图像中的孤立点、鼓励线或线端点为目的的场合。同梯度算子一样,拉普拉斯算子也会增强图像中的噪声,有时用拉普拉斯算子进行边缘检测时,可将图像先进行平滑处理。

  10. 图像锐化处理的作用是使灰度反差增强,从而使模糊图像变得更加清晰。图像模糊的实质就是图像受到平均运算或积分运算,因此可以对图像进行逆运算,如微分运算能够突出图像细节,使图像变得更为清晰。由于拉普拉斯是一种微分算子,它的应用可增强图像中灰度突变的区域,减弱灰度的缓慢变化区域。因此,锐化处理可选择拉普拉斯算子对原图像进行处理,产生描述灰度突变的图像,再将拉普拉斯图像与原始图像叠加而产生锐化图像。

  11. 将原始图像通过拉普拉斯变换后增强了图像中灰度突变处的对比度,使图像中小的细节部分得到增强并保留了图像的背景色调,使图像的细节比原始图像更加清晰。基于拉普拉斯变换的图像增强已成为图像锐化处理的基本工具。

  12. 降低图像精度的过程称为缩减像素采样(downsampling),提升图像精度的过程称为提升像素采样(upsampling)。为尽可能保持图像质量,采用低通滤波器来实现。

  13. 在小图像(即像素较少的图像)中展现精致纹理和尖锐边缘的效果不如在较高分辨率的图像中展现它们的效果好(高清电视和普通电视的差别)。图像中精致的细节对应着高频,因此需要在缩小图像之前去除它的高频成分。

  14. Nyquist-Shannon定理,如果把图像缩小一半,那么其可见的频率带宽也将减少一半。

    cv::Mat reducedImage;  // 用于存储缩小后的图像
    cv::pyrDown(image, reducedImage); 	// 图像尺寸缩小一半
    

    该函数使用一个5x5的高斯滤波器,在把图像缩小一半之前先进行低通滤波。功能相反的函数有cv::pyrUp,它可以放大图像的尺寸。在这种提升像素采样的过程中,先在每两行和每列之间分别插入值为0的像素,然后对扩展后的图像应用同样的5x5高斯滤波器(但系数要扩大4倍)。这两个函数可用来创建图像金字塔。它是一个数据结构,由一幅图像不同尺寸的版本堆叠起来,用于高效的图像分析。

  15. 先缩小一幅图像再把它放大,显然不能完全让它恢复到原始状态,因为缩小过程中丢失的信息是无法恢复的。

  16. cv::resize是一个更通用的函数,它可以指定缩放后图像的尺寸。你只需要在调用它时指定新的尺寸,这个尺寸可用比原始图像小,也可以比原始图像大:

    cv::Mat resizedImage;		// 用于存储缩放后的图像
    cv::resize(image, resizedImage, cv::Size(image.cols/4, image.rows/4)); // 行和列均缩小为原来的1/4
    也可以指定缩放比例。在参数中提供一个空的图像实例,然后提供缩放比例:
    cv::resize(image, resizedImage, cv::Size(), 1.0/4.0. 1.0/4.0); 	// 缩小为原来的1/4
    
  17. 按比例缩放图像后,必须进行像素插值,以便在原像素之间的位置插入新的像素值。

  18. 进行插值的最基本方法是使用最近邻策略。把带生成图像的像素网格放在原图像的上方,每个新像素被赋予原图像中最近邻像素的值。

    cv::resize(reduced, newImage, cv::Size(), 3, 3, cv::INTER_NEAREST);
    // 双线性插值
    cv::resize(reduced, newImage, cv::Size(), 4, 4, cv::INTER_LINEAR);
    
  19. 中值滤波器对消除椒盐噪声非常有用。因为中值滤波器是非线性的,所以不能用核心矩阵表示,也不能进行卷积运算。但它通过操作一个像素的领域,来确定输出的像素值。中值滤波器把当前像素和它的领域组成一个集合,然后计算处这个集合的中间值,以此作为当前像素的值。这正是中值滤波器在消除椒盐噪声时如此高效的原因。

  20. 中值滤波器还利于保留边缘的尖锐度,但它会洗去均质区域的纹理。因此中值滤波器具有良好的视觉效果。

  21. 高通滤波器进行边缘检测,放大图像中的高频成分。

  22. Sobel滤波器 只对垂直或水平方向的图像频率起作用(具体方向取决于滤波器选用的内核),所以被认为是一种定向滤波器。

    cv::Sobel(image, // 输入 
    		sobelX,  // 输出
    		CV_8U,   // 图像类型
    		1,0,    // 内核规则 (1,0)为水平方向, (0,1)为垂直方向
    		3,    // 正方形内核尺寸
    		0.4,128  // 比例和偏移量
    		);
    
  23. 照片编辑软件的图像浮雕化特效。实际上,就算定向滤波器生成的。

  24. 组合垂直和水平方向,得到Sobel滤波器的范数:

    // 计算Sobel滤波器的范数
    cv::Sobel(image, sobelX, CV_16S, 1,0);
    cv::Sobel(image, sobelY, CV_16S, 0, 1);
    cv::Mat sobel;
    // 计算L1范数
    sobel = abs(sobelX) + abs(sobelY);
    // 计算梯度的L2范数和方向
    cv::Mat norm, dir;
    // 将笛卡尔坐标换算成极坐标,得到幅值和角度
    cv::cartToPolar(sobelX, sobelY, norm, dir);
    注意:得到的方向用弧度表示。如果要使用角度,只需要增加一个参数并设为true;
    
  25. 在convertTO方法中使用可选的缩放参数可得到一幅图像,图像中的白色用0表示,更黑的灰色阴影用大于0的值表示。这个图像可以很方便地显示Sobel算子地范数。也即为什么Sobel算子可以称作边缘检测器的原因。

    // 找到Sobel最大值
    double sobmin, sobmax;
    cv::minMaxLoc(sobel, &sobmin, &sobmax);
    // 转换成8位图像
    // sobelImage = -alpha*sobel + 255;
    cv::Mat sobelImage;
    sobel.convertTo(sobelImage, CV_8U, -255./sobmax, 255);
    // 接着对这幅图像阈值化,得到图像轮廓的二值分布
    cv::threshold(sobelImage, sobelThresholded, threshold, 255, cv::THRESH_BINARY);
    
  26. Sobel算子是一种典型的用于边缘检测的线性滤波器,它基于两个简单的3x3内核。如果把图像看作二维函数,那么Sobel算子就算图像在垂直和水平方向变化的速度。也称梯度。

  27. Sobel算子在水平和垂直方向计算像素值的差分,得到图像梯度的近似值。它在像素周围的一定范围内进行运算,以减少噪声带来的影响。

  28. 因为梯度是一个二维向量,所以它有范数和方向。梯度向量的范数表示变化的振幅,计算时通常被当作欧几里得范数(L2范数)。在图像处理领域,通常把绝对值之和作为范数进行计算,即L1范数,它得到的结果与L2范数比较接近,但计算速度块快。

  29. 梯度向量总是指向变化最剧烈的方向。对于一幅图像来说,这意味着梯度的方向和边缘垂直。从较暗区域指向较亮区域

  30. 应用倒数滤波器之前应用高斯平滑滤波器,这会减少对噪声的敏感度。

  31. 几个梯度算子(定向滤波器,所有这些定向滤波器都会计算图像函数的一阶导数。因此,在滤波器方向上像素强度变化大的区域将得到较大的值,较平坦的区域将得到较小的值。正因如此,计算图像导数的滤波器被称为高通滤波器。)

    • Prewitt算子用来计算某个像素位置的梯度。
    • Roberts算子基于简单的2x2内核’
    • Scharr算子可以更精确地计算梯度方向,在cv::Sobel()函数中,参数为CV_SCHARR参数
  32. 高斯导数 往往会放大图像中的噪声和细小的高对比度细节。为了减少这些高频成分的影响,最好在应用导数滤波器之前对图像做平滑处理。平滑化图像和计算导数,可以通过选用合适的平滑内核完成。此时它已经成为一个带通滤波器,部分较高的频率被高斯滤波器移除,较低的频率被Sobel滤波器移除。
  33. 项的累加和的导数等于项的导数的累加和
  34. 在OpenCV中,图像的坐标系为左上角点,x的值就是从左往右,y的值的大小就算从上到下;相应的图像的行列和坐标轴存在以下关系,col<=>x, row<=>y。
  35. 拉普拉斯算子也是一种基于图像导数运算的高通线性滤波器,它通过计算二阶导数来度量图像的曲率。
  36. 在OpenCV中,可用cv::Laplacian函数计算图像的拉普拉斯算子。拉普拉斯算子的计算在浮点数类型的图像上进行。要对结果作缩放处理才能使其正常显示。缩放基于拉普拉斯算子的最大绝对值,其中数值0对应灰度级128。
  37. 与Sobel算子相比,拉普拉斯算子在计算时可用使用更大的内核,并且对图像噪声更加敏感,因此是更理想的选择(除非要重点考虑计算效率)。因为这些更大的内核是用高斯函数二阶导数计算的,因此这个算子也常称作高斯–拉普拉斯算子(Laplacian of Gaussian, LoG)。注意,拉普拉斯算子内核的值的累加和总是0。这保证在强度值恒定的区域中,拉普拉斯算子将变为0。因为拉普拉斯算子度量的是图像函数的曲率,所以他在平坦区域中应该等于0。
  38. 从内核的定义可以明显看出,任何孤立的像素值(即与周围像素差别很大的值)都会被拉普拉斯算子放大,这时因此该算子对噪声非常敏感。但更值得关注的是图像边缘附近的拉普拉斯值。图像边缘是灰度值在不同区域之间快速过渡的产物。观察图像函数在边缘上的变化(例如从暗道亮的边缘)可以发现一个规律:如果灰度级上升,那么肯定存在从正曲率(强度值开始上升)到负曲率(强度值即将到达高地)的品换过渡。因此,如果拉普拉斯值从正数过渡到负数(反之亦然),就说明这个位置很可能是边缘,或者说边缘位于拉普拉斯函数的过零点。可以检测到亚像素级精度的图像边缘,至少从理论上是成立的。
  39. 可以使用简化的算法来检测过零点的大致位置。这种算法首先对拉普拉斯图像阈值化(采用的阈值为0),得到正数和负数之间的分割区域,这两个区域之间的边界就是过零点。用形态学运算来提取这些轮廓,也就是用拉普拉斯图像减去膨胀后的图像。

  40. 拉普拉斯的过零点方法检测了所有的边缘,不能区分强边缘和弱边缘。有些可见边缘是由于压缩失真产生的。正是由于这些原因,拉普拉斯算子才检测出了那么多边缘。在实际检测边缘时(例如梯度很大的过零点才能确认的边缘),只会把拉普拉斯算子与其他算子结合使用。在不同比例下检测兴趣点时,拉普拉斯算子和其他二阶算子是非常有用的。

  41. 用拉普拉斯算子增强图像的对比度,从图像减去它的拉普拉斯图像,可用增强图像的对比度。

  42. 高斯差分,高斯滤波器过滤的频率范围取决于参数$\delta$的值,这个参数控制了滤波器的宽度。现在用两个不同带宽的高斯滤波器对一幅图像坐滤波,然后将这两个结果相减,就能得到由较高的频率构成的图像。这些频率被一个滤波器保留,被另一个滤波器丢弃。这种运算称为高斯差分(Difference of Gaussians, DoG),

    cv::GaussianBlur(image, gauss20, cv::size(), 2.0);
    cv::GaussianBlur(image, gauss22, cv::size(), 2.2);
    // 计算高斯差分
    cv::subtract(gauss22, gauss20, dog, cv::Mat(), CV_32F);
    // 计算DoG的过零点
    zeros = laplacian.getZeroCrossings(dog);
    

    事实上,如果选择了合适的$\delta$值,DoG算子其实可以和好地模拟LoG滤波器。如果从一个$\delta$值的增长队列中选取连续的数据对,用以计算一系列的高斯差分,就可以得到该图像的尺度空间表示法。这种尺度表示法非常有用,例如用于检测尺度不变特征

Table of Contents