OpenCV

1.  图像读取与显示

知识点:
imread -- 读取图像

imshow -- 显示图像

imshow只支持浮点数类型跟字节类型图像显示

示例:

#include<iostream>
#include<opencv2\opencv.hpp>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	Mat src;  //声明一个保存图像的类

    src = imread("shuibei1.jpg"); //在计算机内存中,数字图像以矩阵的形式存储和运算,
	                                 //在OpenCV和MatLab中图像读取之后对应一个矩阵。
	                                 //用Mat表示一个矩阵
	                                //imread()读取的永远是彩色图像(即图片本身的颜色)

	//Mat src = imread("shuibei.jpg",IMREAD_GRAYSCALE); //将彩色图转为灰度图像

	
	if (src.empty()) //判断图像文件是否存在
	{
		cout << "could not load image..." << endl;
		system("pause");
		return -1;
	}

	namedWindow("输入窗口",WINDOW_FREERATIO); //namedWindow() 创建一个窗口,这个窗口大小比例可以调整

	imshow("输入窗口",src); //imshow 函数有两个参数,第一个表示窗口的名称,src表示数据对象
	                        //通过imshow直接显示的时候是根据图片的尺寸直接显示相同大小的窗口,窗口大小不可以调整

	waitKey(0);  // waitKey(0)表示执行到此行,程序就停止;
	             // waitKey(1)表示停顿1ms,在继续往下执行

	destroyAllWindows(); //destroyAllWindows()表示执行到该行时前面所有的执行窗口全部关闭

	
	return 0;
}

2. 图像色彩空间转换

知识点:

1. 色彩空间转换函数 -- cvtColor

COLOR_BGR2GRAY = 6 彩色到灰度

COLOR_GRAY2BGR = 8 灰度到彩色

COLOR_BGR2HSV = 40 BGR到HSV

COLOR_HSV2BGR = 40 HSV到BGR

2. 图像保存函数 -- imwrite

第一个参数是图像保存的路径

第二个参数是图像内存对象

示例:

 主程序:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<quickopencv.h>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	Mat src;  //声明一个保存图像的类

	src = imread("tupian.jpg");

	if (src.empty()) //判断图像文件是否存在
	{
		cout << "could not load image..." << endl;
		system("pause");
		return -1;
	}

	//namedWindow("输入窗口", WINDOW_FREERATIO); 

	imshow("输入窗口", src); 

	QuickDemo qd;
	qd.colorSpace_Demo(src);

	waitKey(0);  

	destroyAllWindows(); 


	return 0;
}

 3. 图像对象的创建与赋值

 创建方法 :

克隆   Mat m1 = src.clone();


复制    Mat m2;      src.copyTo(m2);

赋值法   Mat m3 = src; //赋值法,当m3改变时src同样变

创建空白对象(常见的初始化方法):

Mat  m4 = Mat::zeros(int i, int j, int type); //创建i * j矩阵,类型是type,并将元素初始化为0

Mat  m4 = Mat::zeros(src.Size(),src.type);

Mat m5 = Mat::zeros(Size(400,400), CV_8UC3);  // 8位无符号数,图像通道3个。

Mat m6 = Mat::ones(int i, int j, int type); // 创建i * j矩阵,类型是type,并将元素初始化为1

Mat m6 = Mat::ones(Size(400,400), CV_8UC3);

Mat kernel = (Mat_<char>(3,3) << 0,-1,0,

-1,5,-1,

0,-1,0);   // 结果是3*3矩阵;

Mat常用函数:

Mat::channels();  //返回通道数

Mat::rows;   //返回行数

Mat::cols;   //返回列数

cv::Scalar(v1, v2, v3, v4)的这四个参数就依次是BGRA,即蓝、绿、红和透明度。
 

示例:

函数:

#include<quickopencv.h>

void QuickDemo::mat_creation_Demo()
{
	//Mat m1, m2;
    //m1 = image.clone(); //克隆
	//image.copyTo(m2);  //拷贝

	//创建空白对象    常见的初始化方法:
	//Mat m3 = Mat::zeros(Size(8,8),CV_8UC1); // CV_8UC1 :字面拆解为8位无符号数,图像通道1个,例如灰度图
	Mat m3 = Mat::zeros(Size(400,400), CV_8UC3);  // 图像通道3个。
	//Mat m3 = Mat::ones(Size(8, 8), CV_8UC3); // 输出结果是只把每个通道的第一个像素点初始化为1
	//Mat m3 = Mat::ones(Size(8, 8), CV_8UC1); //结果是每个元素都是1

	//m3 = 127; //  输出结果是每个通道的第一个像素点赋值为127
	m3 = Scalar(127,127,127); // 输出结果是每个通道的像素点赋值为127

	//Scalar(v1, v2, v3, v4)的这四个参数就依次是BGRA,即蓝、绿、红和透明度。
	//m3 = Scalar(255, 0, 0); //输出的图像是纯蓝色
	//m3 = Scalar(0, 255, 0); //输出的图像是纯绿色
	//m3 = Scalar(0, 0, 255); //输出的图像是纯红色

	std::cout << "width: "<< m3.cols << "  height: " << m3.rows << "  channels:" << m3.channels() << std::endl; //打印m3的宽度(列数),高度(行数)和通道数
	//std::cout << m3 << std::endl;

	//imshow("图像", m3);

	//Mat m4 = m3; //将m3赋值给m4,m4就是m3,所以当m4变化时,m3结果同样改变
	//m4 = Scalar(0, 255, 255);
	//imshow("图像", m3);

	//Mat m4 = m3.clone();
	Mat m4;
	m3.copyTo(m4);
	m4 = Scalar(0, 255, 255);
	imshow("图像", m3);
	imshow("图像1", m4);

	/*Mat kernel = (Mat_<char>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
	std::cout << kernel << std::endl;
	imshow("图像2", kernel);*/
}

 主程序:

#include<iostream>
#include<opencv2/opencv.hpp>
#include<quickopencv.h>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	Mat src;  //声明一个保存图像的类

	src = imread("tupian.jpg");

	if (src.empty()) //判断图像文件是否存在
	{
		cout << "could not load image..." << endl;
		system("pause");
		return -1;
	}

	//namedWindow("输入窗口", WINDOW_FREERATIO); 

	imshow("输入窗口", src);

	QuickDemo qd;
	qd.mat_creation_Demo();

	waitKey(0);

	destroyAllWindows();


	return 0;
}

4. 图像像素的读写操作

单通道:
     int pv = image.at<uchar>(row, col); //获取此时该点对应的像素值

 3通道:

     Vec3b bgr = image.at<Vec3b>(row, col);  // Vec3b 表示每一个Vec3b对象中,存储3个                                                                                            char(字符型)数据
                                                    // 这样可以直接获得每一个像素点的三个值。bgr相当于一个数组
                                                   //如果是int图像 就是Vec3i,如果是浮点图像,就是Vec3f   

  示例:



             函数:

#include<quickopencv.h>

void QuickDemo::pixel_visit_Demo(Mat &image)
{
	int w = image.cols; //获取图像的列(宽)
	int h = image.rows; //获取图像的行(高)
	int dims = image.channels(); //获取图像的通道数(维度)dims = 1灰度图; dims = 3彩色图

	//数组的方式遍历图像的每个像素点
	/*for (int row = 0; row < h; row++)
	{
		for (int col = 0; col < w; col++)
		{
			if (dims == 1) // 灰度图
			{  
				//uchar的取值范围是0-255
				int pv = image.at<uchar>(row, col); //获取此时该点对应的像素值
				image.at<uchar>(row, col) = 255 - pv; //给该点重新赋像素值
			}
			if (dims == 3) // 彩色图
			{
				Vec3b bgr = image.at<Vec3b>(row, col);  // Vec3b 表示每一个Vec3b对象中,存储3个char(字符型)数据
				                                       // 这样可以直接获得每一个像素点的三个值。bgr相当于一个数组
				                                       //如果是int图像 就是Vec3i,如果是浮点图像,就是Vec3f

				// 对彩色图像像素点进行重新赋值
				image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
				image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
				image.at<Vec3b>(row, col)[2] = 255 - bgr[2];
			}

		}
	}*/

	//指针的方式遍历图像的每个像素点
	for (int row = 0; row < h; row++)
	{
		uchar* current_row = image.ptr<uchar>(row);

		for (int col = 0; col < w; col++)
		{
			if (dims == 1) // 灰度图
			{
				//uchar的取值范围是0-255
				int pv = *current_row; //获取此时该点对应的像素值
				*current_row++ = 255 - pv; //给该点重新赋像素值
			}
			if (dims == 3) // 彩色图
			{
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
			}

		}
	}


	imshow("像素读写演示", image);

}

  主程序:
 

#include<iostream>
#include<opencv2/opencv.hpp>
#include<quickopencv.h>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	Mat src = imread("tupian.jpg");

	if (src.empty()) //判断图像文件是否存在
	{
		cout << "could not load image..." << endl;
		system("pause");
		return -1;
	}

	//namedWindow("输入窗口", WINDOW_FREERATIO); 

	imshow("输入窗口", src);

	QuickDemo qd;
	qd.pixel_visit_Demo(src);

	waitKey(0);
	destroyAllWindows();
	system("pause");
	return 0;
}

5. 图像像素的算术操作

加法:add (image,m,dst); //三个参数,前两个分别是进行相乘的两个图象第三个参数是输出图像

减法:subtract(image,m,dst);

乘法:multiply(image,m,dst); 

除法:divide(image,m,dst); 

#include<quickopencv.h>

void QuickDemo::operators_Demo(Mat &image)
{
	Mat dst;
	/*dst = image + Scalar(50, 50, 50);
	imshow("加法操作", dst);*/

	/*dst = image - Scalar(50, 50, 50);
	imshow("减法操作", dst);*/

	/*dst = image / Scalar(5, 5, 5);
	imshow("除法操作", dst);*/

	//Mat m = Mat::zeros(image.size(), image.type()); //创建一个与image同类型的变量
	//m = Scalar(2, 2, 2);
	//dst = image * m;
	//imshow("乘法操作", dst);    // 错误。opencv中两个图像不能进行直接的乘法

	//opencv中,有一个函数可以直接进行两个图像相乘
	Mat m = Mat::zeros(image.size(), image.type());
    m = Scalar(20, 20, 20);
	/*multiply(image,m,dst);//三个参数 ,前两个分别是进行相乘的两个图象,第三个参数是输出图像
	imshow("乘法操作", dst);*/

    dst = Mat::zeros(image.size(), image.type());

	加法
	//int w = image.cols; //获取图像的列(宽)
	//int h = image.rows; //获取图像的行(高)
	//int dims = image.channels(); //获取图像的通道数(维度)dims = 1灰度图; dims = 3彩色图

	//for (int row = 0; row < h; row++)
	//{
	//	for (int col = 0; col < w; col++)
	//	{
	//		Vec3b p1 = image.at<Vec3b>(row, col);  // Vec3b 表示每一个Vec3b对象中,存储3个char(字符型)数据
	//		Vec3b p2 = m.at<Vec3b>(row, col);      // 这样可以直接获得每一个像素点的三个值。bgr相当于一个数组
	//			                                   //如果是int图像 就是Vec3i,如果是浮点图像,就是Vec3f

	//		//saturate_cast<uchar>函数的作用是对括号里的数据进行一个范围的限定,结果在uchar的范围进行取值
	//		//如果结果小于0那就取0,如果大于255,就取255
	//		
	//		dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar> (p1[0] + p2[0]); 
	//		dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);
	//		dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);
	//	}
	//}


	//常用加减乘除方法:

	//加法
	add(image, m, dst);
	imshow("加法操作", dst);
	//减法
	subtract(image, m, dst);
	imshow("减法操作", dst);
	//乘法
	multiply(image, m, dst);
	imshow("乘法操作", dst);
	//除法
	divide(image, m, dst);
	imshow("除法操作", dst);
}

6. 滚动条操作演示 --- 调整图像亮度

createTrackbar是Opencv中的API,可在显示图像的窗口中快速创建一个滑动控件,用于手动调节阈值。具体定义如下:(原文链接:https://blog.csdn.net/mysee1989/article/details/41379817)

CV_EXPORTS int createTrackbar(const string& trackbarname, const string& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);

形式参数一: trackbarname:滑动空间的名称;

形式参数二: winname:滑动空间用于依附的图像窗口的名称;

形式参数三: value:初始化阈值;

形式参数四: count:滑动控件的刻度范围; 滑块可以滑动的范围永远都是[0, count]

形式参数五: TrackbarCallback是回调函数,其定义如下:

typedef void (CV_CDECL *TrackbarCallback)(int pos, void* userdata);

         第一个形参pos,它表示的是当前滑块所在的位置,它的值是createTrackbar()传给他的,也就是createTrackbar()形参value的值,这个传输过程是在createTrackbar()内部实现的,无需深究,然后回调函数形参userdata的值就是通过createTrackbar()的形参userdata直接得到的,所以createTrackbar()的形参userdata其实就是专门给回调函数准备的。

回调函数的理解:

回调函数里会处理并显示图片,就是每次滑动条滑块被用户拖动一个位置,createTrackbar()中的Value值会变化,而且会访问并执行回调函数一次。回调函数里有对图片的相关操作以及显示操作,所以每动一次滑块,图片显示效果就不同。

形式参数六:userdata:这个参数是用户传给回调函数的数据,用来处理轨迹条事件,默认值为0。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

 //刚开始的亮度值
Mat g_c, dst, m;
int lightness = 50;

static void on_track(int, void*)
{
	m = Scalar(lightness, lightness, lightness);
	add(g_c,m,dst);
	imshow("亮度调整",dst);
	cout <<lightness << endl;
}

void QuickDemo::tracking_bar_Demo(Mat &image)
{
	namedWindow("亮度调整",WINDOW_AUTOSIZE); //创建一个窗口  WINDOW_AUTOSIZE是自适应调整窗口大小
	dst = Mat::zeros(image.size(), image.type());
    m = Mat::zeros(image.size(), image.type());
	g_c = image;

	int max_value = 100;  //最大的亮度值
	
	createTrackbar("Value Bar","亮度调整",&lightness,max_value, on_track);

	on_track(0, 0);

7.  滚动条操作演示 --- 参数传递与调整亮度与对比度

addWeighted函数:

1、函数作用
作用:实现两幅图片的(叠加)线性融合;

2、函数原型

void addWeighted(InputArray src1double alpha, InputArray src2, double beta, double gamma, OutputArray dstint dtype=-1)

src1:第一幅图片(背景图片);
alpha:第一幅图片的权重;
src2:第二幅图片(需要融合的图片);
beta:第二幅图片的权重;
gamma:一个作用到加权和后的图像上的标量, 可以理解为加权和后的图像的偏移量;
(计算两个数组的加权和 (dst =alphasrc1 + betasrc2 + gamma))
dst:融合后的图片(输出图片);
dtype:输出阵列的可选深度,有默认值-1。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

static void on_lightness(int b, void* userdata)
{
	Mat image = *((Mat*)userdata);
	Mat dst = Mat::zeros(image.size(), image.type());
	Mat m = Mat::zeros(image.size(), image.type());
	addWeighted(image,1.0,m,2.1,b,dst);  //可以融合两张图
	imshow("亮度与对比度调整", dst);
}

static void on_contrast(int b, void* userdata)
{
	Mat image = *((Mat*)userdata);
	Mat dst = Mat::zeros(image.size(), image.type());
	Mat m = Mat::zeros(image.size(), image.type());

	double contrast = b / 100.0;

	addWeighted(image, contrast, m, 3.2, 0, dst);  //可以融合两张图
	imshow("亮度与对比度调整", dst);
}

void QuickDemo::tracking_bar_Demo(Mat &image)
{
	namedWindow("亮度与对比度调整", WINDOW_AUTOSIZE); //创建一个窗口  WINDOW_AUTOSIZE是自适应调整窗口大小
	int lightness = 50;  //亮度初始值
	int max_value = 100;  //最大的亮度值
	
	int contrast_value = 100; //初始对比度

	createTrackbar("Value Bar", "亮度与对比度调整", &lightness, max_value, on_lightness,(void*)&image);

	createTrackbar("Contrast Bar", "亮度与对比度调整", &contrast_value, 200, on_contrast, (void*)&image);

	on_lightness(50, &image);
	on_contrast(50, &image);

}

8. 键盘响应操作

函数原型:int waitKey(int  delay=0)

函数功能:
- 延时delay毫秒,默认0则延时无限长,必须有键按下才继续执行。 
- 函数返回值为按下的键的ASCII码值,没按键则返回-1

waitKey(0),表示程序会无限制的等待用户的按键事件;

waitKey(1), 表示程序每1ms检测一次按键,检测到返回按键值,检测不到返回 - 1;

waitKey(100), 表示程序每100ms检测一次按键,检测到返回按键值,检测不到返回 - 1;

函数:
 

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::key_Demo(Mat &image)
{
	Mat dst = Mat::zeros(image.size(),image.type());
	while (true)
	{
		int c = waitKey(100); //waitKey(0),表示程序会无限制的等待用户的按键事件;
		                      //waitKey(1), 表示程序每1ms检测一次按键,检测到返回按键值,检测不到返回 - 1;
			                //waitKey(100), 表示程序每100ms检测一次按键,检测到返回按键值,检测不到返回 - 1;
		if (c == 27) //退出
		{
			break;
		}
		if (c == 49) //1
		{
			std::cout << "you enter key # 1"  << std::endl;
			cvtColor(image, dst,COLOR_BGR2GRAY);
			imshow("键盘响应",dst);
		}
		if (c == 50) //2
		{
			std::cout << "you enter key # 2" << std::endl;
			cvtColor(image, dst, COLOR_BGR2HSV);
		}
		if (c == 51) //3
		{
			std::cout << "you enter key # 3"  << std::endl;
			dst = Scalar(50,50,50);
			add(image, dst, dst);
		}
		imshow("键盘响应", dst);
	}

}

9. OpenCv自带颜色表操作

1. 伪色彩函数applyColorMap(src, dst, colormap)

src:输入图像

dst:输出图像

colormap: 提供的色彩图代码值

2.  Look Up Table(LUT)查找表

伪色彩函数就是使用到了Look Up Table(LUT)查找表,是通过函数把图像像素对应的数值替换或者重新赋值到新的图像上,是一种映射的概念。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::color_style_demo(Mat &image)
{
	int colormap[] =
	{
		COLORMAP_AUTUMN,
		COLORMAP_BONE,
		COLORMAP_JET,
		COLORMAP_WINTER,
		COLORMAP_RAINBOW,
		COLORMAP_OCEAN,
		COLORMAP_SUMMER,
		COLORMAP_SPRING,
		COLORMAP_COOL,
		COLORMAP_PINK,
		COLORMAP_HOT,
		COLORMAP_PARULA,
		COLORMAP_VIRIDIS,
		COLORMAP_CIVIDIS,
		COLORMAP_TWILIGHT,
		COLORMAP_TWILIGHT_SHIFTED
	};

	Mat dst;
	int index = 0;
	while (true)
	{

		int c = waitKey(2000); //waitKey(0),表示程序会无限制的等待用户的按键事件;
							  //waitKey(1), 表示程序每1ms检测一次按键,检测到返回按键值,检测不到返回 - 1;
							  //waitKey(100), 表示程序每100ms检测一次按键,检测到返回按键值,检测不到返回 - 1;
		if (c == 27) //退出
		{
			break;
		}
		applyColorMap(image, dst, colormap[index % 19]);
		index++;
		imshow("颜色风格",dst);
	}
	
}

10. 图像像素的逻辑操作

绘制矩形:

1. rectangle函数

void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );

rectangle函数是用来绘制一个矩形框的,通过对角线上的两个顶点绘制矩形,通常用在图片的标记上。

img   图像.

pt1   矩形的一个顶点。

pt2   矩形对角线上的另一个顶点

color  线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。

thickness   组成矩形的线条的粗细程度。取负值时代表绘制填充了色彩的矩形。取正值时代表绘制了色彩的矩形。

line_type    线条的类型。

shift    坐标点的小数点位数。

2. Rect函数

Rect函数是画出图像中的矩形

Rect(x,y,width,height)

x, y 为左上角坐标,

width, height 则为长和宽。

图像像素逻辑操作:

Mat m1, m2, dst;
bitwise_and(m1,m2,dst);  //逻辑与

bitwise_or(m1, m2, dst); //逻辑或

bitwise_not(image, dst);  //逻辑非

bitwise_xor(m1, m2, dst);  //异或

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::bitwise_demo(Mat &image)
{
	
	Mat m1 = Mat::zeros(Size(256,256),CV_8UC3);  //创建空白图像
	Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);

	rectangle(m1,Rect(100,100,80,80),Scalar(255,255,0),-1,LINE_8,0);  //绘制矩形
	rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);

	imshow("m1",m1);
	imshow("m2", m2);

	Mat dst;
	//bitwise_and(m1,m2,dst);  //逻辑与
	//bitwise_or(m1, m2, dst); //逻辑或
	//bitwise_not(image, dst);  //逻辑非
	bitwise_xor(m1, m2, dst);  //异或

	imshow("像素位操作", dst);
}

