我的个人博客:谋仁·Blog
微信公众号:谋仁的麻袋


一些概念

颜色空间

为了表示一种特定的颜色,我们通常建立一维、二维、三维甚至四维空间坐标来表示,这种坐标系统所能定义的色彩范围即颜色空间。下面列举常见的颜色空间:

  • RGB模型
    RGB模型将颜色编码成(R,G,B)即(Red红,Green绿,Blue蓝)。每个分量范围是[0,255]共256级。因此,RGB模型可以表示256×256×256≈1670万种颜色。
    注意:在OpenCV中存储顺序不是RGB,而是BGR。

  • 单通道模型
    单通道图即灰度图,图片像素颜色由一维的值来表示,分量范围是[0,255]。0是黑色,255是白色,中间是不同程度的灰色。

  • 二值模型
    二值图像即黑白图像,每个像素的颜色由一维的值来表示,分量范围是[0,1],0代表黑色,1代表白色。

  • HSV模型
    HSV模型由三个参数来表示颜色:色调(H)、饱和度(S)、明度(V)。
    色调(H)用角度表示,范围[0°,360°],红色为0°,从红色开始按逆时针方向计算。(如下图)
    饱和度(S)表示颜色接近光谱色的程度,饱和度越高,颜色越深而艳。取值范围[0%,100%]。
    明度(V)表示颜色鲜亮的程度,取值范围[0%,100%],0%为黑色,100%为白色。
    HSV模型图示

  • Lab模型
    Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白;a表示从红色到绿色的范围,取值范围是[127,-128];b表示从黄色到蓝色的范围,取值范围是[127,-128]。图示如下:
    Lab模型

    图像的深度

    图像用于存储像素的值占得比特(bit)位数(就是转换为二进制的位数),就是图像的深度。例如:在二值模型中每个像素点的值是一维的,且取值范围是[0,1],所以仅用一位比特位就可以满足,图像深度为1;RGB模型中每个像素用R,G,B三个分量表示,每个分量取值范围[0,255],用8位可以表示,所以像素深度总共为24位。

    图像通道

    为了储存像素数据,每一个像素点用一个或多个分量来存储数据,分量的数量就是图像通道。例如,RGB模式中每个像素点由Red、Dreen、Blue三个分量来表示,每个取值范围都是[0,255],这个图像通道就是3。灰度图仅用一个取值范围为[0,255]数值来表示,灰度图的图像通道就是1。

    颜色空间的转换-cvtColor

    函数cvtColor

    cv::cvtColor()作用是将图像从一个颜色空间转换到另一个颜色空间。并且在转换的过程中能够保证数据的类型不变,即转换后的图像的数据类型和位深与源图像一致。
    函数定义:

1
2
3
4
5
6
void cv::cvtColor(
cv::InputArray src, // 输入图像
cv::OutputArray dst, // 输出图像
int code, // 模式转换的映射码
int dstCn = 0 // 输出的通道数 (0='automatic')
);
  • src: Mat类,输入待转换的图像
  • dst:Mat类,转换后新图像放在这里
  • code:转换的代码或标识,即图像转换的格式(模式1→模式2)
    OpenCV提供的映射:
  • dstCn:(默认是0)目标图像通道数,如果取值为0,则由src和code决定

    示例(BGR转换成灰度图)

    源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG

int main()
{
Mat img = imread("D:\\My Bags\\图片\\Test.jpg");
Mat imgGray;
cvtColor(img, imgGray, COLOR_BGR2GRAY);
imshow("原图", img);
imshow("灰度图", imgGray);
waitKey(0);
return 0;
}

运行结果:

高斯滤波-GaussianBlur

函数GaussianBlur

函数的定义:

1
2
3
4
5
6
void GaussianBlur( InputArray src, //输入的图像
OutputArray dst, //接收输出的图像
Size ksize,//高斯内核大小
double sigmaX,
double sigmaY = 0,
int borderType = BORDER_DEFAULT);
  • src:Mat类,输入待处理的图像
  • dst:Mat类,输出与原图大小和类型相同的图像
  • ksize:高斯内核的大小,决定了滤波的程度。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来
  • sigmaX:高斯核函数在X方向上的标准偏差
  • sigmaY:高斯核函数在Y方向上的标准偏差,如果sigmaY是0,则函数会自动将sigmaY的值设置为与sigmaX相同的值,如果sigmaX和sigmaY都是0,这两个值将由ksize.width和ksize.height计算而来
  • borderType:推断图像外部像素的某种便捷模式,有默认值BORDER_DEFAULT,如果没有特殊需要不用更改

    示例

    源代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG

int main()
{
Mat img = imread("D:\\My Bags\\图片\\Test.jpg");
Mat imgGaus;
GaussianBlur(img, imgGaus, Size(7, 7), 7);
imshow("原图", img);
imshow("高斯滤波图", imgGaus);
waitKey(0);
return 0;
}

运行结果:

图像边缘检测-Canny

函数Canny

函数定义

重载1:

1
2
3
4
5
6
7
void Canny( InputArray image,//输入图像
OutputArray edges,//输出图像
double threshold1, //阈值1
double threshold2,//阈值2
int apertureSize = 3, //Sober算子大小
bool L2gradient = false //是否采用更精确的方式计算图像梯度
);

两个阈值参数:

  • 低于阈值1的像素点会被认为不是边缘;
  • 高于阈值2的像素点会被认为是边缘;
  • 在阈值1和阈值2之间的像素点,若与第2步得到的边缘像素点相邻,则被认为是边缘,否则被认为不是边缘
  • 建议上限是下限的2或3倍

apertureSize参数:

  • 为Sobel()运算提供内核大小,默认值为3

重载2:

1
2
3
4
5
6
7
void Canny( InputArray dx, 
InputArray dy,
OutputArray edges,//输出图像
double threshold1, //下阈值
double threshold2,//上阈值
bool L2gradient = false
);
  • InputArray dx:输入图像在x方向的导数
  • InputArray dy:输入图像在y方向的导数

    示例

    源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG

int main()
{
Mat img = imread("D:\\My Bags\\图片\\Test.jpg");
Mat imgGaus,imgEdge;
GaussianBlur(img, imgGaus, Size(3, 3), 3);//高斯滤波降噪
Canny(imgGaus, imgEdge, 100, 300);//边缘检测
imshow("原图", img);
imshow("边缘图", imgEdge);
waitKey(0);
return 0;
}

运行结果:

图像的膨胀和侵蚀-dilate和erode

函数dilate/erode

dilate函数的定义

1
2
3
4
5
6
7
8
void dilate( InputArray src, 
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue()
);

erode函数的定义

1
2
3
4
5
6
7
8
void erode( InputArray src, 
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue()
);

两个函数参数完全一样,参数解释:

  • src:Mat类,输入的原图
  • dst:Mat类,用于存放处理后的图像
  • kernel:用于膨胀/侵蚀操作的结构元素,当为NULL时,那么默认使用一个3 x 3 的方形结构元素,可以使用getStructuringElement()(下文有详细介绍)来创建结构元素
  • anchor:结构元素的锚点位置,默认值value(-1,-1)表示锚点位于结构元素中心
  • iterations:进行膨胀/侵蚀操作迭代执行的次数,默认是1
  • borderType:用于推断图像外部像素的某种推断模式。默认值BORDER_CONSTANT
  • Scalar& borderValue:边缘值,默认值morphologyDefaultBorderValue()。一般不用改动

    补充:函数getStructuringElement

    函数的定义:
1
2
3
4
Mat getStructuringElement(int shape, 
Size ksize,
Point anchor = Point(-1,-1)
);

作用:返回(Mat类型)指定形状和尺寸的结构元素

参数解释:

  • shape:结构元素的形状
    矩形:MORPH_RECT;
    交叉形:MORPH_CROSS;
    椭圆形:MORPH_ELLIPSE;
  • ksize:结构元素的尺寸,同GaussianBlur函数的ksize
  • anchor:锚点的位置,默认值Point(-1,-1),表示锚点位于中心点

示例

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG

int main()
{
Mat imgDilate, imgErode;
Mat img = imread("D:\\My Bags\\图片\\Test.jpg");
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));//新建一个结构元素
dilate(img, imgDilate, kernel);//膨胀操作
erode(img, imgErode, kernel);//侵蚀操作
imshow("原图", img);
imshow("膨胀图", imgDilate);
imshow("侵蚀图", imgErode);
waitKey(0);
return 0;
}

运行结果: