Opencv学习笔记

前置-C++部分常用知识

1.打印与输出-采用C++的标准输出std::cout,代码演示如下:

// 输出与打印字符串与数字
std::cout << "Hello World, OpenCV" << cv::getVersionMajor()<<"."<<cv::getVersionMinor()<<std::endl;
// 打印Mat矩阵
cv::Mat m1 = cv::Mat(3, 3, CV_8UC1);
std::cout <<"m1=" <<m1 << std::endl;

 结果如上所示

2.使用auto关键字,避免过长类型变量声明

C++11支持auto关键字推导,这个可以避免很多不必要的书写,提供代码编写效率。注:auto不能作为函数参数,代码演示如下:

// 使用auto关键字,实现自动类型推导与识别
auto t1 = 3; // 推导为int类型
auto t2 = 3.0; // 推导为double
// 推导为cv::Mat类型
auto m2 = cv::Mat(3, 3, CV_8UC1);
// 直接用auto避免类型书写
// cv::Ptr<ORB> orb_detecto = cv::ORB::create();
auto orb_detecto = cv::ORB::create();

3.使用std::vector,

这个在OpenCV的程序中被大量使用,特别是在二值图像分析,特征提取等模块中。代码演示部分涉及到vector容器的定义初始化、添加元素、排序、几种循环fectch方式,删除元素、合并多个vector为一个等。全部的代码演示如下:

// 使用数组容器 - 直接定义
std::vector<int> a;
// 初始化定义
std::vector<int> b{ 3,2,1,4,6,5,9,8,7 };

// 循环fetch元素    
for (auto i : b) {
    std::cout <<"for-loop方式"<< i << std::endl;
}
// 迭代器方式fetch元素
for (auto it = std::begin(b); it != std::end(b); ++it) {
    std::cout << "迭代器方式: " << *it << std::endl;
}
// for-each方式
std::for_each(std::begin(b), std::end(b), process);

// 容器合并
a.push_back(22);
a.push_back(11);
b.insert(b.end(), a.begin(), a.end());
std::cout << "打印合并之后的数组:" << std::endl;
for (auto ab : b) {
    std::cout << ab <<" " ;
}
std::cout << " " << endl;

// 排序
std::sort(b.begin(), b.end());
std::cout << "打印排序之后的数组:" << std::endl;
for (auto ab : b) {
    std::cout << ab << " ";
}
std::cout << " " << endl;

// 保留前面N个
auto n = 5;
b.erase(b.begin() + n, b.begin() + b.size());
for (auto ab : b) {
    std::cout <<"打印删除后的剩余元素: "<< ab << std::endl;
}

 运行结果上图所示

4.字符串流

std::stringstream在OpenCV中无论什么想输出的数据类型从int\float\double\string都可以往里面扔,拼接在一起,最后只要调用一下str()方法就会全部转换为str,可以输出到图像,文本、控制台上,非常的方便。特别是通过putText输出到图像上。代码演示如下:

// 字符串流,任意拼接
std::stringstream ss;
ss << "Hello World, " << "OpenCV";
auto version = 4.5;
ss << version;
cv::putText(image, ss.str().c_str(), cv::Point(20, 50), cv::FONT_HERSHEY_SIMPLEX, 1.0, cv::Scalar(0, 0, 255), 2, 8);
cv::imshow("图像", image);

5.使用map容器

代码演示了map对象的添加与循环fetch元素的方式,演示代码如下:

// map对象实现key-value操作
std::map<int, std::string> labels;
labels.insert(std::pair<int, std::string>(0, "background"));
labels.insert(std::pair<int, std::string>(1, "person"));
labels.insert(std::pair<int, std::string>(2, "car"));
labels.insert(std::pair<int, std::string>(3, "road"));
 // 获取map对象, for-loop方式
std::cout << "map对象循环,for-loop方式" << std::endl;
for (auto item : labels) {
    std::cout << item.first << ","<<item.second <<std::endl;
}
// 获取map对象, 迭代器方式
std::cout << "map对象循环,迭代器方式" << std::endl;
for (auto it = labels.begin(); it != labels.end(); ++it) {
    std::cout << it->first << "," << it->second << std::endl;
}

 结果如上图所示

6.时间计算与转换

主要是基于std::chrono包的各种函数应用,演示代码如下:

std::cout << "Hello World, C++" << std::endl;
// 毫秒
std::chrono::milliseconds ms(3);
std::cout << ms.count() << std::endl;
// 转换为微秒
std::chrono::microseconds us = ms * 2;
std::cout << us.count() << std::endl;
// 计算执行时间
auto t1 = std::chrono::system_clock::now();
cv::Mat src = cv::imread("D:/images/test.png");
cv::Mat gray;
cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY);
auto t2 = std::chrono::system_clock::now();
std::cout <<"毫秒数:"<<
         std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count() <<
         " 微秒数:"<< std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count() <<
         " 秒数:" << std::chrono::duration_cast<std::chrono::seconds>(t2 - t1).count() << std::endl;


// 计算当前时间与日期
auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::cout << "Current Time: "<<std::put_time(std::localtime(&t), "%Y-%m-%d %H.%M.%S")<< std::endl;

7.数值转换

有时候会读取数据文件,需要把数据从字符(string)类型转为数值(number)类型,常见的有int、float、double、long等类型与string类型的相互转换,这部分的转换主要依赖函数:

  • std::to_string 这个是万能的

  • atoi 转化为整数int类型

  • atof 转换为浮点数float类型

// 各种字符与数值转换
double d = 1.234;
float f = 3.145;
int i = 314;
long l = 22;
std::cout << std::to_string(d) << std::endl;
std::cout << std::to_string(f) << std::endl;
std::cout << std::to_string(i) << std::endl;
std::cout << std::to_string(l) << std::endl;


// 从string到数值
const char* str1 = "3.2333";
const char* str2 = "5.321";
float f1 = std::atof(str1);
float f2 = std::atof(str2);
float f3 = f1 + f2;
std::cout << f3 << std::endl;


const char* str3 = "100";
const char* str4 = "121";
int i3 = std::atoi(str3) + std::atoi(str4);
std::cout << i3 << std::endl;

 

 8.size_t

size_t是跟int/float等这些数据类型定义相似的一个数据类型定义,它表示的是无符合的整型数据,在<stddef.h>, <stdio.h>, <stdlib.h>, <string.h>, <time.h>, <wchar.h>等这些头文件中均有定义。C语言中内存分配、内存拷贝、字符串长度声明的都使用size_t。此外size_t经常用来表现对象的数目,注:size_t数据不可能为负数。代码如下:

std::vector<int> a = { 11, 21, 9, 14, 8, 19 };
// 正确遍历
for (size_t t = 0; t < a.size(); t++) {
    std::cout << "a["<<t<<"] = "<< a[t] << std::endl;
}

// 无限循环
for (size_t t = a.size() - 1; t >= 0; --t) {
    std::cout << "a[" << t << "] = " << a[t] << std::endl;
}

std::sort(a.begin(), a.end());
for (auto i : a) {
    std::cout << i << std::endl;
}

9.数值精度取舍

C++浮点数计算数值精度的取舍支持,OpenCV中有三个相关函数,它们分别是:

int cvRound     (double value) // 四舍五入int cvFloor       (double value) // 总是舍弃int cvCeil (double value) // 总是进位

 10.快速生成随机像素图象

随机像素生成随机噪声图象,相关函数为:

void cv::randu(         InputOutputArray dst,         InputArray      low,         InputArray      high)

Lesson01
实现读取(imread)和显示(imshow)图像

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

using namespace cv;
using namespace std;

int main(int artc, char** argv) {
	Mat src1 = imread("E:/opencv/photo/dog.jpeg",IMREAD_COLOR);
	//彩色(默认为1)读取一个图片name1 = imread("绝对路径",IMREAD_COLOR);
	Mat src2 = imread("E:/opencv/photo/dog.jpeg",IMREAD_GRAYSCALE);
	//黑白(0)读取一个图片name1 = imread("绝对路径",IMREAD_GRAYSCALE);
	if (src2.empty()) {//如果空,返回-1
		printf("could not load image..\n");
		return -1;
	}
	namedWindow("输入窗口", WINDOW_AUTOSIZE);
	//使用namedWindow("name2",WINDOW_FREERATIO); 控制输出图片窗口大小
	imshow("输入窗口", src2);
	//imshow("name2",name1);将图片name1从name2窗口中输出
	waitKey(0);
	//waitKey(num);表示停顿num毫秒。waitKey(0);表示图片输出后停止不向下运行
	destroyAllWindows;
	return 0;
}

小结


Lesson02

色彩转换函数(cvtColor)图像保存(imwrite)

void QuickDemo::ColorSpace_Demo(Mat& image) {
	Mat hsv, gray;
	cvtColor(image, hsv, COLOR_BGR2HSV);
	cvtColor(image, gray, COLOR_BGR2GRAY);
	imshow("HSV", hsv);
	imshow("灰度图", gray);
	imwrite("E:/VSworkplace/photo/hsv.jpg", hsv);
	imwrite("E:/VSworkplace/photo/gray.jpg", gray);
}

Lesson03

对Mat对象的操作

如何创建一个Mat对象 (Mat::ones/zeros(Size(8,8),CV_8UC1))

赋值        m1 = image

克隆        m2 = iamge.clone()

拷贝        m3 = copyTo(image)

给三通道赋值        m = Scalar(p , q , r)

图像宽度 image.cols        图像高度 image.rows        图像通道数 image.channels()

void QuickDemo::MatCreation_Demo(Mat& image) {
	Mat m1, m2,m3;
	m1 = image.clone();//将输入的image克隆给m1,改变m1,image不改变
	image.copyTo(m2);//将输入的image拷贝给m2,改变m2,image不改变
	m3 = image;//m3和image指向同一数据,改变m3,image也改变
	//创建空白图像
	Mat m = Mat::ones(Size(8, 8), CV_8UC1);
	//Mat::ones(Size(n,n),CV_8UC1) 创建一个n*n的CVp位的无符号q通道的unsigned char
	
	m = Scalar(0);//空白图像赋值(灰度图)
	m = Scalar(255, 0, 0);//空白图像赋值(BGR图)
	//给三通道赋值(B,G,R)
	//数据的宽度和长度由通道数决定
	cout <<"width = "<< m3.cols << endl;//image.cols查看图像宽度
	cout << "height =" << m3.rows << endl;//image.rows查看图像高度
	cout << "channels = " << m3.channels() << endl;//image.channels()查看图像的通道数
}
//CV_8UC3含义:
//比特数:
//每一个像素点在内存空间所占的空间大小是8bite, 8位--所以它对应的就是CV_8

//S | U | F:
//S--代表---signed int-----有符号整形
//U--代表--unsigned int----无符号整形
//F--代表--float-----------单精度浮点型

//C<number_of_channels>:
//代表-- - 一张图片的通道数, 比如:
//1--灰度图片--grayImg-- - 是--单通道图像
//2--RGB彩色图像-------- - 是--3通道图像
//3--带Alph通道的RGB图像--是--4通道图像

Lesson04

图像像素的读写操作

如何遍历和修改每个像素点的数值,分为单通道和多通道。
访问模式模式也有两种:
第一种是数组访问模式,用最常规的数组下标访问像素值
第二种是指针访问模式,指定一个指针为图片的首地址,通过循环遍历,指针++,一次往后推一

//具体<>中应填写什么类型,是由图像img的类型决定的:
//<uchar>——CV_8UC1
//<char>——CV_8SC1
//<short>——CV_16SC1
//<ushort>——CV_16UC1
//<int>——CV_32SC1
//<float>——CV_32FC1
//<double>——CV_64FC1
//<Vec3b>——CV_8UC3--uchar型
//<Vec3f>——CV_32FC3--Float型
//<Vec3i>——CV_32SC3--int 型

//利用数组遍历和修改每一个像素点的值
//数组下标访问像素值
void QuickDemo::Pixel_Visit_Demo1(Mat& image) {
	int channels = image.channels();//获取图像的通道数
	int height = image.rows;//获取图像的行数
	int width = image.cols;//获取图像的列数

	for (int rows = 0; rows < height; ++rows) {//遍历每一行
		for (int cols = 0; cols < width; ++cols) {//遍历每一列

			if (channels == 1) {//单通道的灰度图像
				int pv = image.at<uchar>(rows, cols);//获取灰度图像一点的像素值
				image.at<uchar>(rows, cols) = 255 - pv;//灰度图像修改像素值
			}

			if (channels == 3) {//三通道的彩色图像
				Vec3b bgr = image.at<Vec3b>(rows, cols);
				image.at<Vec3b>(rows, cols)[0] = 255 - bgr[0];//blue
				image.at<Vec3b>(rows, cols)[1] = 255 - bgr[1];//green
				image.at<Vec3b>(rows, cols)[2] = 255 - bgr[2];//red
			}
		}
	}
	namedWindow("像素读写演示", WINDOW_FREERATIO);
	imshow("像素读写演示", image);
}

//利用指针遍历和修改每一个像素点的值
//指定一个指针为图片的首地址,通过循环遍历,指针++,一次往后推一个
void QuickDemo::Pixel_Visit_Demo2(Mat& image) {
	int channels = image.channels();//获取图像的通道数
	int height = image.rows;//获取图像的行数
	int width = image.cols;//获取图像的列数

	for (int rows = 0; rows < height; ++rows) {//遍历每一行

		uchar* current_row = image.ptr<uchar>(rows);//指针current_row指向图像第rows行的第一个数据。

		for (int cols = 0; cols < width; ++cols) {//遍历每一列

			if (channels == 1) {//单通道的灰度图
				int pv = *current_row;
				*current_row++ = 255 - pv;//每一行是像素的个数(image.cols)乘以通道数(image.channels())
										  //每次current_row++,其实是移动指针指向下一个通道
			}

			if (channels == 3) //三通道的彩色图像
			{
				*current_row++ = 255 - *current_row; //指针每做一次运算,就向后移动一位
				*current_row++ = 255 - *current_row;
				*current_row++ = 255 - *current_row;
			}
		}
	}
}

Lesson05

像素点的运算

1、对图像的各个像素点实现加减乘除的操作。
2、常用的除爆函数saturate_cast,防止数值过界。

void QuickDemo::Operator_Demo(Mat& image) {
	Mat dst;
	Mat m;
	m = Scalar(50, 50, 50);
	namedWindow("原图像", WINDOW_FREERATIO);
	imshow("原图像", image);

	add(image, m, dst);//加法操作 api
	namedWindow("加法图像", WINDOW_FREERATIO);
	imshow("加法图像", dst);

	subtract(image, m, dst);//减法操作 api
	namedWindow("减法图像", WINDOW_FREERATIO);
	imshow("减法图像", dst);

	multiply(image, m, dst);//乘法操作 api
	namedWindow("乘法图像", WINDOW_FREERATIO);
	imshow("乘法图像", dst);

	divide(image, m, dst);//除法操作 api
	namedWindow("除法图像", WINDOW_FREERATIO);
	imshow("除法图像", dst);

	//加法操作的底层
	int height = image.rows;
	int width = image.cols;
	int dims = image.channels();
	for (int row = 0; row < height; ++row) {
		for (int col = 0; col < width; ++col) {
			Vec3b p = image.at<Vec3b>(row, col);
			Vec3b q = m.at<Vec3b>(row, col);
			//saturate_cast用来防爆,小于0就是0,大于255就是255
			dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p[0] + q[0]);
			dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p[1] + q[1]);
			dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p[2] + q[2]);
		}
	}
	namedWindow("加法图像", WINDOW_FREERATIO);
	imshow("加法图像", dst);
}

Lesson06

TrackBar 滚动条操作

通过createTrackbar来设置一个进度条,实现图片的亮度调节

int lightness = 50; //定义初始亮度为50
Mat src, m, dst;
static void on_track(int, void*) {
	m = Scalar(lightness, lightness, lightness);//创建亮度变化的数值
	add(src, m, dst);//亮度变化为增加
	imshow("亮度增强", dst);//显示亮度变化后的图像
}
void QuickDemo::Tracking_Bar_Demo(Mat& image) {
	m = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像
	dst = Mat::zeros(image.size(), image.type());
	src = image;
	int max_value = 100;//设置亮度增加的最大值
	createTrackbar("Value Bar:", "亮度增强", &lightness, max_value, on_track);
	on_track(50, 0);
}
//CV_EXPORTS int createTrackbar(const string& trackbarname, const string& winname,
//	int* value, int count,
//	TrackbarCallback onChange = 0,
//	void* userdata = 0);
//		形式参数一、trackbarname:滑动空间的名称;
//		形式参数二、winname:滑动空间用于依附的图像窗口的名称(必须和imshow中的窗口名称相同);
//		形式参数三、value:初始化阈值;
//		形式参数四、count:滑动控件的刻度范围;
//		形式参数五、TrackbarCallback是回调函数

Lesson07

滚动条演示操作-传递参数

//void AddWeighted(const CvArr* src1, double alpha, const CvArr* src2, double beta, double gamma, CvArr* dst);
//参数1:src1,第一个原数组.
//参数2:alpha,第一个数组元素权重
//参数3:src2第二个原数组
//参数4:beta,第二个数组元素权重
//参数5:gamma,图1与图2作和后添加的数值。不要太大,不然图片一片白。总和等于255以上就是纯白色了。
//参数6:dst,输出图片
static void on_lightness(int a, void* userdata) {
	Mat image = ( * (Mat*)(userdata));//使用userdata,先强制转换格式,然后此函数要求传入内容,所以还要取内容符,第三步
	Mat m = Mat::zeros(image.size(), image.type());
	Mat dst = Mat::zeros(image.size(), image.type());
	m = Scalar(a, a, a);
	addWeighted(image, 1.0, m, 0.0, a, dst);
	imshow("亮度与对比度调节", dst);
}
static void on_contrast(int b, void* userdata) {
	Mat image = ( * (Mat*)(userdata));//使用userdata,先强制转换格式,然后此函数要求传入内容,所以还要取内容符,第三步
	Mat m = Mat::zeros(image.size(), image.type());
	Mat dst = Mat::zeros(image.size(), image.type());
	double contrast = b / 100.0;
	addWeighted(image, contrast, m, 0.0, 0, dst);
	imshow("亮度与对比度调节", dst);
}
void QuickDemo::Tracking_Bar_Demo2(Mat& image) {//传入图片,第一步
	int lightness = 50;
	int contrastness = 100;
	createTrackbar("lightness :", "亮度与对比度调节", &lightness, 100, on_lightness ,(void*)(&image));//将图片指针传入userdata,第二步
	createTrackbar("contractness :", "亮度与对比度调节", &contrastness, 200, on_contrast, (void*)(&image));//将图片指针传入userdata,第二步
	on_lightness(50, &image);
}

Lesson08

键盘响应操作

本节介绍通过键盘输入,终端能够读取响应的信息。

void QuickDemo::Key_Demo(Mat& image) {
	Mat dst = Mat::zeros(image.size(), image.type());
	while (1) {
		char c = waitKey(100);
		cout << c << endl;
		if (c == 27) {
			cout << "Esc" << endl;
			break;
		}
		if (c == 49) {
			cout << "input #1,灰度图" << endl;
			cvtColor(image, dst, COLOR_BGR2GRAY);
		}
		if (c == 50) {
			cout << "input #2,HSV图" << endl;
			cvtColor(image, dst, COLOR_BGR2HSV);
		}
		if (c == 51) {
			cout << "input #3,亮度增强" << endl;
			dst = Scalar(50, 50, 50);
			add(image, dst, dst);
		}
		namedWindow("键盘输入", WINDOW_AUTOSIZE);
		imshow("键盘键入", dst);
	}
}

Lesson09

opencv自带颜色操作

void QuickDemo::Color_Style_Demo(Mat& image) {
	int ColorMap[] = {
		COLORMAP_AUTUMN ,
		COLORMAP_BONE,
		COLORMAP_CIVIDIS,
		COLORMAP_DEEPGREEN,
		COLORMAP_HOT,
		COLORMAP_HSV,
		COLORMAP_INFERNO,
		COLORMAP_JET,
		COLORMAP_MAGMA,
		COLORMAP_OCEAN,
		COLORMAP_PINK,
		COLORMAP_PARULA,
		COLORMAP_RAINBOW,
		COLORMAP_SPRING,
		COLORMAP_TWILIGHT,
		COLORMAP_TURBO,
		COLORMAP_TWILIGHT,
		COLORMAP_VIRIDIS,
		COLORMAP_TWILIGHT_SHIFTED,
		COLORMAP_WINTER
	};
	Mat dst = Mat::zeros(image.size(),image.type());
	int index = 0;
	while (1) {
		char c = waitKey(1000);
		if (c == 27) {
			cout << "ESC,退出" << endl;
			break;
		}
		if (c == 49) {
			cout << "#1,保存此时图像" << endl;
			imwrite("E:/VSworkplace/OpenCV/photo/style.jpg", dst);
		}
		applyColorMap(image, dst, ColorMap[index % 20]);
		++index;
		imshow("循环播放", dst);
	}
}
//	void applyColorMap(InputArray src, OutputArray dst, int colormap)
//	src: 源图像(灰度图或彩色图(CV_8UC1 or CV_8UC3))。
//	dst: 在源图像上进行色彩映射后的结果图像。
//	colormap:提供的色彩图代码值。

Lesson10

图像像素的逻辑操作

对图像的像素进行操作,包括与、或、非、异或,矩形在图像中的绘制。

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);
	//rectangle(img2, Point(j, i), Point(j + img4.cols, i + img4.rows), Scalar(255, 255, 0), 2, 8);
	//img2:被处理的图片
	//Point(j, i)代表矩形左上点的坐标
	//Point(j + cols, i + rows)代表矩形右下点的坐标【矩形的大小(cols, rows】
	//scalar:颜色
	//2代表线条宽度
	//8是线型,默认取8
	//Rect()函数是画出图像中的矩形
	//Rect(x, y, width, height),x, y为左上角坐标, width, height则为长和宽。
	Mat dst;
	bitwise_and(m1, m2, dst);//与
	imshow("与", dst);
	bitwise_or(m1, m2, dst);//或
	imshow("或", dst);
	bitwise_not(image, dst);//非
	imshow("非", dst);
	bitwise_xor(m1, m2, dst);//异或
	imshow("异或", dst);
}

Lesson11

通道的分离与合并

如何把不同的通道给分离,归并,使得能显现出来不同的通道颜色。

void QuickDemo::Channels_Demo(Mat& image) {
	vector<Mat>mv;
	//vector是向量类型,可以容纳许多类型的数据,因此也被称为容器
	//(可以理解为动态数组,是封装好了的类)
	split(image, mv);
	void split(InputArray m, OutputArrayOfArrays mv);
	//split函数:第一个参数为要进行分离的图像矩阵,
				//第二个参数可以是Mat数组的首地址,或者一个vector<Mat>对象
	imshow("Blue", mv[0]);
	imshow("Green", mv[1]);
	imshow("Red", mv[2]);
	Mat dst;
	mv[2] = 0;
	merge(mv, dst);
	imshow("merge", dst);

	int from_to[6] = { 0, 2, 1, 1, 2, 0 };
	mixChannels(&image, 1, &dst, 1, from_to,3);//3表示3个通道
	//void cv::mixChannels(const Mat * src,
	//					size_t 	nsrcs,
	//					Mat * dst,
	//					size_t 	ndsts,
	//					const int* fromTo,
	//					size_t 	npairs)
	//	第一个参数:输入矩阵
	//	第二个参数:输入矩阵的数量
	//	第三个参数:输出矩阵
	//	第四个参数:输出矩阵的数量
	//	第五个参数:复制列表
	//	第六个参数:复制列表的数量
	imshow("混合通道", dst);
}

Lesson12

图像色彩空间转换 

void QuickDemo::Inrange_Demo(Mat& image) {
	Mat hsv = Mat::zeros(image.size(), image.type());
	cvtColor(image, hsv, COLOR_BGR2HSV);
	imshow("hsv", hsv);

	Mat mask;
	inRange(image, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);
//inRange(输入,最小值,最大值,输出)
//inRange()函数可实现二值化功能,可同时对多个通道进行操作
//主要功能是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)
	imshow("mask", mask);

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

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

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


Lesson13

图像像素值统计

定义双精度型变量 minv和maxv。
指针变量minLoc,maxLoc;
因为这图片是多通道的,所以使用一个容器装取数值,并且用split分离图片到MV中
通过for循环操作,遍历图片信息,并且打印信息到终端。
图像信息包括,方差,均值,大小。

void QuickDemo::Pixel_Statistic_Demo(Mat& image)
{
	double minv, maxv;//定义最值
	Point minLoc, maxLoc;//定义最值地址
	std::vector<Mat>mv;//mv是一个Mat类型的容器 装在这个容器内
	split(image, mv);
	for (int i = 0; i < mv.size(); i++)
	{
		//分别打印各个通道的数值
		minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc);//求出图像的最大值和最小值。
//CV_EXPORTS_W void minMaxLoc(InputArray src, CV_OUT double* minVal,
//							CV_OUT double* maxVal = 0, CV_OUT Point * minLoc = 0,
//							CV_OUT Point * maxLoc = 0, InputArray mask = noArray());
//参数1 src 输入单通道矩阵.
//参数2 minVal 返回最小值的指针; 如果不需要输入NULL.
//参数3 maxVal 返回最大值的指针; 如果不需要输入NULL.
//参数4 minLoc 返回最小值位置的指针(二维情况下); 如果不需要输入NULL.
//参数5 maxLoc 返回最大值位置的指针(二维情况下); 如果不需要输入NULL.
//参数6 mask 可选参数,用于选择一个子矩阵.
		std::cout << "No.channels:" << i << "minvalue:" << minv << "maxvalue:" << maxv << std::endl;
	}

	Mat mean, stddev;
	meanStdDev(image, mean, stddev);//求出图像的均值和方差
//void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, InputArray mask = noArray())
//src:输入矩阵,这个矩阵应该是1 - 4通道的
//mean:输出参数,计算均值
//stddev:输出参数,计算标准差
//mask:可选参数
	std::cout << "mean:" << mean << std::endl;
	std::cout << "stddev:" << stddev << std::endl;
}

Lesson14

图像几何形状的绘制

绘制椭圆,矩形,直线,圆等

void QuickDemo::Drawing_Demo(Mat& image) {
	Rect rect;
	rect.x = 200;
	rect.y = 200;
	rect.width = 100;
	rect.height = 100;
	rectangle(image, rect, Scalar(255, 0, 0), 1, 8, 0);
//参数1为绘图的底图或者画名称
//参数2位图片的起始,宽度,高度
//参数3代表填充颜色。
//参数4大于0是线小于0是填充
//参数5表示邻域填充
//参数6默认值为0
	imshow("rect", image);

	Mat bg =Mat::zeros(image.size(), image.type());
	circle(bg, Point(150, 200), 15, Scalar(255, 255, 0), -1,LINE_AA, 0);
//cvCircle(CvArr * img, CvPoint center, int radius, CvScalar color, int thickness = 1, int lineType = 8, int shift = 0)
//img为源图像指针
//center为画圆的圆心坐标
//radius为圆的半径
//color为设定圆的颜色,规则根据B(蓝)G(绿)R(红)
//thickness 如果是正数,表示组成圆的线条的粗细程度。否则,表示圆是否被填充
//line_type 线条的类型,默认是8;LINE_AA(16)表示去掉锯齿
//shift 圆心坐标点和半径值的小数点位数
	imshow("circle", bg);

	Mat dst;
	addWeighted(image, 0.7, bg, 0.3, 0, dst);
	imshow("dst", dst);

	RotatedRect rtt;
	rtt.center = Point(200, 200);
	rtt.size = Size(100, 200);
	rtt.angle = 0.0;
//  RotatedRect(const Point2f& center, const Size2f& size, float angle);
//	center; 矩形的质心
//	size;   矩形的边长
//	angle;  旋转角度,当角度为0、90、180、270等时,矩形就成了一个直立的矩形

	line(bg, Point(100, 100), Point(350, 400), Scalar(0, 0, 255), 8, LINE_AA, 0);
//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 : 线段的类型。
//shift : 坐标点小数点位数
	imshow("bg", bg);

	ellipse(bg, rtt, Scalar(0, 0, 255), 2, 8);
//void ellipse(Mat & img, const RotatedRect & box, const Scalar & color, int thickness = 1, int lineType = 8)
//img	图像 
//color	线条的颜色
//thickness	线条的粗细程度
//line_type	线条的类型
	imshow("椭圆的绘制", bg);
}

Lesson15

随机数与随机颜色

产生一个随机数字和随机颜色,并且用线条的方式显示出来。

void QuickDemo::Random_Drawing_Deno(Mat& image) {
	Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
	int width = canvas.cols;
	int height = canvas.rows;
	RNG rng(12345);
//RNG(int seed) 使用种子seed产生一个64位随机整数
//RNG::uniform(a, b) 返回一个[a, b)范围的均匀分布的随机数,a, b的数据类型要一致,默认是int。
//RNG::gaussian(σ) 返回一个均值为0,标准差为σ的随机数。
//如果要产生均值为λ,标准差为σ的随机数,可以λ + RNG::gaussian(σ)
	while (1) {
		char c = waitKey(100);
		if (c == 27)
			break;
		int x1 = rng.uniform(0, width);
		int x2 = rng.uniform(0, width);
		int y1 = rng.uniform(0, height);
		int y2 = rng.uniform(0, height);
		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), 8, 16, 0);
		imshow("canvas", canvas);
	}
}

Lesson16

多边形填充与绘制

2种多边形绘制的实现方式。

void QuickDemo::Polyline_Drawing_Demo(Mat& image) {
//法一:通过标记各个点,然后存储到容器中,之后对容器中的点进行操作。
//		填充多边形调用fillPoly,绘制多边形调用polylines。
	Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
	Point p1(100, 100);
	Point p2(180, 180);
	Point p3(150, 260);
	Point p4(50, 260);
	Point p5(20, 20);
	vector<Point>pts;
	pts.push_back(p1);
	pts.push_back(p2);
	pts.push_back(p3);
	pts.push_back(p4);
	pts.push_back(p5);
//std::vector<Point> pts(5);
//pts[0] = p1;
//pts[1] = p2;
//pts[2] = p3;
//pts[3] = p4;
//pts[4] = p5;
//pts.push_back 用于将点加入到点集数组中,用于不知道点集数量的情况。
	polylines(canvas, pts, true, Scalar(255, 0, 255), 8, LINE_AA, 0);
//void cv::polylines(Mat & img,
//	const Point* const* pts,
//	bool    isClosed,
//	const Scalar & color,
//	int     thickness = 1,
//	int     lineType = LINE_8,
//	int     shift = 0
//)
//	参数img	作为画布的矩阵
//	参数pts	折线顶点数组
//	参数isClosed	是否是闭合折线(多边形)
//	参数color	折线的颜色
//	参数thickness	折线粗细
//	参数lineType	线段类型
//	参数shift	缩放比例(0是不缩放, 4是1 / 4)
	imshow("polylines", canvas);

	fillPoly(canvas, pts, Scalar(255, 255, 0), LINE_AA);
//	void cv::fillPoly(
//		InputOutputArray	img,
//		InputArrayOfArrays	pts,
//		const Scalar & color,
//		int		lineType = LINE_8,
//		int		shift = 0
//	)
//fillPoly()绘制填充的多边形
//参数img	要绘制的目标图像
//参数pts	提前构造的点集
//参数color	多边形边框的颜色
//参数lineType	线条的类型
//参数shift	坐标点的小数点位数
	imshow("fillPoly", canvas);

//法二:使用一个API接口绘制。通过一个容器中的存储的点组成的另一个容器 
	Mat dst = Mat::zeros(canvas.size(), canvas.type());
	vector<vector<Point>>contours;
//vector容器里放了一个vector容器
	contours.push_back(pts);
	drawContours(dst, contours, -1, Scalar(0, 255, 255), 8, LINE_AA );
//绘制多个多边形
//	void drawContours(
//		InputOutputArray 	image,
//		InputArrayOfArrays 	contours,
//		int 	contourIdx,
//		const Scalar & color,
//		int 	thickness = 1,
//		int 	lineType = 8,
//		InputArray 	hierarchy = noArray(),
//		int 		maxLevel = INT_MAX,
//		Point 		offset = Point())
//第一个参数image表示目标图像,
//第二个参数contours表示输入的轮廓组,每一组轮廓由点vector构成,
//第三个参数contourIdx指明画第几个轮廓,如果该参数为负值,则画全部轮廓,
//第四个参数color为轮廓的颜色,
//第五个参数thickness为轮廓的线宽,如果为负值或CV_FILLED表示填充轮廓内部,
//第六个参数lineType为线型,
//第七个参数为轮廓结构信息,
//第八个参数为maxLevel
	imshow("dst", dst);
}

第十七节课

鼠标操作与响应

Point sp(-1, -1);
Point ep(-1, -1);
Mat temp;
//void on_Mouse(int event, int x, int y, int flags, void* param);
//event是 CV_EVENT_* 变量之一
//x和y是鼠标指针在图像坐标系的坐标(不是窗口坐标系)
//flags是CV_EVENT_FLAG的组合, param是用户定义的传递到setMouseCallback函数调用的参数。
static void on_Mouse(int event, int x, int y, int flags, void* userdata) {
	Mat image = *((Mat*)userdata);
	if (event == EVENT_LBUTTONDOWN)//如果鼠标的左键按下
	{
		sp.x = x;
		sp.y = y;
		cout << "start point" << sp << 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);
			//Rect (x,y,width,height)
			imshow("ROI区域", image(box));
			rectangle(image, box, Scalar(0, 0, 255), 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);
				rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
				imshow("鼠标绘制", image);
			}
		}
	}
}
void QuickDemo::Mouse_Drawing_Demo(Mat& image) {
	namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
	setMouseCallback("鼠标绘制", on_Mouse);
//void setMousecallback(const string & winname, MouseCallback onMouse, void* userdata = 0)
//winname:窗口的名字
//onMouse : 鼠标响应函数,回调函数。指定窗口里每次鼠标时间发生的时候,被调用的函数指针。
//userdate:传给回调函数的参数
//设置窗口的回调函数。参数1表示名称,参数2表示调用on_draw
	imshow("鼠标绘制", image);
	temp = image.clone();
}

Lesson18

图像像素类型的转换与归一化

//归一化就是要把需要处理的数据经过处理后(通过某种算法)限制在你需要的一定范围内。
//主要优点是能够避免大数值区间的属性过分支配了小数值区间的属性。另一个优点能避免计算过程中数值复杂度。
void QuickDemo::Norm_Demo(Mat& image) {
	Mat dst;
	cout << image.type() << endl;
	cout << dst.type() << endl;
	image.convertTo(image, CV_32F);
	cout << image.type() << endl;
	cout << dst.type() << endl;
	imshow("image2", image);
	//CV_8UC3 -> CV_32FC3
	normalize(image, dst, 1.0,0.0, NORM_MINMAX);
//normalize(
//	InputArray 	src, // 输入图像
//	InputOutputArray 	dst, // 输出图像
//	double 	alpha = 1, // range normalization模式的最小值
//	double 	beta = 0, // range normalization模式的最大值
//	int 	norm_type = NORM_L2, //归一化的类型,可以有以下的取值:
//								   NORM_MINMAX:数组的数值被平移或缩放到一个指定的范围,线性归一化,一般较常用。
//								   NORM_INF :归一化数组的C - 范数(绝对值的最大值)
//								   NORM_L1 : 归一化数组的L1 - 范数(绝对值的和)
//								   NORM_L2 : 归一化数组的(欧几里德)L2 - 范数
//	int 	dtype = -1, //  dtype为负数时,输出数组的type与输入数组的type相同;
//							否则,输出数组与输入数组只是通道数相
//	InputArray 	mask = noArray() // 操作掩膜,用于指示函数是否仅仅对指定的元素进行操作
//)
	cout << image.type() << endl;
	cout << dst.type() << endl;
	imshow("dst1", dst);
	dst = dst * 255;
	cout << image.type() << endl;
	cout << dst.type() << endl;
	imshow("dst2", dst);
}

Lesson19

图像的放缩与差值

基本的图像变换大小的方法。
图像的差值处理主要有线性、双线性差值、卢卡斯差值、双立方差值。

void QuickDemo::Resize_Demo(Mat& image) {
	Mat zoom1, zoom2;
	int h = image.rows;
	int w = image.cols;
	resize(image, zoom1, Size(w / 2, h / 2));
	resize(image, zoom2, Size(w * 1.5, h * 1.5));
//resize(InputArray src, OutputArray dst, Size dsize,
//		double fx = 0, double fy = 0, int interpolation = INTER_LINEAR)
//参数解释:
//InputArray src :输入,原图像,即待改变大小的图像;
//OutputArray dst: 输出,改变后的图像。这个图像和原图像具有相同的内容,只是大小和原图像不一样而已;
//dsize:输出图像的大小。
//		如果这个参数不为0,那么就代表将原图像缩放到这个Size(width,height)指定的大小;
//		如果这个参数为0,那么原图像缩放之后的大小就要通过下面的公式来计算:
//		dsize = Size(round(fxsrc.cols), round(fysrc.rows))
//其中,fx和fy就是下面要说的两个参数,是图像width方向和height方向的缩放比例。
//		fx:width方向的缩放比例,如果它是0,那么它就会按照(double)dsize.width / src.cols来计算;
//		fy:height方向的缩放比例,如果它是0,那么它就会按照(double)dsize.height / src.rows来计算;
//interpolation:这个是指定插值的方式,图像缩放之后,肯定像素要进行重新计算的,就靠这个参数来指定重新计算像素的方式,有以下几种:
//		INTER_NEAREST - 最邻近插值
//		INTER_LINEAR - 双线性插值,如果最后一个参数你不指定,默认使用这种方法
//		INTER_AREA - resampling using pixel area relation.It may be a preferred method for image decimation, as it gives moire’ - free results.But when the image is zoomed, it is similar to the INTER_NEAREST method.
//		INTER_CUBIC - 4x4像素邻域内的双立方插值
//		INTER_LANCZOS4 - 8x8像素邻域内的Lanczos插值
	imshow("zoom1", zoom1);
	imshow("zoom2", zoom2);
}

Lesson20

图像的旋转

void QuickDemo::Flip_Demo(Mat& image) {
	Mat dst;
	flip(image, dst, -1);
	imshow("dst-1", dst);
	flip(image, dst, 0);
	imshow("dst0", dst);
	flip(image, dst, 1);
	imshow("dst1", dst);
//void flip(InputArray src, OutputArray dst, int flipCode)
//参数:
//src,输入矩阵
//dst,翻转后矩阵,类型与src一致
//flipCode,翻转模式:
//		flipCode == 0垂直翻转(沿X轴翻转),
//		flipCode > 0水平翻转(沿Y轴翻转),
//		flipCode < 0水平垂直翻转(先沿X轴翻转,再沿Y轴翻转,等价于旋转180°)
}

Lesson21

图像的翻转

翻转图像的指定角度,确定得到新的图像信息,长宽发生改变。

void QuickDemo::Rotate_Demo(Mat& image) {
	Mat dst, M;
	int h = image.rows;
	int w = image.cols;
	M = getRotationMatrix2D(Point(h / 2, w / 2), 45.0,1.0);
	//第一个参数旋转中心,第二个参数旋转角度,第三个参数:缩放比例

	double cos = abs(M.at<double>(0, 0));
	double sin = abs(M.at<double>(0, 1));
	int nw = cos * w + sin * h;
	int nh = sin * w + cos * h;
	M.at<double>(0, 2) += (nw / 2 - w / 2);
	M.at<double>(1, 2) += (nh / 2 - h / 2);
	warpAffine(image, dst, m, image.size(), INTER_LINEAR, 0, Scalar(255, 255, 0));
	//	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
	imshow("dst", dst);
}

Lesson22

视频文件摄像头使用

如何读取一个视频,调用电脑的摄像头,并且对读取到的视频进行滤镜变换。

void QuickDemo::Video_Demo(Mat& image) {
	VideoCapture capture(0);
//cv::VideoCapture capture(int device);  
//视频捕捉设备 id ---笔记本电脑的用0表示 
	Mat frame;
	while (1) {
		capture.read(frame);
//将视频帧读取到cv::Mat矩阵中,有两种方式:一种是read()操作;另一种是 “ >> ”操作。
//Mat frame;
//cap.read(frame); //读取方式一  
//cap >> frame; //读取方式二  
flip(frame, frame, 1);
//镜像翻转
		if (frame.empty())
			break;
		imshow("frame", frame);
		ColorSpace_Demo(frame);
		char c = waitKey(100);
		if (c == 27)
			break;
	}
	capture.release();
	//释放视频资源
}

Lesson23

视频处理和保存

视频的属性,SD(标清),HD(高清),UHD(超清),蓝光。
如何读取视频文件,以及读取视频文件的属性,衡量视频处理指标:FPS。
保存视频时的编码格式。保存视频的实际size和create的size大小保持一致。

void QuickDemo::Video_Demo(Mat& image) {
	VideoCapture capture("E:/VSworkplace/OpenCV/photo/93.mp4");
//cv::VideoCapture capture(int device);  
//视频捕捉设备 id ---笔记本电脑的用0表示 
	int width = capture.get(CAP_PROP_FRAME_WIDTH);
	int height = capture.get(CAP_PROP_FRAME_HEIGHT);
	int number = capture.get(CAP_PROP_FRAME_COUNT);
	int fps = capture.get(CAP_PROP_FPS);
	cout << "Frame_Width:" << width << endl;
	cout << "Frame_Height:" << height << endl;
	cout << "Frame_Number:" << number << endl;
	cout << "Frame_Fps:" << fps << endl;
	VideoWriter writer("E:/VSworkplace/OpenCV/photo/932.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(width, height));
//cv2.VideoWriter('out.avi', fourcc, 20.0, (640, 480))
//VideoWriter()一共有四个参数,分别为:
//filename:文件路径
//fourcc:四个字符用来表示压缩帧的codec,可选参数如下
//		CV_FOURCC('P', 'I', 'M', '1') = MPEG - 1 codec
//		CV_FOURCC('M', 'J', 'P', 'G') = motion - jpeg codec
//		CV_FOURCC('M', 'P', '4', '2') = MPEG - 4.2 codec
//		CV_FOURCC('D', 'I', 'V', '3') = MPEG - 4.3 codec
//		CV_FOURCC('D', 'I', 'V', 'X') = MPEG - 4 codec
//		CV_FOURCC('U', '2', '6', '3') = H263 codec
//		CV_FOURCC('I', '2', '6', '3') = H263I codec
//		CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec
//		若编码器代号为 - 1,则运行时会弹出一个编码器选择框.
//fps:被创建视频流的帧率
//frame_size:视频流的大小
//isColor:如果为True则每一帧为彩色图,否则为灰度图,默认为True
	Mat frame;
	while (1) {
		capture.read(frame);
//将视频帧读取到cv::Mat矩阵中,有两种方式:一种是read()操作;另一种是 “ >> ”操作。
//Mat frame;
//cap.read(frame); //读取方式一  
//cap >> frame; //读取方式二  
	flip(frame, frame, 1);
//镜像翻转
		if (frame.empty())
			break;
		imshow("frame", frame);

		writer.write(frame);
		char c = waitKey(100);
		if (c == 27)
			break;
	}
	capture.release();
	writer.release();
	//释放视频资源
}

Lesson24

图像的直方图

直方图是图像的统计学特征。
表示了图像的各个像素在0-255出现的频率。
图像的平移旋转都不会对性质进行改变。
缺点:不能表征一张图像。

void QuickDemo::Show_Histogram_Demo(Mat& image) {
	vector<Mat>bgr_plane;
	split(src, bgr_plane);//分为三通道
	
	const int channels[1] = { 0 };
	const int bins[1] = { 256 };//灰度级别
	float hranges[2] = { 0,255 };//每个通道的取值范围
	const float* ranges[1] = { hranges };
	Mat b_hist;
	Mat g_hist;
	Mat r_hist;
	//计算BGR通道的直方图
	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, histImage.rows, NORM_MINMAX, -1, Mat());
	normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
	//绘制直方图曲线
	for (int i = 1; i < bins[0]; ++i) {
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
			Point(bin_w * i, hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
			Point(bin_w * i, hist_h - cvRound(g_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
		line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
			Point(bin_w * i, hist_h - cvRound(r_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
	}
	//显示直方图
	namedWindow("Histogram Demo", WINDOW_AUTOSIZE);
	imshow("Histogram Demo", histImage);
}

Lesson25

直方图的均衡化

用途:用于图像增强,人脸检测,卫星遥感。均衡化的图像只支持单通道。

void QuickDemo::histogram_eq_demo(Mat &image)
{
	Mat gray;
	cvtColor(image, gray, COLOR_BGR2GRAY);
	//直方图均衡化只支持灰度图像,不支持彩色图像。
	imshow("灰度图像", gray);
	Mat dst;
	equalizeHist(gray, dst);
	imshow("直方图均衡化", dst);
}

Lesson26

图像的卷积操作

卷积的作用,高的往下降,低的往上升,但是会造成信息丢失。
产生模糊效果。是一种线性操作,点乘,之后相加。

 

Lesson27

高斯模糊

中心的数值最大,离中心距离越远,数值越小。
高斯卷积数学表达式说明:

void QuickDemo::gaussian_blur_demo(Mat &image)
{
	Mat dst;
	GaussianBlur(image, dst, Size(5, 5), 15);
	imshow("高斯模糊", dst);
	//参数1表示初始图像,参数2表示处理后的图像,参数3表示高斯矩阵大小 正数而且是奇数,
	//参数4表示西格玛x为15 西格玛y为15 
}


Lesson28
高斯双边模糊

void QuickDemo::bifilter_demo(Mat &image)
{
	Mat dst;
	bilateralFilter(image,dst,0,100,0);
	//参数1代表原图,参数2代表处理之后的图像,参数3色彩空间。参数4表示坐标空间,双边是指 色彩空间和坐标空间。
	namedWindow("双边模糊", WINDOW_FREERATIO);//创建了一个新窗口,参数1表示名称,第二个参数代表一个自由的比例
	imshow("双边模糊", dst);//表示显示在新创建的
}

完结

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,OpenCV是一个开源计算机视觉和机器学习软件库,用于开发图像和视频处理应用程序。通过使用OpenCV,您可以读取摄像头并显示实时图像,打开视频文件或摄像头文件,并获取视频的相关信息,例如帧宽度、帧高度、帧率和总帧数。 对于学习OpenCV,你可以按照以下步骤进行: 1. 安装OpenCV库:在开始学习OpenCV之前,您需要从OpenCV官方网站下载和安装OpenCV库。根据您的操作系统和编程语言选择合适的版本。 2. 学习基本概念:熟悉OpenCV的基本概念和术语,例如图像和视频的加载、显示、保存以及常用的图像处理操作,如滤波、边缘检测和特征提取等。 3. 掌握OpenCV函数和类:深入了解OpenCV提供的函数和类,例如cv::Mat用于图像和矩阵操作,cv::VideoCapture用于读取和处理视频,以及cv::imshow和cv::waitKey等用于显示图像的函数。 4. 实践项目:通过完成一些实践项目来应用您所学到的知识。例如,利用OpenCV实现人脸检测、目标追踪、图像识别等。 5. 学习资料和资源:查找和阅读OpenCV的官方文档、教程和示例代码,参与开源社区讨论和交流,加入相关的论坛和邮件列表等。 总结起来,学习OpenCV包括安装OpenCV库、学习基本概念、掌握OpenCV函数和类、实践项目以及查找和阅读相关资料和资源。通过不断实践和学习,您将能够更好地理解和应用OpenCV库来开发图像和视频处理应用程序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值