【OpenCV】学习笔记(一):基础操作部分

前言:跟着浅墨大神,学习OpenCV3后,笔记如下。

1 基础知识

1.1 加载、修改、保存图像

  • 加载 cv::imread
  • 保存 cv::cvtColor
  • 保存 cv::imwrite
Mat image = imread("E:/result/MyPic1.png", 0);  //路径,0代表灰度打开
imshow("image1", image);
Mat gray_image;
cvtColor(image, gray_image, COLOR_BGR2GRAY);    //转换色彩空间
imshow("out_gray_image", gray_image);          
imwrite("E:/result/Mypic1_opencv.jpg", gray_image); //保存图片

1.2 获取图像像素指针

void showCamera()
{
	VideoCapture capture(0); //从摄像头中读取视频
	while (true)
	{
		Mat frame;
		capture >> frame;
		namedWindow("读取视频");
		imshow("读取视频", frame);
		waitKey(30);
	}
}
void showCapture()
{

	VideoCapture capture;
	capture.open("E:/result/MyVid.avi");
	
	while (true)
	{
		Mat frame;
		capture >> frame;	//读取当前帧

		if (frame.empty())
		{
			break;
		}
		imshow("读取视频", frame);
		waitKey(30);	//30ms
	}
}
void showImage()
{
	Mat src, dst;
	src = imread("E:/result/MyPic1.png");
	//src = imread("E:/result/zkc2.jpg");
	if (!src.data)
	{
		cout << "could not open this image..." << endl;
		return;
	}
	imshow("【原图】input image", src);

	Mat edge, grayImage;
	cvtColor(src, grayImage, CV_BGR2GRAY);
	blur(grayImage, edge, Size(3, 3));
	Canny(edge, edge, 3, 9, 3);

	imshow("【灰度】", grayImage);
	imshow("【边缘检测】Canny", edge);
}

1.3 创建滑动条

createTrackbar()

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

【OpenCV】学习笔记(一):基础操作部分_第1张图片

【示例】

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include 
   
using namespace cv;
using namespace std;
 
//-----------------------------------【全局函数声明部分】-----------------
//	描述:全局函数声明
//--------------------------------------------------------------------
Mat img;
int threshval = 160;			//轨迹条滑块对应的值,给初值160
 
//-----------------------------【on_trackbar( )函数】-------------------
//	描述:轨迹条的回调函数
//--------------------------------------------------------------------
static void on_trackbar(int, void*)
{
	Mat bw = threshval < 128 ? (img < threshval) : (img > threshval);
 
	//定义点和向量
	vector > contours;
	vector hierarchy;
 
	//查找轮廓
	findContours( bw, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
	//初始化dst
	Mat dst = Mat::zeros(img.size(), CV_8UC3);
	//开始处理
	if( !contours.empty() && !hierarchy.empty() )
	{
		//遍历所有顶层轮廓,随机生成颜色值绘制给各连接组成部分
		int idx = 0;
		for( ; idx >= 0; idx = hierarchy[idx][0] )
		{
			Scalar color( (rand()&255), (rand()&255), (rand()&255) );
			//绘制填充轮廓
			drawContours( dst, contours, idx, color, CV_FILLED, 8, hierarchy );
		}
	}
	//显示窗口
	imshow( "Connected Components", dst );
}
 
 
//-----------------------------------【main( )函数】--------------------------------------------
//	描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main(  )
{
	system("color 5F");  
	//载入图片
	img = imread("1.jpg", 0);
	if( !img.data ) { printf("Oh,no,读取img图片文件错误~! \n"); return -1; }
 
	//显示原图
	namedWindow( "Image", 1 );
	imshow( "Image", img );
 
	//创建处理窗口
	namedWindow( "Connected Components", 1 );
	//创建轨迹条
	createTrackbar( "Threshold", "Connected Components", &threshval, 255, on_trackbar );
	on_trackbar(threshval, 0);//轨迹条回调函数
 
	waitKey(0);
	return 0;
}

2 常用的数据结构和函数

2.1 点:Point类

//二维坐标系下点
Point point;
point.x = 10;
point.y = 8;
//或者
Point point = Point(10, 8);

2.2 颜色:Scalar类

//Scalar()具有4个元素的数组,最后一个可以不用,前三个表示BGR
Scalar(a, b, c); //a->B  b->G  c->R

2.3 尺寸:Size类

Size s1 = Size(5, 5); //构造Size宽5,高5,即xxx.width 和 xxx.height 均5  

2.4 矩形:Rect类

//Rect包含左上角点坐标x,y 矩形的宽度width和高度height
//contains(Point) 判断点是否在内
//inside(Rect) 判断矩形是否在内
//tl()返回左上角坐标;
//br()返回右下角坐标;
Rect rect = rect1 & rect2; //交集
Rect rect = rect1 | rect2; //并集
Rect rectShift = rect + point; //平移
Rect rectScale = rect + size;  //缩放

2.5 颜色空间转换:cvtColor()函数

//函数原型 输入图,输出图,颜色转换标识符,通道数(默认保持不变)
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0);
cvtColor(srcImage, dstImage, COLOR_BGR2GRAY); //转换为灰度图

2.6 其他常用的知识点

Matx轻量级的Mat
Vec是Matx派生类,一维的
Range

Matx23f image1; //2*3float类型的Matx
Range:all(); //其实就是MATLAB里的符号
Range(a, b); //就是MATLAB中的a:b

OpenCV中防止溢出的函数
alignPtr、alignSize、allocate、deallocate、fastMalloc、fastFree

include 
计算向量角度函数 fastAtan2
计算立方根函数 cubeRoot
向上取整函数 cvCeil
向下取整函数 cvFloor
四舍五入函数 cvRound
判断自变量是否无穷大 cvIsInf
判断自变量是否不是一个数 cvIsNaN
显示文字函数 getTextSize、cvInitFont、putText
作图相关函数 circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、类LineIterator
填充相关函数 fillConvexPoly、fillPoly
OpenCv中RNG()函数作用为初始化随机数状态生成器

3 基本图形的绘制

  • 绘制直线 Line 函数
  • 绘制椭圆 ellipse 函数
  • 绘制矩形 rectangle 函数
  • 绘制圆 circle 函数
  • 绘制填充的多边形 fillPoly 函数
void DrawEllopse(Mat img, double angele); //绘制椭圆
void DrawFilledCircle(Mat img, Point center); //绘制实心圆
void DrawPolygon(Mat img); //绘制凸多边形
void DrawLine(Mat img, Point start, Point end); //绘制直线

3.1 DrawEllipse()函数的写法

//绘制椭圆
void DrawEllipse(Mat img, double angle)
{
	int thickness = 2;
	int lineType = 8;

	ellipse(img, 
		Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2), //椭圆的中心点坐标
		Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16), //在这个大小的矩形内
		angle, //椭圆旋转角度
		0,
		360,
		Scalar(255, 129, 0), //BGR蓝色
		thickness, //线宽2
		lineType   //线型8,联通线型
	);

}

3.2 DrawFilledCircle()函数的写法

void DrawFilledCircle(Mat img, Point center)
{
	int thinkness = -1;
	int lineType = 8;

	circle(img,
		center,				//圆心
		WINDOW_WIDTH / 32,	//半径
		Scalar(0, 0, 255),  //颜色
		thinkness,			//线宽-1,故为实心圆
		lineType);
}

3.3 DrawPolygo()函数的写法

void DrawPolygon(Mat img)
{
	int lineType = 8;
	//Creat points
	Point rookPoints[1][20];
	rookPoints[0][0] = Point(WINDOW_WIDTH/4, 7* WINDOW_WIDTH/8);
	rookPoints[0][1] = Point(3*WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][2] = Point(3*WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][3] = Point(11*WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][4] = Point(19*WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][5] = Point(3*WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][6] = Point(3*WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][7] = Point(26*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][8] = Point(26*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][9] = Point(22*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][10] = Point(22*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][11] = Point(18*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][12] = Point(18*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][13] = Point(14*WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][14] = Point(14*WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][17] = Point(13*WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][18] = Point(5*WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);

	const Point* ppt[1] = { rookPoints[0] };
	int npt[] = { 20 };

	fillPoly(img,
		ppt,	//多边形顶点集
		npt,	//多边形顶点数目
		1,		//绘制的数量
		Scalar(255, 255, 255), //颜色:白色
		lineType);
}

3.4 DrawLine()函数的绘制

void DrawLine(Mat img, Point start, Point end)
{
	int thinckness = 2;
	int lineType = 8;
	line(img,
		start,
		end,
		Scalar(0, 0, 0),
		thinckness,
		lineType);

}

3.5 main()

#include 
#include 
#include 
#define WINDOW_WIDTH 600
#define WINDOW_NAME1 "【绘制图1】"
#define WINDOW_NAME2 "【绘制图2】"

using namespace std;
using namespace cv;

void DrawEllipse(Mat img, double angele); //绘制椭圆
void DrawFilledCircle(Mat img, Point center); //绘制实心圆
void DrawPolygon(Mat img); //绘制凸多边形
void DrawLine(Mat img, Point start, Point end); //绘制直线

int main()
{
	Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
	Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);

	//1.1 绘制椭圆
	DrawEllipse(atomImage, 90);
	DrawEllipse(atomImage, 0);
	DrawEllipse(atomImage, 45);
	DrawEllipse(atomImage, -45);

	//1.2 绘制圆心
	DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));

	//2.1 绘制多边形
	DrawPolygon(rookImage);

	//2.2 绘制矩形
	rectangle(rookImage,
		Point(0, 7*WINDOW_WIDTH / 8),
		Point(WINDOW_WIDTH, WINDOW_WIDTH),
		Scalar(0, 255, 255),
		-1,
		8);

	//2.3绘制一些线段
	DrawLine(rookImage, Point(0, 15*WINDOW_WIDTH / 16), 
		Point(WINDOW_WIDTH, 15* WINDOW_WIDTH / 16));
	DrawLine(rookImage, Point(WINDOW_WIDTH / 4, 7*WINDOW_WIDTH / 8),
		Point(WINDOW_WIDTH / 4, WINDOW_WIDTH));
	DrawLine(rookImage, Point(WINDOW_WIDTH / 2, 7*WINDOW_WIDTH / 8),
		Point(WINDOW_WIDTH / 2, WINDOW_WIDTH));
	DrawLine(rookImage, Point(3*WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8),
		Point(3*WINDOW_WIDTH / 4, WINDOW_WIDTH));

	//3.1 showImage
	imshow(WINDOW_NAME1, atomImage);
	moveWindow(WINDOW_NAME1, 0, 200);
	imshow(WINDOW_NAME2, rookImage);
	moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);

	waitKey();
	return 0;
}