11. 通道分离与合并

OpenCv中默认imread函数加载图像文件,加载进来的是三通道彩色图像,色彩空间是RGB色彩空间,通道顺序是BGR(蓝色,绿色,红色),对于三通道的图像OpenCv中提供了两个API函数用以实现通道分离与合并。

split --- 通道分离

merge --- 通道合并

mixChannels()是图像通道处理有关的一个函数,能够实现复杂通道的组合

函数原型:

void cv::mixChannels	
(	
	const Mat * 	src,
	size_t 	nsrcs,
	Mat * 	dst,
	size_t 	ndsts,
	const int * 	fromTo,
	size_t 	npairs 
)	

第一个参数:输入矩阵
第二个参数:输入矩阵的数量
第三个参数:输出矩阵
第四个参数:输出矩阵的数量
第五个参数:复制列表     表示第输入矩阵的第几个通道复制到输出矩阵的第几个通道
                        比如 {20,,1,1,2,0}表示:
                        src颜色通道0复制到dst颜色通道2
                        src颜色通道1复制到dst颜色通道1
                        src颜色通道2复制到dst颜色通道0

                 在这里插入图片描述
第六个参数:复制列表的数量

在这个函数原型中,如果输入矩阵和输出矩阵都不为1,就可以实现多个矩阵组合并或者一个矩阵拆分为多个复杂矩阵等功能。

注意:输入矩阵和输出矩阵的结构必须是预先定义好的。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::channels_demo(Mat &image)
{
	std::vector<Mat> mv;

	//通道顺序是BGR(蓝色,绿色,红色)
	split(image,mv);  // split-- - 通道分离  将image图片分离然后放入mv容器中
	imshow("蓝色",mv[0]);
	imshow("绿色", mv[1]);
	imshow("红色", mv[2]);

	Mat dst;
	mv[1] = 0;
	mv[2] = 0;
	merge(mv, dst);  //merge --- 通道合并 
	imshow("蓝色",dst);

	int from_to[] = {0,2,1,1,2,0};
	mixChannels(&image,1,&dst,1,from_to,3);
	imshow("通道混合", dst);
}

 12. 图像色彩空间转换

RGB 色彩空间

HSV色彩空间

YUV色彩空间

YCrCb色彩空间

API知识点:

cvtColor --- 色彩空间转换

inRange --- 提取指定色彩范围区域

OpenCV中的inRange()函数可实现二值化功能(这点类似threshold()函数),更关键的是可以同时针对多通道进行操作,使用起来非常方便!主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),该功能类似于之间所讲的双阈值化操作。

函数原型:

void inRange(	InputArray src,
				InputArray lowerb,  
				InputArray upperb,   
				OutputArray dst);

第一个参数:输入图像
第二个参数:H、S、V的最小值,示例:Scalar(low_H, low_S, low_V) 

                     指的是图像中低于这个lowerb的值,图像值变为0
第三个参数:H、S、V的最大值,示例:Scalar(low_H, low_S, low_V)

                     指的是图像中高于这个upperb的值,图像值变为0

在lowerb~upperb之间的值变成255   (0是黑色,255是白色)
第四个参数:输出图像,要和输入图像有相同的尺寸且为CV_8U类

image.copyTo()有两种形式:

1、image.copyTo(imageROI),作用是把image的内容粘贴到imageROI;

2、image.copyTo(imageROI,mask),作用是把mask和image重叠以后把mask中像素值为0(黑色)的点对应的image中的点变为透明,而保留其他点。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::inrange_demo(Mat &image)
{
	Mat hsv;
	cvtColor(image,hsv,COLOR_BGR2HSV);  //色彩空间转换
	Mat mask;
	inRange(hsv,Scalar(35,43,46),Scalar(77,255,255),mask);
	//imshow("色彩空间转换", mask);

	Mat redback = Mat::zeros(image.size(),image.type());
	redback = Scalar(0,0,200);
	//imshow("red", redback);

	bitwise_not(mask,mask);
	imshow("mask",mask);

	image.copyTo(redback, mask);  //image.copyTo(redback,mask): 作用是把mask和image重叠以后
	                             // 把mask中像素值为0(黑色)的点对应的image中的点变为透明,而保留其他点。
	imshow("ROI区域提取", redback);
}

13. 图像像素值统计

最小(min)

最大(max)

均值(mean)

标准方差(standard  deviation)

API知识点:

minMaxLoc --- 最大最小值函数

函数原型: 

void minMaxLoc( const Mat& src,  double* minVal, double* maxVal=0, 
Point* minLoc=0, Point* maxLoc=0, const Mat& mask=Mat() ); 

参数解释
参数1:InputArray类型的src,输入单通道数组(图像)
参数2:double*类型的minVal,返回最小值的指针。若无须返回,此值置为NULL。
参数3:double*类型的maxVal,返回最大值的指针。若无须返回,此值置为NULL。
参数4:Point*类型的minLoc,返回最小位置的指针(二维情况下)。若无须返回,此值置为NULL。
参数5:Point*类型的maxLoc,返回最大位置的指针(二维情况下)。若无须返回,此值置为NULL。
参数6:InputArray类型的mask,用于选择子阵列的可选掩膜。

说明: 
1  minMaxLoc寻找矩阵(一维数组当作向量,用Mat定义) 中最小值和最大值的位置. 
2  参数若不需要,则置为NULL或者0,即可. 
3  minMaxLoc针对Mat和MatND的重载中 ,第5个参数是可选的(optional),不使用不传递即可.

4. minMaxLoc针对单通道图像,minMaxIdx则不限制(不过输出的坐标会变成三维)。

meanStdDev --- 计算均值与标准方差 

函数原型:

void meanStdDev(InputArray src,OutputArray mean, OutputArray stddev, 
InputArray mask=noArray())

参数解释:

src:输入的源图像或矩阵

mean:输出的均值矩阵

stddev:输出的标准差矩阵

mask:可选的掩码矩阵

注意:若是彩色图像(3 通道),则是生成 3x1 矩阵,若是灰度图像(单通道),则生成 1x1 矩阵。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::pixel_statistic_demo(Mat &image)
{
	double minv, maxv;
	Point minLoc, maxLoc;

	std::vector<Mat> mv;
	split(image, mv); //通道分离

	  //minMaxLoc针对单通道图像  
	for (int i = 0; i < mv.size(); i++)
	{
		minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());   // minMaxLoc --- 最大最小值函数

		std::cout<< "No.channels:"<< i  << "  min value:" << minv << "  max value:" << maxv << std::endl;
	}

	Mat mean, stddev;
	meanStdDev(image, mean, stddev);  //  meanStdDev --- 计算均值与标准方差 
	
	std::cout << "means:" << mean << std::endl;
	std::cout << "stddev:" << stddev << std::endl;

	double pv = mean.at<double>(2, 0);  //获取均值的第三个数
	std::cout << pv << std::endl;

}

14. 图像几何形状绘制

1. circle --- 绘制圆形

void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color,
               int thickness=1, int line_type=8, int shift=0 );

参数解释:

img   ---   图像

center   ---      圆心坐标

radius    ---   圆形的半径

color   ---   线条的颜色