4 croe组件进阶

  • 感兴趣区域(ROI)
  • 图像混合
  • 分离颜色通道
  • 多通道颜色混合
  • 调整对比度和亮度值
  • 图像傅立叶变换
  • 输入输出XML和YAML文件

4.1 图像中的像素

4.1.1 颜色空间缩减

//0~9 ->0;10~19->10
int divideWith = 10;
uchar table[256];
for(int i = 0; i < 256; i++)
{
  table[i] = divideWith * (i / divideWith);
}

4.1.2 LUT函数:Look up table 操作

	//建立Mat性用于查表
	Mat lookUpTable(1, 256, CV_8U);
	uchar* p = lookUpTable.data;
	for (int i = 0; i < 256; i++)
		p[i] = table[i];
	//调用函数(I是输入,J是输出)
	for (int i = 0; i < times; i++)
		LUT(I, lookUpTable, J);

4.1.3 计时函数

double time0 = static_cast(getTickCount()); //记录起始时间
	// **** 图像操作 ***  //
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "图像操作运行时间为:" << time0 << "秒" << endl; //输出运行时间

4.1.4 访问图像中像素的三种方法

  • 指针访问
  • 迭代器 iterator
  • 动态地址计算

主程序 main
把256种颜色缩减成64种颜色

#include 
#include 
#include 
#include 	//模块
#include 	//图像处理头文件

using namespace std;
using namespace cv;

void colorReduce(Mat & inputImage, Mat & outputImage, int div);

int main()
{
	//1.创建原图并显示
	Mat srcImage = imread("E:/result/MyPic1.png");
	imshow("原始图像", srcImage);
	//2.按照原图参数创建效果图
	Mat dstImage;
	dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
	//3.记录起始时间
	double time0 = static_cast(getTickCount());
	//4.调用颜色空间缩减函数
	colorReduce(srcImage, dstImage, 32);
	//5.计算运行时间并输出
	time0 = ((double)getTickCount() - time0) / getTickFrequency();
	cout << "图像操作运行时间为:" << time0 << "秒" << endl;
	//6.显示效果图
	imshow("效果图", dstImage);


	waitKey();
	return 0;
}

1.指针访问

void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
	outputImage = inputImage.clone(); //复制实参到临时变量
	int rowNumber = outputImage.rows; //行数
	int colNumber = outputImage.cols * outputImage.channels(); //列数*通道数 = 每一行元素个数
	//双循环,遍历所有像素值
	for (int i = 0; i < rowNumber; i++)    //行循环
	{
		uchar * data = outputImage.ptr(i); //获取第i行的首地址
		for (int j = 0; j < colNumber; j++) //列循环
		{
			data[j] = data[j] / div * div + div / 2;
		}
	}
}

2.迭代器访问

void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
	outputImage = inputImage.clone();
	//获取迭代器
	Mat_::iterator it = outputImage.begin(); //初始位置的迭代器
	Mat_::iterator itend = outputImage.end(); //终止位置的迭代器
	//存取彩色图像像素
	for (; it != itend; it++)
	{
		//处理每个像素
		(*it)[0] = (*it)[0] / div * div + div / 2;
		(*it)[1] = (*it)[1] / div * div + div / 2;
		(*it)[2] = (*it)[2] / div * div + div / 2;
	}
}

3 动态地址

void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
	outputImage = inputImage.clone();
	int rowNumber = outputImage.rows;
	int colNumber = outputImage.cols;
	//存取彩色图像像素
	for (int i = 0; i < rowNumber; i++)
	{
		for (int j = 0; j < colNumber; j++)
		{
			outputImage.at(i, j)[0] =
		outputImage.at(i, j)[0] / div * div + div / 2; //蓝色通道
			outputImage.at(i, j)[1] =
		outputImage.at(i, j)[1] / div * div + div / 2; //绿色通道
			outputImage.at(i, j)[2] =
		outputImage.at(i, j)[2] / div * div + div / 2; //红色通道
		}
	}
}

4.2 ROI区域图像叠加&图像混合

4.2.1 感兴趣区域:ROI (region of interesting)

Mat srcImage = imread...
Mat logo = imread...
Mat imageROI = srcImage(Rect(500, 250, logo.cols, logo.rows)); //img的ROI
Mat mask = imread("E:/pic.png", 0); //必须灰度图
logo.copyTo(imageROI, mask);

4.2.2 线性混合操作

计算数组加权和addWeighted()函数

void addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
//输入1,权重1,输入2(与1类型同),权重2,gamma,输出,默认深度同。
//内部操作如下
dst = src1[I]*alpha + src2[I]*beta + gamma;

4.3 分离颜色通道、多通道图像混合

  • split
  • merge

4.3.1 通道分离:split()函数

void split(const Mat& src, Mat* mvbegin);
void split(InputArray m, OutputArrayOfArrays mv);

基础示例

vector channels;
Mat imageBlueChannel, imageGreenChannel, imageRedChannel;
srcImage = imread("MyPic");
//三通道图像转换成3个单通道图像
split(srcImage, channels); //分离颜色通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);

4.3.2 通道合并:merge()函数

void merge(const Mat& mv, size_tcont, OutputArray dst);
void merge(InputArrayOfArrays mv, OutputArray dst);
Mat srcImage = imread("1.png");
Mat imageBlueChannel, imageGreenChannel, imageRedChannel, mergeImage;
//三通道图像转换成3个单通道图像
split(srcImage, channels); //分离颜色通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);
//对拆分的通道进行合并
merge(channels, mergeImage);

4.4 图像对比度、亮度调整

4.4.1 理论依据

访问BGR像素
g(x) = a*f(x) + b

for (int y = 0; y < image.rows; y++)
{
	for (int x = 0; x < image.cols; x++)
	{
		for (int c = 0; c < 3; c++)
		{
			new_image.at(y, x)[c] =
			saturate_cast((g_nContrastValue*0.01)*(image.at(y, x)[c])
			+ g_nBrightValue);
		}
	}
}
//saturate_cast 保护防止溢出

4.4.2 示例程序:图像对比度、亮度调整

#include 
#include 
#include 
#include 

using namespace std;
using namespace cv;

static void on_ContrastAndBright(int, void *); //改变图像对比度和亮度值的回调函数
//static void ShowHelpText(); 

int g_nContrastValue; //对比度
int g_nBrightValue;	  //亮度值
Mat g_srcImage, g_dstImage;

int main()
{
	//【1】读取输入图像
	g_srcImage = imread("E:/result/MyPic.png");
	if (!g_srcImage.data) { cout << "读取g_srcImage error~"; return false; }
	g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
	//【2】设置对比度和亮度
	g_nBrightValue = 80;
	g_nContrastValue = 80;
	//【3】创建效果图窗口
	namedWindow("【效果图窗口】", 1);
	//【4】创建轨迹条
	createTrackbar("对比度", "【效果图窗口】", &g_nContrastValue,
		300, on_ContrastAndBright);
	createTrackbar("亮  度", "【效果图窗口】", &g_nBrightValue,
		200, on_ContrastAndBright);
	//【5】进行回调函数初始化
	on_ContrastAndBright(g_nContrastValue, 0);
	on_ContrastAndBright(g_nBrightValue, 0);
	//【6】按下"q",程序退出
	while(char(waitKey(1)) != 'q'){}
	destroyAllWindows();
	return 0;
}

static void on_ContrastAndBright(int, void *)
{
	cvNamedWindow("【原图窗口】", 1);
	//for计算:g_dstImage(i, j) = a*g_dstImage(i, j) + b
	for (int y = 0; y < g_srcImage.rows; y++)
	{
		for (int x = 0; x < g_srcImage.cols; x++)
		{
			for (int c = 0; c < 3; c++)
			{
				g_dstImage.at(y, x)[c] =
					saturate_cast((g_nContrastValue*0.01)*(g_srcImage.at(y, x)[c])
						+ g_nBrightValue);
			}
		}
	}
	imshow("【原图窗口】", g_srcImage);
	imshow("【效果图窗口】", g_dstImage);
}

4.5 离散傅立叶变换

  • 图像增强和去噪
  • 图像分割
  • 边缘检测、特征提取、图像压缩等

4.5.1 dft()函数

对一维或二维浮点数进行正向和反向离散傅立叶变换

void dft(InputArray src, OutputArray dst, int flages = 0, int nonzeroRows = 0)
//参数3->变换类型(默认正变换);参数4->最好是要处理的行C.rows

dft()函数计算两个二维实矩阵卷积的示例核心片段

void convolveDFT(InputArray A, InputArray B, OutputArray C)
{
	//【1】初始化输出矩阵
	C.create(abs(A.rows - B.rows) + 1, abs(A.cols - B.cols) + 1, A.type());
	Size dftSize;
	//【2】计算DFT变换的尺寸
	dftSize.width = getOptimalDFTSize(A.cols + B.cols - 1);
	dftSize.height = getOptimalDFTSize(A.rows + B.rows - 1);
	//【3】分配临时缓冲区并初始化为零
	Mat tempA(dftSize, A.type(), Scalar::all(0));
	Mat tempB(dftSize, B.type(), Scalar::all(0));
	//【4】分别复制A和B到tempA和tempB的左上角
	Mat roiA(tempA, Rect(0, 0, A.cols, A.rows));
	A.copyTo(roiA);
	Mat roiB(tempB, Rect(0, 0, B.cols, B.rows));
	B.copyTo(roiB);
	//【5】就地操作(in-place),快速傅立叶变换,并将nonzeroRows参数置为非零
	dft(tempA, tempA, 0, A.rows);
	dft(tempB, tempB, 0, B.rows);
	//【6】将得到的频谱相乘,结果放在tempA中
	mulSpectrums(tempA, tempB, tempA, 0);
	//【7】将结果变换为频域,且结果行(result rows)都为非零
	//我们只需要其中的C.rows的第一行,所以采用nonzeroRows == C.rows
	dft(tempA, tempA, DFT_INVERSE + DFT_SCALE, C.rows);
	//【8】将结果复制到C中
	tempA(Rect(0, 0, C.cols, C.rows)).copyTo(C);
	//所有的临时缓冲区将被自动释放,无需收尾工作
}

4.5.2 返回DFT最优尺寸大小:getOptimalDEFSize()函数

返回给定向量尺寸的傅立叶最优尺寸大小
int 类型的 vecsize, 向量尺寸,即图片的rows、cols

int getOptimalDEFSize(int vecsize);

4.5.3 扩充图像边界:copyMakeBorder()函数

void copyMakeBorder(InputArray src, OutputArray dst, int top, int bottom, int left, int right, int borderType, const Scalar& value = Scalar());
  • 参数1:源图
  • 参数2:输出,size为Size(src.cols+left+right, src.rows+top+bottom)
  • 参数3-6:扩充多少像素,例如top=2,…
  • 参数7:边界类型,常用BORDER_CONSTANT
  • 参数8:默认为0

4.5.4 计算二维矢量的幅值:magnitude()函数

void magnitude(InputArray x, InputArray y, OutputArray magnitude
);
  • 浮点型X坐标,实部
  • 浮点型Y坐标,虚部
  • 输出

4.5.5 计算自然对数:log()函数

void log(InputArray src, OutputArray dst);

4.5.6 矩阵归一化:normalize()函数

void normalize(InputArray src, OutputArray dst, double alpha = 1, 
double beta = 0, int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray())
  • 参数3:归一化后的最大值,默认为1
  • 参数4:归一化后的最大值,默认为0
  • 参数5:归一化后的类型 NORM_LNF、NORM_L1、NORM_L2、NORM_MINMAX
  • 参数6:默认-1,输出矩阵和src有同样类型
  • 参数7:可选择掩膜mask操作

5 图像处理:线性&非线性滤波

imgproc组件是Image和Process组合

  • 方框滤波——boxblur函数
  • 均值滤波(邻域平均滤波)——blur函数
  • 高斯滤波——GaussianBlur函数
  • 中值滤波——medianBlur函数
  • 双边滤波——bilateralFilter函数

5.1 方框滤波(box Filter)

方框滤波(box Filter)被封装在一个名为boxblur的函数中,作用是模糊一张图片

void boxFilter(InputArray src,OutputArray dst, int ddepth, Size ksize, Point anchor=Point(-1,-1), boolnormalize=true, int borderType=BORDER_DEFAULT)

均值滤波是方框滤波归一化后的特殊情况。

如果我们要在可变的窗口中计算像素总和,可以使用integral()函数。

5.2 均值滤波 (blur)

破坏了细节

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

第三个参数,Size类型(对Size类型稍后有讲解)的ksize,内核的大小。一般这样写Size( w,h )来表示内核的大小( 其中,w 为像素宽度, h为像素高度)。Size(3,3)就表示3x3的核大小,Size(5,5)就表示5x5的核大小。
第四个参数:默认中心点为目标点。

5.3 高斯滤波(GaussianBlur)

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

【OpenCV】学习笔记(一):基础操作部分_第2张图片

5.4 中值滤波:medianBlur()

medianBlur( InputArray src,OutputArray dst, int ksize )

【OpenCV】学习笔记(一):基础操作部分_第3张图片

5.5 双边滤波:bilateralFilter()

void bilateralFilter(InputArray src, OutputArraydst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT)

【OpenCV】学习笔记(一):基础操作部分_第4张图片

 //载入原图
 Mat image=imread("1.jpg");
 //进行双边滤波操作
 Mat out;
 bilateralFilter( image, out, 25, 25*2, 25/2 );

5.6 【示例】双边滤波+bar

Mat g_srcImage, g_dstImage;

//双边滤波参数值
int g_nBilateralFilterValue = 10;

//轨迹条回调函数 - 
static void on_BilateralFilter(int, void *);




int main()
{
	
	g_srcImage = imread("E:/result/MyPic.png", 1);
	if (!g_srcImage.data)
	{
		cout << "读取错误" << endl;
		return 0;
	}


	//复制原图
	g_dstImage = g_srcImage.clone();

	namedWindow("【原图】双边滤波", 1);
	imshow("【原图】双边滤波", g_srcImage);

	//双边滤波操作
	namedWindow("【效果图】双边滤波", 1);
	createTrackbar("value:", "【效果图】双边滤波", &g_nBilateralFilterValue,
		50, on_BilateralFilter);

	on_BilateralFilter(g_nBilateralFilterValue, 0);

	waitKey(0);
	return 0;
}