thickness   ---   如果是正数,表示描绘一个圆,数字的大小代表组成圆的线条的粗细程度。否则,表示圆是被填充。

line_typ   ---   线条的类型

shift   ---    圆心坐标点和半径值的小数点位数

2. line --- 绘制直线

void line(Mat& img, Point pt1, Point pt2, const Scalar& color, 
int thickness=1, int lineType=8, int shift=0)

参数解释:

img   ---   要绘制线段的图像

pt1   ---   线段的起点

pt2   ---   线段的终点

color   ---   线段的颜色,通过一个Scalar对象定义

thickness   ---   线条的宽度

lineType  ---   线段的类型,默认值为8

shift   ---   坐标点小数点位数

3. ellipse --- 绘制椭圆圆弧和椭圆扇形

绘制椭圆有两种方法

函数原型1:

void ellipse(InputOutputArray img, const RotatedRect& box, const Scalar& color,
             int thickness = 1, int lineType = LINE_8);

参数解释:

InputOutputArray类型的img,输入图像也是输出图像,如Mat类型。

RotatedRect类型的box,椭圆位置,里面有三个参数,中心,长轴短轴尺寸,角度。

Scalar类型的color,文字颜色。

int类型的thickness,文字线条宽度。

int类型的line_type,绘制线的类型,-1就是FILLED(填满),4是LINE_4(4连通域),8是LINE_8(8连通域),LINE_AA(抗锯齿线)

函数原型2:

void ellipse(InputOutputArray img, Point center, Size axes,
double angle, double startAngle, double endAngle,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);

 

参数解释:

img   ---   图像

center   ---   椭圆圆心坐标

axes   ---   轴的长度

angle   ---   偏转的角度

start_angle   ---   圆弧起始角的角度

end_angle   ---   圆弧终结角的角度

color   ---   线条的颜色

thickness   ---   线条的粗细程度

line_type   ---   线条的类型

shift   ---   圆心坐标点和数轴的精度

函数原型1是通过指定长短轴和倾斜角的椭圆弧,当起始角度和结束角度设置为0、360时才是封闭的椭圆,否则是一个弧;

函数原型2通过cv::RotatedRect指定需要绘制的椭圆的外边框,在矩形内绘制一个内接椭圆;

函数:
 

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::drawing_demo(Mat &image)
{
	//定义一个矩形
	Rect rect;
	rect.x = 200; //矩形左上角的横坐标
	rect.y = 100;  //矩形左上角的纵坐标
	rect.width = 100;  //矩形的宽
	rect.height = 100;  //矩形的高

	Mat bg = Mat::zeros(image.size(),image.type());
	//在指定图像上绘制矩形框
	//rectangle(image,rect,Scalar(0,0,255),1,8,0);
	rectangle(bg, rect, Scalar(0, 0, 255), 1, 8, 0);

	//在指定图像上绘制圆
	//circle(image,Point(350,400),15,Scalar(255,0,0),1,8,0);
	circle(bg, Point(350, 400), 15, Scalar(255, 0, 0), 1, 8, 0);
	//imshow("绘制演示",image);

	Mat dst;
	addWeighted(image, 0.7, bg, 0.3, 0, dst);  //addWeighted函数​实现两幅图片的(叠加)线性融合​
	imshow("绘制演示",dst);

	//在指定图像上绘制线
	line(bg, Point(200, 100), Point(300, 200), Scalar(0, 255, 0), 2, 8, 0);
	imshow("绘制演示", bg);

	//在指定图像上绘制椭圆
	RotatedRect rrt;
	rrt.center = Point(200, 200); //圆心坐标
	rrt.size = Size(100, 200);  //椭圆的短轴为参数size里面的第一个参数width,
	                             // 长轴为参数size里面的第二个参数height。
	rrt.angle = 0.0; // 偏转的角度

	ellipse(bg, rrt, Scalar(0, 255, 255), 2, 8);
	imshow("绘制演示",bg);
}

15. 随机数与随机颜色

随机数产生器RNG

RNG rng((unsigned)time(NULL));     //用这种方式是系统时间作为种子初始化rng,产生随机数是变的。注意加头文件#include<time.h>

rng.next()    取出下一个随机数

rng.uniform(a, b);   返回指定范围的随机数

rng.gaussian(σ);   函数返回一个均值为0的高斯随机数

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::random_drawing()
{
	Mat canvas = Mat::zeros(Size(512,512),CV_8UC3);
	int w = canvas.cols;
	int h = canvas.rows;

	RNG rng(12345);  //此类用于产生随机数  12345是产生随机数的种子
	while (true)
	{
		int c = waitKey(10);
		if (c == 27) //退出
		{
			break;
		}

		//产生随机数
		//第一个点的横纵坐标
		int x1 = rng.uniform(0,w); //uniform函数可以返回指定范围的随机数
		int y1 = rng.uniform(0,h);

		std::cout << x1 << std::endl;

		//第二个点的横纵坐标
		int x2 = rng.uniform(0, w);
		int y2 = rng.uniform(0, h);

		int b = rng.uniform(0, 255);
		int g = rng.uniform(0, 255);
		int r = rng.uniform(0, 255);

		canvas = Scalar(0, 0, 0); //加上这行代码可以每次都只绘制一个图形

		//随机绘制线段
		line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, 8, 0);

		//随机绘制矩形
		rectangle(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, 8, 0);
		imshow("随机绘制", canvas);
	}

}

16. 多边形填充与绘制

绘制多边形的函数  ---  polylines

函数原型:

void cv::polylines  (   InputOutputArray    img,
        InputArrayOfArrays      pts,
        bool    isClosed,
        const Scalar &      color,
        int     thickness = 1,
        int     lineType = LINE_8,
        int     shift = 0 
    )

 InputArrayOfArrays 的本源是 vector

参数说明
img作为画布的矩阵
pts折线顶点数组
isClosed是否是闭合折线(多边形)
color折线的颜色
thickness折线粗细
lineType线段类型
shift缩放比例(0是不缩放,4是1/4)

 填充多边形函数  ---   fillPoly

函数原型:

void fillPoly(InputOutputArray img, InputArrayOfArrays pts,
                           const Scalar& color, int lineType = LINE_8, int shift = 0,
                           Point offset = Point() );

img :输入图像

pts :折线顶点数组

color :填充的颜色。

lineType :直线类型,一共有三个:LINE_4,LINE_8,LINE_AA.其中LINE_AA是无锯齿直线。

shift :点坐标中的小数位数,一般用默认值0。

offset:轮廓所有点的可选偏移。

绘制轮廓  ---   drawContours,用于绘制找到的图像轮廓

void drawContours( InputOutputArray image, InputArrayOfArrays contours,
                              int contourIdx, const Scalar& color,
                              int thickness = 1, int lineType = LINE_8,
                              InputArray hierarchy = noArray(),
                              int maxLevel = INT_MAX, Point offset = Point() );

image  :要绘制轮廓的图像

contours :所有输入的轮廓,每个轮廓都是一组点集,每个轮廓被保存成一个point向量 ,可用 Point 类型的 vector 表示。

contourIdx :指定要绘制轮廓的编号,如果是负数,则绘制所有的轮廓

color :绘制轮廓所用的颜色

thickness = 1 :绘制轮廓的线的粗细,如果是负数,则轮廓内部被填充

lineType = 8 :线条的类型,有默认值 8。8 --- 8 连通线型,4 --- 4 连通线型,LINE_AA --- 抗锯齿线型

hierarchy() :关于层级的可选参数,只有绘制部分轮廓时才会用到,有默认值 noArray()。

maxLevel : 绘制轮廓的最高级别,这个参数只有hierarchy有效的时候才有效
                  //maxLevel=0,绘制与输入轮廓属于同一等级的所有轮廓即输入轮廓和与其相邻的轮廓
                 //maxLevel=1, 绘制与输入轮廓同一等级的所有轮廓与其子节点。
                //maxLevel=2,绘制与输入轮廓同一等级的所有轮廓与其子节点以及子节点的子节

offset :轮廓所有点的可选偏移。轮廓信息相对于目标图像对应点的偏移量,相当于在每一个轮廓点上加上该偏移量,有默认值 Point() 。在 ROI 区域(感兴趣区域)绘制轮廓时,这个参数便可派上用场。

函数:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::polyline_drawing_demo()
{
	Mat canvas = Mat::zeros(Size(512,512),CV_8UC3);
	Point p1(100, 100);
	Point p2(350, 100);
	Point p3(450, 280);
	Point p4(320, 450);
	Point p5(80, 400);

	std::vector<Point>pts;
	pts.push_back(p1);
	pts.push_back(p2);
	pts.push_back(p3);
	pts.push_back(p4);
	pts.push_back(p5);

	//绘制多边形的函数
	//polylines(canvas, pts, true, Scalar(0, 0, 255), 2, 8, 0);

	//填充多边形
	//fillPoly(canvas, pts, Scalar(255, 255, 0), 8, 0);

	//绘制轮廓的函数   (可以绘制并填充多边形)
	std::vector<std::vector<Point>>contours;
	contours.push_back(pts);
	drawContours(canvas, contours, -1, Scalar(255, 0, 0), -2);
	imshow("多边形绘制",canvas);

}

17. 鼠标操作与响应

平时在写代码时可能用到鼠标在图上做标记等其他作用,opencv主要用setMouseCallback()这个函数。

setMouseCallback()  ---  创建了一个鼠标回调函数

函数原型:

void setMousecallback(const string& winname, MouseCallback onMouse, void* userdata=0)
   