//回调函数
static void on_BilateralFilter(int, void *)
{
	bilateralFilter(g_srcImage, g_dstImage, g_nBilateralFilterValue,
		g_nBilateralFilterValue * 2, g_nBilateralFilterValue / 2);

	imshow("【效果图】双边滤波", g_dstImage);
}

6 图像处理:形态学滤波(1)

  • 腐蚀
  • 膨胀

腐蚀和膨胀是对***白色部分(高亮部分***)而言的,不是黑色部分。

膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。

6.1 膨胀:dilate()

void dilate(
	InputArray src,
	OutputArray dst,
	InputArray kernel,
	Point anchor=Point(-1,-1),
	int iterations=1,
	int borderType=BORDER_CONSTANT,
	const Scalar& borderValue=morphologyDefaultBorderValue() 
);

第三个参数,InputArray类型的kernel,膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。

我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。

其中,getStructuringElement函数的第一个参数表示内核的形状,可以选择如下三种形状之一:

  • 矩形: MORPH_RECT
  • 交叉形: MORPH_CROSS
  • 椭圆形: MORPH_ELLIPSE

而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。

我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。

对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。

getStructuringElement函数相关的调用示例代码如下:

 int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
 
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT,
	Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
	Point( g_nStructElementSize, g_nStructElementSize ));

【膨胀核心代码】

//载入原图 
Mat image = imread("1.jpg");
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//进行膨胀操作
dilate(image, out, element);

6.2 腐蚀:erode()

参数列表与6.1相同

【腐蚀核心代码】

//载入原图 
Mat image = imread("1.jpg");
//获取自定义核
Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));
Mat out;
//进行腐蚀操作
erode(image,out, element);

7 图像处理:形态学滤波(2)

7.1 开运算:Opening Operation

先腐蚀后膨胀的过程
开运算可以用来消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。

7.2 闭运算:Closing Operation

先膨胀后腐蚀的过程
排除小型黑洞(黑色区域)

7.3 形态学梯度:MorphologicalGradient

膨胀图与腐蚀图之差
对二值图像进行这一操作可以将团块(blob)的边缘突出出来

7.4 顶帽:Top Hat

原图像与“开运算“的结果图之差
顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

7.5 黑帽:Black Hat

”闭运算“的结果图与原图像之差
分离比邻近点暗一些的斑块

7.6 核心API函数:morphologyEx()

void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArraykernel,
Pointanchor=Point(-1,-1),
intiterations=1,
intborderType=BORDER_CONSTANT,
constScalar& borderValue=morphologyDefaultBorderValue() );

【OpenCV】学习笔记(一):基础操作部分_第5张图片

MORPH_OPEN – 开运算(Opening operation)

MORPH_CLOSE – 闭运算(Closing operation)

MORPH_GRADIENT -形态学梯度(Morphological gradient)

MORPH_TOPHAT - “顶帽”(“Top hat”)

MORPH_BLACKHAT - “黑帽”(“Black hat“)

MORPH_ERODE - 腐蚀

MORPH_DILATE - 膨胀

图片

getS参数一:

  • 矩形: MORPH_RECT
  • 交叉形: MORPH_CROSS
  • 椭圆形: MORPH_ELLIPSE

【OpenCV】学习笔记(一):基础操作部分_第6张图片

【getS示例】

int g_nStructElementSize = 3; //结构元素(内核矩阵)的尺寸
 
//获取自定义核
Mat element =getStructuringElement(MORPH_RECT,
       Size(2*g_nStructElementSize+1,2*g_nStructElementSize+1),
       Point(g_nStructElementSize, g_nStructElementSize ));
//定义核[常用版本]
	Mat element = getStructuringElement(MORPH_RECT, Size(15, 15));

【OpenCV】学习笔记(一):基础操作部分_第7张图片

8 图像处理:一些算法

8.1 漫水填充算法:floodFill()

用特定的颜色填充联通区域
标记或分离图像的一部分以便对其进行进一步处理或分析

int floodFill(InputOutputArray image, InputOutputArray mask, Point seedPoint,Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )

8.2 图像金字塔与图像尺寸缩放

* **resize()  缩放**
* **pyrUp()  向上采样(高斯金字塔) -- 尺寸加倍**
* **pyrDown()  向下采样(拉普拉斯金字塔)**

resize() 函数

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

INTER_NEAREST - 最近邻插值

INTER_LINEAR - 线性插值(默认值)

INTER_AREA - 区域插值(利用像素区域关系的重采样插值)

INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)

INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值)

若要缩小图像,一般情况下最好用CV_INTER_AREA来插值,

而若要放大图像,一般情况下最好用INTER_CUBIC(效率不高,慢,不推荐使用)或INTER_LINEAR(效率较高,速度较快,推荐使用)。

【方式一:设定目标尺寸】

Mat dst=Mat::zeros(512 ,512, CV_8UC3 );//新建一张512x512尺寸的图片
Mat src=imread(“1.jpg”);
//显式指定dsize=dst.size(),那么fx和fy会其计算出来,不用额外指定。
resize(src, dst, dst.size());

【方式二:设定放缩因子】

Mat dst;
Mat src=imread(“1.jpg”)
 //指定fx和fy,让函数计算出目标图像的大小。
resize(src, dst, Size(), 0.5, 0.5);  //x,y  缩放0.5

pyrUp() 向上采样(高斯金字塔) – 尺寸放大

void pyrUp(InputArray src, OutputArraydst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT )

【OpenCV】学习笔记(一):基础操作部分_第8张图片

pyrDown() 向下采样(拉普拉斯金字塔) – 尺寸缩小

void pyrDown(InputArray src,OutputArray dst, const Size& dstsize=Size(), int borderType=BORDER_DEFAULT)

【OpenCV】学习笔记(一):基础操作部分_第9张图片

8.3 阈值化

根据灰度差异,选区出目标

  • Threshold() 固定阈值操作
  • adaptiveThreshold() 自适应阈值
    Threshold() 固定阈值操作
    典型应用于灰度图处理,得到二值图像
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type)

参数三:具体的阈值。

adaptiveThreshold() 自适应阈值

void adaptThresholde(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)

9 图像变换:边缘检测

* **Canny 算子**
* **Sobel 算子**
* **Laplacian 算子**
* **Scharr 滤波器**

9.1 Canny() 函数

void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
//[示例]
Mat src = imread("1.jpg");  //工程目录下应该有一张名为1.jpg的素材图
Canny(src, src, 3, 9, 3);
imshow("【效果图】Canny边缘检测", src);

9.2 sobel 算子

Sobel 算子是一个主要用作边缘检测的离散微分算子 (discrete differentiation operator)。 Sobel算子结合了高斯平滑和微分求导,用来计算图像灰度函数的近似梯度。
在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

void Sobel (
InputArray src,//输入图
 OutputArray dst,//输出图
 int ddepth,//输出图像的深度
 int dx,
 int dy,
 int ksize=3,
 double scale=1,
 double delta=0,
 int borderType=BORDER_DEFAULT );

【OpenCV】学习笔记(一):基础操作部分_第10张图片

9.3 Laplacian() 函数

void Laplacian(InputArray src,OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, intborderType=BORDER_DEFAULT );

【OpenCV】学习笔记(一):基础操作部分_第11张图片

9.4 scharr 滤波器

使用Scharr滤波器运算符计算x或y方向的图像差分。
其实它的参数变量和Sobel基本上是一样的,除了没有ksize核的大小。

void Scharr(
InputArray src, //源图
 OutputArray dst, //目标图
 int ddepth,//图像深度
 int dx,// x方向上的差分阶数
 int dy,//y方向上的差分阶数
 double scale=1,//缩放因子
 double delta=0,// delta值
 intborderType=BORDER_DEFAULT )// 边界模式

10 图像变换:霍夫变换

10.1 霍夫线变换

<1>标准霍夫变换(StandardHough Transform,SHT),由HoughLines函数调用。

<2>多尺度霍夫变换(Multi-ScaleHough Transform,MSHT),由HoughLines函数调用。

<3>累计概率霍夫变换(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函数调用。

10.2 标准霍夫变换:HoughLines()

void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )

【OpenCV】学习笔记(一):基础操作部分_第12张图片

10.3 累计概率霍夫变换:HoughLinesP()

void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )

【OpenCV】学习笔记(一):基础操作部分_第13张图片

10.4 霍夫圆变换:HoughCircles()

oughCircles函数可以利用霍夫变换算法检测出灰度图中的圆

void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100,double param2=100, int minRadius=0, int maxRadius=0 )

【OpenCV】学习笔记(一):基础操作部分_第14张图片

11 图像变换:其他知识

11.1 重映射:remap()

镜像、翻转

11.2 仿射变换

* **旋转  rotation(线性变换)**
* **平移  translation(向量加)**
* **缩放  scale(线性变换)**

11.3 仿射变换:WarpAffine()函数

【OpenCV】学习笔记(一):基础操作部分_第15张图片

11.4 计算二维旋转变换矩阵:getRotationMatrix2D()函数

【OpenCV】学习笔记(一):基础操作部分_第16张图片

11.5 直方图均衡化:equalizeHist()

void equalizeHist(InputArray src, OutputArray dst)

12 图像轮廓与图像分割修复

12.1 寻找轮廓:findContours()

用于二值图像中寻找轮廓

vector> contours; //轮廓
vector hierarchy; //层次结构

12.2 绘制轮廓:drawContours()

绘制外部/内部轮廓

12.3 寻找凸包:convexHull()

12.4 使用多边形轮廓包围

12.4.1 返回外部矩形边界:boundingRect()

//返回Rect

12.4.2 寻找最小包围矩形:minAreaRect()

//返回RotatedRect

12.4.3 寻找最小包围圆形:minEnclosingCircle()

12.4.4 椭圆拟合二维点集:fitEllipse()

//返回RotatedRect

12.4.5 逼近多边形曲线:approxPolyDP()

12.5 图像的矩

12.5.1 矩的计算:moments()