参数解释:
winname : 窗口的名字

onMouse : 鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。 这个函数的原型应该为void on_Mouse(int event, int x, int y, int flags, void* userdate)

userdate :传给回调函数的参数

MouseCallback onMouse的函数原型:

void on_Mouse(int event, int x, int y, int flags, void* param);

参数解释:

event是 CV_EVENT_*变量之一,event事件代表了鼠标的各种操作,详细看一下各个event事件:

Event:
#define CV_EVENT_MOUSEMOVE 0             //鼠标移动
#define CV_EVENT_LBUTTONDOWN 1           //左键点击
#define CV_EVENT_RBUTTONDOWN 2           //右键点击
#define CV_EVENT_MBUTTONDOWN 3           //中键点击
#define CV_EVENT_LBUTTONUP 4             //左键放开
#define CV_EVENT_RBUTTONUP 5             //右键放开
#define CV_EVENT_MBUTTONUP 6             //中键放开
#define CV_EVENT_LBUTTONDBLCLK 7         //左键双击
#define CV_EVENT_RBUTTONDBLCLK 8         //右键双击
#define CV_EVENT_MBUTTONDBLCLK 9         //中键双击
#define CV_EVENT_MOUSEWHEEL   10         //滚轮滚动

int x,int y,代表鼠标位于窗口的(x,y)坐标位置,即Point(x,y);

int flags,代表鼠标的拖拽事件,以及键盘鼠标联合事件,共有32种事件:

flags:
#define CV_EVENT_FLAG_LBUTTON 1       //左鍵拖曳
#define CV_EVENT_FLAG_RBUTTON 2       //右鍵拖曳
#define CV_EVENT_FLAG_MBUTTON 4       //中鍵拖曳
#define CV_EVENT_FLAG_CTRLKEY 8       //(8~15)按Ctrl不放事件
#define CV_EVENT_FLAG_SHIFTKEY 16     //(16~31)按Shift不放事件
#define CV_EVENT_FLAG_ALTKEY 32       //(32~39)按Alt不放事件

param 是用户定义的传递到setMouseCallback函数调用的参数。

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

//初始化
Point sp(-1, -1); //开始的位置
Point ep(-1, -1); //结束的位置
Mat temp;

static void on_draw(int event, int x, int y, int flags, void* userdata)  //回调函数 int x,int y,代表鼠标位于窗口的(x,y)坐标位置,即Point(x,y);
{
	Mat image = *((Mat*)userdata); 

	if (event == EVENT_LBUTTONDOWN)  //左键点击
	{
		sp.x = x;
		sp.y = y;

		std::cout << "start point:" << sp << std::endl;
	}
	else if (event == EVENT_LBUTTONUP)  //左键放开
	{
		ep.x = x;
		ep.y = y;

		int dx = ep.x - sp.x;  //矩形的宽
		int dy = ep.y - sp.y;  //矩形的高
		if (dx > 0 && dy > 0)
		{
			Rect box(sp.x, sp.y, dx, dy);  //绘制矩形
			imshow("ROI区域", image(box)); //截图图像某一区域
			rectangle(image, box, Scalar(255, 0, 0), 2, 8, 0);
			imshow("鼠标绘制", image);
			
			//为下一次绘制做准备
			sp.x = -1;
			sp.y = -1;
		}
	}
	else if (event == EVENT_MOUSEMOVE)  //滑动
	{
		if (sp.x > 0 && sp.y > 0)
		{
			ep.x = x;
			ep.y = y;

			int dx = ep.x - sp.x;  //矩形的宽
			int dy = ep.y - sp.y;  //矩形的高
			if (dx > 0 && dy > 0)
			{
				Rect box(sp.x, sp.y, dx, dy);  //绘制矩形
				temp.copyTo(image);  //将temp的图拷贝给image
				rectangle(image, box, Scalar(255, 0, 0), 2, 8, 0);
				imshow("鼠标绘制", image);
				
			}
		}
	}
}

void QuickDemo::mouse_drawing_demo(Mat &image)
{
	namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
	setMouseCallback("鼠标绘制", on_draw, (void*)&image); //(void*)&image是传给回调函数的参数
	imshow("鼠标绘制", image);
	temp = image.clone();
}

18. 图像像素类型转换与归一化

归一化函数 --- normalize()

函数作用:

归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。

该函数分为范围归一化数据值归一化。

函数原型:

void cv::normalize(InputArry src,InputOutputArray dst,double alpha=1,
double beta=0,int norm_type=NORM_L2,int dtype=-1,InputArray mark=noArry())

参数解释:

src               输入数组;

dst               输出数组。除非使用dtype参数,否则输出数组的尺寸和数据类型与输入数组一致;

alpha           alpha = 1,用来规范值,alpha = 2.规范范围;并且是下限;

beta             只用来规范范围,并且是上限;在范数归一化时不会使用。

norm_type   归一化选择的数学公式类型;

dtype           当为负,输出在大小深度通道数都等于输入,当为正,输出只在深度与输入不同,不同的地方由dtype决定;默认类型与src一致

mark            掩码。选择感兴趣区域,选定后只能对该区域进行操作。默认值为空

归一化选择的数学公式类型:

设数组中原有{A1,A2,A3...An}
NORM_L1:                                     

                                                                 

NORM_INF:

                                                                

NORM_L2:

                                                                
NORM_MINMAX:(AK不属于{max(Ai)},min(Ai),当AK等于max(Ai)时p=1,等于min(Ai)时p=0)

                                                         

举例:
src={10,23,71}

NORM_L1运算后得到    dst={0.096,0.221,0.683}

NORM_INF运算后得到  dst={0.141,0.324,1}

NORM_L2运算后得到   dst={0.133,0.307,0.947}

NORM_MINMAX运算得到 dst={0,0.377,1}

范围归一化与值归一化的区别:

区别一:范围归一化使用的是如下式子,设范围为【0,255】

                                             
即把src缩放到【0,255】这个范围内,并不使用上面的4个公式去解。

区别二:使用范围归一化时,beta必有值不等于0


Mat矩阵类型转换函数 --- convertTo()

函数功能:

把一个矩阵从一种数据类型转换到另一种数据类型,同时可以带上缩放因子和增量。

函数原型:

void Mat::convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )

参数解释:
m – 目标矩阵。如果m在运算前没有合适的尺寸或类型,将被重新分配。

rtype – 目标矩阵的类型。因为目标矩阵的通道数与源矩阵一样,所以rtype也可以看做是目标矩阵的位深度。如果rtype为负值,目标矩阵和源矩阵将使用同样的类型。

alpha – 尺度变换因子(可选)。

beta – 附加到尺度变换后的值上的偏移量(可选)

CV_8U是 unsign 的8位像素,即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围。

CV_32F是 float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示。

CV_32S是每个像素的带符号的32位整数值-对像素进行整数数学运算同样有用,但再次需要转换为8位以保存或显示。

CV_8U: 1-byte unsigned integer (unsigned char).

CV_32S: 4-byte signed integer (int).

CV_32F: 4-byte floating point (float)

示例:
 

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::norm_demo(Mat &image)
{
	Mat dst;
	std::cout << image.type() << std::endl;  //图像最初的类型
	std::cout << image.dims<< std::endl;

	// convertTo()函数负责转换数据类型不同的Mat
	image.convertTo(image,CV_32FC3);  //这行代码达到的效果是将CV_8UC3转为CV_32FC3,像素数据转为浮点型数据

	//CV_8U的灰度或BGR图像的颜色分量都在0~255之间。直接imshow可以显示图像。 CV_32F或者CV_64F取值范围为0~1.0,imshow的时候会把图像乘以255后再显示。
	// CV_8U是 unsign 的8位像素,即一个像素的值在0-255区间,这是大多数图像和视频格式的正常范围。

	// CV_32F是 float - 像素是在0 - 1.0之间的任意值,这对于一些数据集的计算很有用,
	// 但是它必须通过将每个像素乘以255来转换成8位来保存或显示。

	// CV_32S是每个像素的带符号的32位整数值

	std::cout << image.type() << std::endl;  //图像转换完后的类型


	normalize(image, dst, 1.0, 0, NORM_MINMAX);  //归一化
	std::cout << dst.type() << std::endl;  //图像归一化后的类型

	imshow("图像数据归一化",dst);


}

19. 图像放缩与插值

图像缩放函数 --- resize()

函数功能:调整图像的大小

函数原型:

void resize(InputArray src, OutputArray dst, Size dsize, 
double fx=0, double fy=0, int interpolation=INTER_LINEAR)  

参数解释:

src  :  原图

dst  :  目标图像。当参数dsize不为0时,dst的大小为size;否则,它的大小需要根据src的大小,参数fx和fy决定。dst的类型(type)和src图像相同

dsize  :  目标图像大小。当dsize为0时,它可以通过以下公式计算得出:

                所以,参数dsize和参数(fx, fy)不能够同时为0

fx  :  水平轴上的比例因子。当它为0时,计算公式如下:

                                    

fy  :  垂直轴上的比例因子。当它为0时,计算公式如下:

interpolation  :  插值方法。共有5种:

1)INTER_NEAREST - 最近邻插值法

2)INTER_LINEAR - 双线性插值法(默认)

3)INTER_AREA - 基于局部像素的重采样(区域插值)(resampling using pixel area relation)。对于图像抽取(image decimation)来说,这可能是一个更好的方法。但如果是放大图像时,它和最近邻法的效果类似。

4)INTER_CUBIC - 基于4x4像素邻域的3次插值法

5)INTER_LANCZOS4 - 基于8x8像素邻域的Lanczos插值

一般来说要缩小图像用区域插值(INTER_AREA);要放大图像一般用三次样条插值(INTER_CUBIC)或者线性插值(INTER_LINEAR);

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>


static void on_Mouse(int event, int x, int y, int flags, void* userdata)
{
	
	// 获得图像
	Mat image = *(Mat*)userdata;
	Mat imgzoom;
	int value;
	int h = image.rows;
	int w = image.cols;
	

	// 判断鼠标事件
	if (event == EVENT_MOUSEWHEEL)  //鼠标滚轮滚动
	{
		// 获取鼠标滚轮的方向
		value = getMouseWheelDelta(flags);
		if (value > 0)   // 向上
		{
			// 放大
			std::cout << "h:" << h << "  w:" << w << "滚轮向上";
			resize(image, imgzoom, Size(h * 2, w * 2), 0, 0, INTER_LINEAR);
			imshow("鼠标操控图像缩放", imgzoom);
			h = h * 2;
			w = w * 2;
		}
		else if (value < 0)  // 向下
		{
			std::cout << "h:" << h << "  w:" << w << "滚轮向下";
			//缩小
			resize(image, imgzoom, Size(h / 2, w / 2), 0, 0, INTER_LINEAR);
			imshow("鼠标操控图像缩放", imgzoom);
			h = h / 2;
			w = w / 2;
		}
		//if (!image.empty())
		//{
		//	// 显示
		//	imshow("鼠标操控图像缩放", imgzoom);
		//}
	}
}

void QuickDemo::resize_demo(Mat &image)
{
	//对图像进行缩放

	//方法1:直接用resize函数进行图像的缩放

	//Mat zoomin, zoomout;
	//int h = image.rows;
	//int w = image.cols;

	对图像进行缩放
	//resize(image, zoomin, Size(w / 2, h / 2), 0, 0, INTER_LINEAR); 
	//imshow("zoomin",zoomin);

	//resize(image, zoomout, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);
	//imshow("zoomout", zoomout);

	//方法2:通过鼠标滚轮上下滑动来操控图像的放大与缩小
	
	namedWindow("鼠标操控图像缩放", WINDOW_AUTOSIZE);
	setMouseCallback("鼠标操控图像缩放", on_Mouse, (void*)&image); //(void*)&image是传给回调函数的参数
}

20. 图像翻转

(图像)镜像(翻转)函数  ---  flip()

函数原型:

void flip(InputArray src, OutputArray dst, int flipCode)

参数解释:

src :输入矩阵

dst :翻转后矩阵,类型与src一致

flipCode :翻转模式。flipCode==0垂直翻转(沿X轴翻转),flipCode>0水平翻转(沿Y轴翻转),flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>


void QuickDemo::flip_demo(Mat &image)
{
	Mat dst; 
	flip(image, dst, 0);  //flipCode==0垂直翻转(沿X轴翻转)
	imshow("图像翻转",dst);

	flip(image, dst, 1);  //flipCode>0水平翻转(沿Y轴翻转)
	imshow("图像翻转", dst);

	flip(image, dst, -1);  //flipCode<0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
	imshow("图像翻转", dst);
}

21. 图像旋转

图像仿射变换 --- warpAffine()函数

利用opencv实现仿射变换一般会涉及到warpAffine和getRotationMatrix2D两个函数,其中warpAffine可以实现一些简单的重映射,而getRotationMatrix2D可以获得旋转矩阵。

warpAffine函数:

void cv::warpAffine     (   InputArray      src,
        OutputArray     dst,
        InputArray      M,
        Size    dsize,
        int     flags = INTER_LINEAR,
        int     borderMode = BORDER_CONSTANT,
        const Scalar &      borderValue = Scalar() 
    )

src: 输入图像

dst: 输出图像,尺寸由dsize指定,图像类型与原图像一致

M: 2X3的变换矩阵

dsize: 指定图像输出尺寸

flags: 插值算法标识符,有默认值INTER_LINEAR,如果插值算法为WARP_INVERSE_MAP, warpAffine函数使用如下矩阵进行图像转换

这里写图片描述

常用的插值算法如下: 

 这里写图片描述

borderMode: 边界像素模式,有默认值BORDER_CONSTANT

borderValue: 边界取值,有默认值Scalar(),即0,为黑色。

getRotationMatrix2D函数

Mat cv::getRotationMatrix2D     (   Point2f     center,
        double      angle,
        double      scale 
    )   

center :  Point2f类型,表示原图像的旋转中心

angle  :  double类型,表示图像旋转角度,角度为正则表示逆时针旋转,角度为负表示逆时针旋转(坐标原点是图像左上角)

scale  :  缩放系数。为0.5表示我们缩小一半,不需要就1.0。

基于旋转测算出旋转后真正的大小,利用下图可以得出

要保证求出来的cos、sin是正数,用abs()求绝对值即可

int nw = cos * w + sin * h;
int nh = sin * w + cos * h;

 

M矩阵的第三列是矩阵平移的时候原来中心点在(0,0),现在你平移到哪边去,要通过这两个参数来声明,如图M13 和M23 

 示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>


void QuickDemo::rotate_demo(Mat &image)
{
	Mat dst, M;
	int w = image.cols;
	int h = image.rows;

	M = getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0); //获得M矩阵 第一个参数为旋转中心

    //要保证求出来的cos、sin是正数,用abs()求绝对值即可
	double cos = abs(M.at<double>(0, 0));  ///0,0为矩阵中的cos
	double sin = abs(M.at<double>(0, 1)); //0,1为矩阵中的sin

	//计算旋转后新的高度
	int nw = cos * w + sin * h;
	int nh = h * cos + w * sin;

	//平移量
	//M.at<double>(0, 2) 为M矩阵第一行最后一列的值,即新的宽度减去原来的宽度的差值
	M.at<double>(0, 2) += (nw / 2 - w / 2);
	M.at<double>(1, 2) += (nh / 2 - h / 2);//计算M矩阵第二行最后一列的值
	
	warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 0, 0)); //新的图像:测算出旋转后真正的大小
	//warpAffine(image, dst, M, image.size(), INTER_LINEAR, 0, Scalar(255, 0, 0)); //图像旋转

	imshow("旋转演示", dst);

}

 总结:由getRotationMatrix2D函数得到M矩阵,如果直接通过函数warpAffine()进行图像的旋转可能会导致图像显示不完全,所以,在得到M矩阵后,可以根据M矩阵获得旋转后图像新的高和宽,然后计算出偏移量,得到新的带偏移量的M矩阵,最后函数warpAffine()中的参数dsize就可以是旋转后新的高和宽,这样就可以将图像完全显示出来。

22. 视频文件摄像头使用

 在opencv中关于视频的读操作是通过VideoCapture类来完成的;关于视频的写操作是通过VideoWriter类来实现的。

VideoCapture---视频的获取操作

VideoCapture既支持从视频文件(.avi ,.mpg格式)读取,也支持直接从摄像机(比如电脑自带摄像头)中读取。

要想获取视频需要先创建一个VideoCapture对象,VideoCapture对象的创建方式有以下三种:

【方式一】从文件(.MPG或.AVI格式)中读取视频,对象创建以后,OpenCV将会打开文件并做好准备读取它,如果打开成功,我们将可以开始读取视频的帧,并且cv::VideoCapture的成员函数isOpened()将会返回true(建议在打开视频或摄像头时都使用该成员函数判断是否打开成功)。

方法:  cv::VideoCapture capture(const string& filename);  // 从视频文件读取 
例程:  cv::VideoCapture capture("C:/Users/DADA/DATA/gogo.avi");  // 从视频文件读取 

【方式二】从摄像机中读取视频。这种情况下,我们会给出一个标识符,用于表示我们想要访问的摄像机,及其与操作系统的握手方式。对于摄像机而言,这个标志符就是一个标志数字——如果只有1个摄像机,那么就是0,如果系统中有多个摄像机,那么只要将其向上增加即可。标识符另外一部分是摄像机域(camera domain),表明我们使用的是何种相机,这个域值可以是下面任一预定义常量。

cv::VideoCapture capture(int device );  //视频捕捉设备 id ---笔记本电脑的用0表示 

 以这种方式创建视频捕获对象时,我们所传递的标识符是域索引和摄像机索引的和。

例如:

cv::VideoCapture capture(cv::CAP_IEEE1394 + 1);  

 这个例子中VideoCapture将尝试打开第2个(编号从0开始)1394摄像机。多数情况下,由于我们只有一个摄像机,因此没必要指定摄像机的域,此时使用cv::CAP_ANY是一种高效的方式(也即是0,所以不用特意指定)。

【方式三】先创建一个捕获对象,然后通过成员函数open()来设定打开的信息,操作如下。

cv::VideoCapture VideoCapture;  这里的第二个VideoCapture是一个对象名
VideoCapture.open( "C:/Users/DADA/DATA/gogo.avi" );  

将视频帧读取到cv::Mat矩阵中,有两种方式:一种是read()操作;另一种是 “>>”操作。

cv::Mat frame;  
cap.read(frame); //读取方式一  
cap >> frame; //读取方式二  

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>