//返回Moments

12.5.2 轮廓面积:contourArea()

//返回double

12.5.3 轮廓长度:arcLength()

//返回double

12.6 分水岭算法:watershed()

12.7 图像修补:inpaint()

13 直方图与匹配

13.1 计算直方图:calcHist()

13.2 找寻最值:minMaxLoc()

13.3 对比直方图:compareHist()

13.4 计算反向投影:calcBackProject()

13.5 通道复制:mixChannels()

13.6 模板匹配:matchTemplate()

14 深入feature2d组件

14.1 harris角点检测:cornerHarris()

14.2 Shi-Tomasi角点检测:确定强角点:goodFeaturesToTrack()

14.3 寻找亚像素点:cornerSubPix()

15 特征检测与匹配

  • FAST—FastFeatureDetector
  • STAR—StarFeatureDetector
  • SIFT—SIFT
  • SURF—SURF
  • ORB—ORB
  • MSER—MSER
  • GFTT—GoodFeatureToTrackDetector
  • HARRIS—GoodFeatureToTrackDetector(配合Harris detector操作)
  • Dense—DenseFeatureDetector
  • SimpleBlob—SimpleBlobDetector

15.1 绘制关键点:drawKeyPoints()

16 xfeatures2d

16.1 SURF

【OpenCV】学习笔记(一):基础操作部分_第17张图片

#include 
#include 
#include 

using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;

int main(int argc, char** argv)
{
	Mat src = imread("1.jpg", IMREAD_GRAYSCALE);
	if (src.empty()) { cout << "load error!" << endl; return -1; }

	namedWindow("input image", WINDOW_AUTOSIZE);
	imshow("input image", src);

	//SURF特征检测
	int minHessian = 400;
	Ptr detector = SURF::create(minHessian);
	vector keypoints;
	detector->detect(src, keypoints, Mat());

	//绘制关键点
	Mat keypoint_img;
	drawKeypoints(src, keypoints, keypoint_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	imshow("keypoints", keypoint_img);


	waitKey(0);
	return 0;
}

16.2 SIFT

【OpenCV】学习笔记(一):基础操作部分_第18张图片

#include 
#include 
#include 

using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;


int main(int argc, char** argv)
{
	Mat src = imread("1.jpg", IMREAD_GRAYSCALE);
	if (src.empty()) { cout << "load error!" << endl; return -1; }
	namedWindow("input image", WINDOW_AUTOSIZE);
	imshow("input image", src);

	//SIF特征检测
	int numFeatures = 100;
	Ptr detector = SIFT::create(numFeatures);
	vector keypoints;
	detector->detect(src, keypoints, Mat());

	//绘制
	Mat keypoints_img;
	drawKeypoints(src, keypoints, keypoints_img, Scalar::all(-1), DrawMatchesFlags::DEFAULT);
	namedWindow("SIFT - OUT", WINDOW_AUTOSIZE);
	imshow("SIFT - OUT", keypoints_img);

	waitKey(0);
	return 0;
}

16.3 HOG

【OpenCV】学习笔记(一):基础操作部分_第19张图片

16.4 Haar

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Oa46OTck-1582962531761)(https://uploader.shimo.im/f/bGxaBSZQO1oCcd8A.png!thumbnail)]

16.5 匹配

16.5.1 Burte-Force 暴力匹配

BFMacher matcher(NORM_L2);

16.5.2 FLANN 快速特征匹配

//FlannBasedMatcher matcher;
FlannBasedMatcher matcher(new flann::LshIndexParams(20, 10, 2));

16.5.3 对象的形变与位置变换

图片

16.5.4 AKAZE局部特征提取

Ptr detector = AKAZE::create();

【OpenCV】学习笔记(一):基础操作部分_第20张图片

16.5.5 BRISK

Ptr detector = BRISK::create();
Ptr detector = BRISK::create();
vector keypoints;
detector->detect(srcImage1, keypoints, Mat());

Mat resultImg;
drawKeypoints(srcImage1, keypoints, resultImg);
imshow("resultImg", resultImg);

16.6 级联检测器 - 人脸检测

#include 
#include 
#include 
#include 


using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;

int main(int argc, char** argv)
{

	String cascadeFilePath = "D:/opencv/build/etc/haarcascades/haarcascade_frontalface_alt.xml";
	CascadeClassifier face_cascade;
	if (!face_cascade.load(cascadeFilePath))
	{
		cout << "could not load haar dara.." << endl;
		return -1;
	}
	Mat src, src_gray;
	src = imread("E:/result/666.jpg", IMREAD_COLOR);
	cvtColor(src, src_gray, COLOR_BGR2GRAY);
	//imshow("原图", src);

	equalizeHist(src_gray, src_gray);
	vector faces;
	face_cascade.detectMultiScale(src_gray, faces, 1.1, 2, 0);
	for (size_t t = 0; t < faces.size(); t++)
	{
		rectangle(src, faces[t], Scalar(0, 0, 255), 2, 8, 0);
	}
	imshow("faces", src);

	waitKey(0);
	return 0;
}

你可能感兴趣的:(OpenCV)