void QuickDemo::video_demo(Mat &image)
{
	//创建对象
	VideoCapture capture(0);  //从摄像机中读取视频        0表示从笔记本的摄像机中读取视频
    //VideoCapture capture("shipin.mpg");  // 从文件(.MPG或.AVI格式)中读取视频

	Mat frame;
	while (true)
	{
		//将视频帧读取到cv::Mat矩阵中,有两种方式:一种是read()操作;另一种是 “ >> ”操作。
		//capture.read(frame); //读取图像  方式一
		capture >> frame;      //读取图像  方式二

		flip(frame, frame, 1);  //镜像翻转

		//图像色彩空间转换
		Mat gray, hsv;
		cvtColor(frame, hsv, COLOR_BGR2HSV); //H  0-180;S ,V 0-255; H和S表示颜色通道,V表示亮度
		cvtColor(frame, gray, COLOR_BGR2GRAY); //等价于 cvtColor(image, gray, 6)

		if (frame.empty())
		{
			break;
		}
		int c = waitKey(10);
		if (c == 27)
		{
			break;
		}

		//release
		imshow("frame", frame);  //显示
		imshow("HVS", hsv);
		imshow("GRAY", gray);
		
	}
	capture.release();//释放视频对象
}

23. 视频处理与保存

VideoWriter  ---  视频的写操作

VideoWriter(const string& filename, int fourcc, double fps,
Size frameSize, bool isColor=true);

filename    输出视频文件名。
fourcc       代表了所使用的编码方式。可以通过capture.get(CAP_PROP_FOURCC)获得。                              capture为读取视频类的定义的对象。

fps            被创建视频流的帧率。
frame_size      保存视频的宽和高。
isColor       如果非零,编码器将希望得到彩色帧并进行编码;否则,是灰度帧(只有在Windows下支持这个标志)。

注:OpenCV生成的视频文件不能大于2GB,而且不能添加音频。

示例:
 

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>


void QuickDemo::video_demo(Mat &image)
{
	//创建视频读取的对象
	//VideoCapture capture(0);  //从摄像机中读取视频        0表示从笔记本的摄像机中读取视
    VideoCapture capture("shipin.mpg");  // 从文件(.MPG或.AVI格式)中读取视频

	int frame_width = capture.get(CAP_PROP_FRAME_WIDTH); //获取视频的宽
	int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT); //获取视频的高

	int count = capture.get(CAP_PROP_FRAME_COUNT);  //获取视频文件总的帧数
	double fps = capture.get(CAP_PROP_FPS);  //fps指每秒可以处理多少帧

	std::cout << "frame.width:" << frame_width << std::endl;
	std::cout << "frame.height:" << frame_height << std::endl;
	std::cout << "frame.count:" << count << std::endl;
	std::cout << "fps:" << fps << std::endl;

	// 创建保存视频的对象
	VideoWriter writer("test.mp4",capture.get(CAP_PROP_FOURCC),fps,Size(frame_width,frame_height),true);

	Mat frame;
	while (true)
	{
		//将视频帧读取到cv::Mat矩阵中,有两种方式:一种是read()操作;另一种是 “ >> ”操作。
		//capture.read(frame); //读取图像  方式一
		capture >> frame;      //读取图像  方式二

		flip(frame, frame, 1);  //镜像翻转

		if (frame.empty())
		{
			break;
		}
		int c = waitKey(100);
		if (c == 27)
		{
			break;
		}

		imshow("frame", frame);  //显示
		writer.write(frame);

	}
	capture.release();//释放视频对象
	writer.release();
}

24. 图像直方图

计算图像直方图 ---  calcHist() 函数

图像的直方图,用来观察图像的主要特征颜色

简单的计算数组集(通常是图像或分割后的通道)的直方图函数 calcHist

函数原型:

void calcHist(const Mat* images, int nimages, const int* channels, 
InputArray mask, OutputArray hist, int dims, 
const int* histSize, const float** ranges,
 bool uniform=true, bool accumulate=false )

参数解释:

const Mat* images:输入图像

 int nimages:输入图像的个数

const int* channels:传入图像的通道。如果是灰度图像,那就不用说了,只有一个通道,值为0,如果是彩色图像(有3个通道),那么值为0,1,2,中选择一个,对应着BGR各个通道。这个值也得用[ ]传入。

InputArray mask:掩码( 0 表示忽略该像素), 如果未定义,则不使用掩码

OutputArray hist : 储存直方图的矩阵

int dims:需要统计直方图通道的个数 (直方图维数)

const int* histSize:指的是直方图分成多少个区间,就是 bin的个数

const float** ranges: 每个维度的取值范围

bool uniform=true : 是否对得到的直方图数组进行归一化处理

bool accumulate=false:在多个图像时,是否累计计算像素值得个数

在画直方图之前,先使用 normalize 归一化直方图,这样直方图bin中的值就被缩放到指定范围。

函数calcHist()的channels参数的理解:
直方图统计都是针对单通道图片的,所以如果出现多通道的图片,那就对其单个通道分别依次统计,如果是多幅图片,那就把所有图片的所有通道按顺序编号(从0开始),然后channels就存储相应的通道序号就可以指定计算范围了。
如果有四张图片:img1,img2,img3,img4,通道数分别是3,1,2,2,所以一字排开总共会有8张单通道图片,按存储先后顺序编号为0~7.
所以channels[ ]取相应数字的集合就可以了。

cvRound(), cvFloor(), cvCeil()函数:

函数cvRound,cvFloor,cvCeil 都是用一种舍入的方法将输入浮点数转换成整数:

cvRound():返回跟参数最接近的整数值,即四舍五入;

cvFloor():返回不大于参数的最大整数值,即向下取整;

cvCeil():返回不小于参数的最小整数值,即向上取整;

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

//绘制一维的直方图
void QuickDemo::histogram_demo(Mat &image)
{
	//步骤一:三通道分离
	std::vector<Mat>bgr_plane;
	split(image, bgr_plane);

	//步骤二:计算blue, green, red通道的直方图
	//定义参数变量
	int channels[1] = { 0 };
	const int bins[1] = { 256 };  //总共256个灰度级别  指的是直方图分成多少个区间
	float hranges[] = { 0,255 };  
	const float*  ranges[1] = { hranges }; //每个通道的取值是0 - 255

	Mat b_hist;
	Mat g_hist;
	Mat r_hist;

	calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);
	calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
	calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);

	int hist_w = 512;
	int hist_h = 400;
	int bin_w = cvRound((double)hist_w / bins[0]);  //直方图的等级  一个等级的宽度
	Mat histImage = Mat::zeros(Size(hist_h, hist_w), CV_8UC3); //创建画布
	
	//归一化直方图数据
	normalize(b_hist, b_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(g_hist, g_hist, 0, hist_h, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, hist_h, NORM_MINMAX, -1, Mat());

	//步骤三:绘制直方图曲线
	for (int i = 1; i < bins[0]; i++) //bins[0] = 256
	{
		line(histImage, Point((i - 1) * bin_w , hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point((i) * bin_w , hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);

		//hist_h - cvRound(b_hist.at<float>(i - 1)) 用hist_h减去的原因是因为屏幕坐标原点是在左上角

		line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point((i)* bin_w, hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 8, 0);

		line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point((i)* bin_w, hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 8, 0);
	}

	//显示直方图
	namedWindow("Histogram Demo", WINDOW_AUTOSIZE);
	imshow("Histogram Demo",histImage);
}

25. 二维直方图

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

//绘制二维的直方图(两个通道)
void QuickDemo::histogram_2d_demo(Mat &image)
{
	Mat hsv, hs_hist;

	//步骤一:空间颜色转换 
	//将BGR转为HSV
	cvtColor(image, hsv, COLOR_BGR2HSV);  //H通道的取值范围是0 - 180,S通道的取值范围是0 - 255

	//步骤二:
	//定义参数变量 
	//H通道分为30个区间,S通道分为32个区间
	int hbins = 30, sbins = 32;
	int hist_bins[] = { hbins ,sbins };

	//H和S通道的取值范围
	float h_range[] = { 0,180 };
	float s_range[] = { 0,256 };
	const float* hs_ranges[] = { h_range ,s_range };

	//通道数
	int hs_channels[] = { 0, 1 };

	// 计算二维直方图
	calcHist(&image, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false);
	//calcHist  函数调用结束后, hs_hist变量中将储存了直方图的信息  用 hs_hist的模版函数 at<Type>(i)得到第i个柱条的值
	//at<Type>(i, j)得到第i个并且第j个柱条的值

	//因为任何一个图像的某个像素的总个数,都有可能会有很多,会超出所定义的图像的尺寸,针对这种情况,先对个数进行范围的限制
	//先用 minMaxLoc函数来得到计算直方图后的像素的最大个数
	//步骤三:寻找最大值
	double maxVal = 0; //最大值
	minMaxLoc(hs_hist, 0, &maxVal, 0, 0);  //minMaxLoc查找数组和子数组的全局最小值和最大值存入maxValue中
	int scale = 10;
	Mat hist2d_image = Mat::zeros(Size(sbins * scale, hbins * scale), CV_8UC3); //创建画布

   //步骤四:绘制直方图曲线
	for (int h = 0; h < hbins; h++)
	{
		for (int s = 0; s < sbins; s++)
		{
			float binVal = hs_hist.at<float>(h, s); //直方图直条的值
			int intensity = cvRound(binVal * 255 / maxVal); //将直方图中的值归一化到0到255
			画矩形柱状图,Point的坐标中x值对应着h的维度,y值对应值s的维度,这与画布矩阵必须一致
			rectangle(hist2d_image, Point(h*scale, s*scale), Point((h + 1)*scale - 1,
				(s + 1)*scale - 1), Scalar::all(intensity), -1); //绘制
		}
	}
	applyColorMap(hist2d_image, hist2d_image,COLORMAP_CIVIDIS);

	imshow("H-S Histogram", hist2d_image);
	imwrite("hist_2d.png", hist2d_image);
}

26. 直方图均衡化

直方图均衡化是图像处理领域中利用图像直方图对对比度进行调整的方法。

直方图均衡化函数 ---  equalizeHist()

这个函数的输入图片仅仅是一副灰度图像,输出结果是直方图均衡化之后的图像。

该函数只支持单通道的灰度图均衡化,所以对于彩色图像来说可以先将多通道分离成单通道,然后再合并成多通道。

函数原型:

void cv::equalizeHist ( InputArray src,
OutputArray dst
)

src : 输入图像

dst:输出图像

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::histogram_eq_demo(Mat &image)
{
	//灰度图像均衡化
	//Mat gray;
	//cvtColor(image, gray, COLOR_BGR2GRAY);
	//imshow("灰度图像", gray);
	//Mat dst;
	//equalizeHist(gray, dst); 

	//imshow("直方图均衡化演示", dst);

	//彩色图像均衡化
	//思路:先将彩色图像通道分离,对每个通道分别进行均衡化,然后将通道合并,最后显示出均衡化后的图像
	Mat dst;
	std::vector<Mat>imageRGB;
	split(image, imageRGB);
	for (int i = 0; i < 3; i++)
	{
		equalizeHist(imageRGB[i], imageRGB[i]);
	}
	merge(imageRGB, dst);
	imshow("直方图均衡化演示", dst);
}

27. 图像卷积操作

blur() --- 模糊函数

作用:对输入的图像src进行均值滤波后用dst输出。

函数原型:

void blur(InputArray src, OutputArray dst, Size ksize, 
Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )

参数解释:

src:输入图像,即源图像,填Mat类的对象即可。该函数对通道是独立处理的,且可以处理任意通道数的图片,但需要注意,待处理的图片深度应该为CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F之一。

dst:即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。

ksize:卷积核。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小,默认系数全是1,就是均值卷积。

anchor,表示锚点(即被平滑的那个点),注意它有默认值Point(-1,-1)。如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。

borderType,图像边缘处理方式。有默认值BORDER_DEFAULT,我们一般不去管它。

注:

卷积核10x1 就是在水平方向上的一维卷积

卷积核1x10 就是在竖直方向上的一维卷积

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::blur_demo(Mat &image)
{
	Mat dst;
	blur(image, dst, Size(13, 13), Point(-1, -1)); //卷积核是13 * 13的,卷积核越大,图像越模糊
	imshow("图像模糊", dst);

}

28. 高斯模糊

GaussianBlur() --- 高斯模糊

函数原型:

void GaussianBlur(InputArray src, OutputArray dst,
 Size ksize, double sigmaX, double sigmaY=0, 
int borderType=BORDER_DEFAULT )

参数解释:
InputArray src: 输入图像,可以是Mat类型,图像深度为CV_8U、CV_16U、CV_16S、CV_32F、CV_64F。

OutputArray dst: 输出图像,与输入图像有相同的类型和尺寸。

Size ksize: 高斯内核大小。ksize.width和ksize.height可以不相同但是这两个值必须为正奇数,如果这两个值为0,他们的值将由sigma计算。

double sigmaX: 高斯核函数在X方向上的标准偏差。

double sigmaY: 高斯核函数在Y方向上的标准偏差,如果sigmaY是0,则函数会自动将sigmaY的值设置为与sigmaX相同的值,如果sigmaX和sigmaY都是0,这两个值将由ksize.width和ksize.height计算而来。具体可以参考getGaussianKernel()函数查看具体细节。建议将size、sigmaX和sigmaY都指定出来。

int borderType=BORDER_DEFAULT: 推断图像外部像素的某种便捷模式,有默认值BORDER_DEFAULT,如果没有特殊需要不用更改,具体可以参考borderInterpolate()函数。

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::gaussian_blur_demo(Mat &image)
{
	Mat dst;
	GaussianBlur(image, dst, Size(0, 0),15);
	imshow("高斯模糊", dst);

}

29. 高斯双边模糊

bilateralFilter() ---  双边模糊的函数

双边模糊可以去除无关噪声,同时保持较好的边缘信息。 但是,其速度比绝大多数滤波器都慢。

函数原型:

void bilateralFilter( InputArray src,         //(原始图像:8-bit或floating-point,1-channel或3-channel)
                      OutputArray dst,        // 目标图像:size和type与原始图像相同
                      int d,                  // 过滤期间使用的各像素邻域的直径
                      double sigmaColor,     // 色彩空间的sigma参数,该参数较大时,各像素邻域内相距较远的颜色会被混合到一起,从而造成更大范围的半相等颜色
                      double sigmaSpace,    // 坐标空间的sigma参数,该参数较大时,只要颜色相近,越远的像素会相互影响
                      int borderType = BORDER_DEFAULT // 边界类型:指定如何确定图像范围外的像素的取值(在处理边缘像素时)
                    );

关于2个sigma参数
简单起见,可以令2个sigma的值相等;
如果他们很小(小于10),那么滤波器几乎没有什么效果;
如果他们很大(大于150),那么滤波器的效果会很强,使图像显得非常卡通化;

关于参数d
过大的滤波器(d>5)执行效率低。
对于实时应用,建议取 d=5;
对于需要过滤严重噪声的离线应用,可取 d=9;
d>0 时,由 d 指定邻域直径;
d<=0 时,d 会自动由 sigmaSpace 的值确定,且 d 与 sigmaSpace 成正比。

示例:

#pragma once  //为了避免同一个头文件被包含(include)多次
#include<quickopencv.h>

void QuickDemo::bifilter_demo(Mat &image)
{
	
	Mat dst;
	bilateralFilter(image, dst, 0, 100, 10);
//	namedWindow("高斯双边模糊", WINDOW_NORMAL);
	imshow("高斯双边模糊", dst);

}

30. 人脸检测(练习)

核心函数介绍

主要涉及函数有以下几个:

readNetFromCaffe :使用“读取”方法从磁盘直接加载序列化模型:

blobFromImage:在dnn中从磁盘加载图片:

setInput

forward

blobFormImage

函数作用:对图像进行预处理,包括:整体像素值减去平均值(mean),通过缩放系数(scalefactor)对图片像素值进行缩放,裁剪,交换通道等。

函数原型:

Mat cv::dnn::blobFromImage(
    InputArray     image,
    double     scalefactor = 1.0,
    const Size &     size = Size(),
    const Scalar &     mean = Scalar(),
    bool     swapRB = false,
    bool     crop = false,
    int     ddepth = CV_32F ) 

函数输出:函数返回一个 4D数组( [N,C,H,W] dimensions.)

参数解释:

image:输入图像

scalefactor: 当我们将图片减去平均值之后,还可以对剩下的像素值进行一定的尺度缩放,它的默认值是1。如果希望将减去平均像素之后的值,全部缩小一半,那么可以将scalefactor设为1/2。

size:指的不是输入图像的尺寸,是指所需要的尺寸,也就是返回的Mat中数据的尺寸

mean:需要将图片整体减去的平均值。如果我们需要对RGB图片的三个通道分别减去不同的值,那么可以使用3组平均值,如果只使用一组,那么就默认对三个通道减去一样的值。减去平均值(mean):为了消除同一场景下不同光照的图片,对我们最终的分类或者神经网络的影响,我们常常对图片的R、G、B通道的像素求一个平均值,然后将每个像素值减去我们的平均值,这样就可以得到像素之间的相对值,就可以排除光照的影响。(e.g. image为bgr3通道的图像,mean=[104.0, 177.0, 123.0],表示b通道的值-104,g通道的值-177,r通道的值-123)

swapRB:是否交换R和B分量。如果图片输入的颜色格式为RGB,则赋值为true,否则为false。

crop:输入图像大小与size不符的时候,是否需要裁剪。如果裁剪为真,则调整输入图像的大小,使调整大小后的一侧等于相应的尺寸大小,另一侧等于或大于大小。 然后,执行从中心的裁剪。 如果裁剪为假,则执行直接调整大小而不裁剪并保留纵横比。

ddepth:图像的数据类型,目前仅支持32F和8U。

注意:

1. 当同时进行scalefactor, size, mean, swapRB操作时,优先按swapRB交换通道,其次按scalefactor比例缩放,然后按mean求减,最后按size进行resize操作

2. 当进行减均值操作时,ddepth不能选取CV_8U,否则报错:

3. 当crop=True时,先等比例缩放,直至宽高尺寸一个等于对应的size尺寸,另一个大于或者等于对应的size尺寸,然后再从中心进行裁剪。

setInput函数

函数原型:

void cv::dnn::Net::setInput    (    
    InputArray     blob,
    const String &     name = "",
    double     scalefactor = 1.0,
    const Scalar &     mean = Scalar() )  

参数解释:

blob:一个新的blob。 应该有CV_32F或CV_8U深度。就是blobFromImage函数的返回值

name:输入图层的名称。

scalefactor:可选的标准化比例。

mean:一个可选的平均减法值。

forward函数:

前向运行计算输出层,输出层名称为outputName。

Mat forward(const String& outputName = String());

outputName:需要输出的图层的名称
返回:指定图层outputName的第一个输出的blob。默认情况下,为整个网络运行正向传递。

注意:返回Mat类型,这是一个4D数,rows and cols can only hold 2 dimensions, so they are not used here, and set to -1

调用:

Mat detection = net.forward("detection_out"); //compute output, [Make forward pass]

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值