基于OPENCV的C++项目开发常用函数及代码

文章目录

  • C++ 书写Hello World
  • 滚动条函数
  • 键盘操作
  • 鼠标操作
  • 头文件
  • 读取图像
    • 色彩空间转换
    • 颜色表操作
    • 通道分离
  • 彩色图像阈值分割
  • 创建图像
  • 计时
  • 读取文件夹所有图像
    • 遍历图像
  • 新建图像
  • 加载图像并判断是否读入
  • 转为灰度图
  • 遍历图像像素值/像素值读写
  • 像素值算数操作
  • 像素逻辑操作
  • 几何形状绘制
    • 绘制矩形
    • 随机颜色
    • 绘制多边形
  • 格式转换
  • 滤波算法
    • 双边滤波
  • 增强算法
  • 阈值分割
  • 模板匹配
  • 边缘检测
  • 直线检测
  • 形态学操作
  • 图像运算
  • 像素值统计
    • 求最大值、最小值
    • 求均值和方差
    • 像素归一化
  • 距离变换
  • 寻找轮廓
    • 得到矩形中心
  • 图像上绘制文字
  • 计算图像直方图
    • 直方图均衡化
  • 图像卷积
    • 高斯双边模糊
  • 提取图像骨架
  • ROI
  • 图像缩放
  • 图像翻转
  • 图像旋转
  • 求直线交点
  • 区域生长
  • 坐标变换
  • 边界填充
    • 自己写的没有自带的好
  • 漫水填充
  • 滚轮放大和缩小
  • 鼠标绘制矩形
  • 创建文件夹
  • 创建txt
  • 视频处理
    • 读取摄像头
  • 深度学习


君子生非异也,善假于物也

哈哈,从大一就接触C++,后面自己看书、看视频自学,趁着年轻学东西就很快,自认为已经读过超10万行代码了,也写了很多很多了,之前一直整理在我的私密文章里,用过什么就加进去什么,看了其他同学的博客,忍不住拿出来分享一下,去掉一些带有自己算法的相关程序,仅分享一些基于opencv做项目开发的代码
PS:目前的研究方向是基于深度学习做图像异常检测,欢迎大家互相交流学习~

C++ 书写Hello World

#include 
 
using namespace std;

int main()
{
	cout << "Hello World" << endl;

	system("pause");
	return 0;
}


滚动条函数

createTrackbar(
	"滚动条名称",
	“窗口名称”,
	&变量,
	最大范围,
	回调函数
);
int createTrackbar(const string& trackbarname, const string&winname, int* value,  int count ,TrackbarCallback onChange = 0,  void* userdata = 0);           

参数1:轨迹条名字

参数2:窗口名字

参数3:滑块初始位置

参数4:表示滑块达到最大位置的值

参数5:默认值为0,指向回调函数

参数6:默认值为0,用户传给回调函数的数据值
#include
#include

using namespace std;
using namespace cv;

void on_track(int lightness, void* usrdata)
{
	Mat src = *(Mat*)(usrdata);
	
	Scalar m = Scalar(lightness, lightness, lightness);
	Mat dst;
	add(src, m, dst);
	imshow("亮度", dst);
}

int main()
{
	Mat src = imread("lena.png");
	int lightness = 50;
	int max_value = 100;
	Mat dst = Mat::zeros(src.size(), src.type());
	Mat m = Mat::zeros(src.size(), src.type());
	namedWindow("亮度", WINDOW_FREERATIO);
	imshow("亮度", src);
	createTrackbar("scroll bar", "亮度", &lightness, max_value, on_track, &src);

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

基于OPENCV的C++项目开发常用函数及代码_第1张图片

键盘操作

while(true)
{
	int c = waitKey(100); //视频分析 waitKey(1)
	if(c == 27)
	{
		break;
	}
	if( c == 49)
	{
	}
}

鼠标操作

基于OPENCV的C++项目开发常用函数及代码_第2张图片

void on_draw(int event, int x, int y, int flags, void *userdata)
{
}
#include 

using namespace std;
using namespace cv;



struct MouseParam
{
	Mat img; //用于画好一个后显示
	Mat imgZoomBackup; //用于zoom的还原备份
	Mat imgTmp; //用于实时显示
	Mat imgBackup; //清空,显示最初的图
	
	Point pt1;
	Point pt2;
	Rect box;
	bool mouseLflag;

};

void draw_rectangle(Mat &img, const Point &pt1, const Point &pt2)
{
	//rectangle函数参数: 图片, 左上角, 右下角, 颜色, 线条粗细, 线条类型,点类型    
	rectangle(img, pt1, pt2, Scalar(0, 255, 0), 1, 0, 0);
}




void on_mouse(int event, int x, int y, int flags, void* param)
{
	MouseParam *par = (MouseParam*)param;
	Point pt;
	pt.x = x;
	pt.y = y;
	

	if (event == EVENT_RBUTTONDOWN) //按下右键,重画
	{
		par->img = par->imgBackup.clone();
	}
	else if (event == EVENT_LBUTTONDOWN)
	{
		par->box = Rect2d(x, y, 0, 0);
		par->pt1 = pt;
		//par->pt2 = pt;
		par->mouseLflag = true;
	}
	else if (event == CV_EVENT_MOUSEMOVE && flags == CV_EVENT_FLAG_LBUTTON)
	{
		par->pt2 = pt;
		par->box.width = x - par->box.x;
		par->box.height = y - par->box.y;
	}
	else if (event == CV_EVENT_LBUTTONUP)
	{
		par->pt2 = pt;
		//draw_rectangle(par->imgTmp, par->pt1, par->pt2);
		//par->imgZoomBackup = par->img.clone();
		par->mouseLflag = false;

		if (par->box.width < 0)
		{
			par->box.x += par->box.width;
			par->box.width *= -1;
		}
		if(par->box.height < 0)
		{
			par->box.y += par->box.height;
			par->box.height *= -1;
		}
		rectangle(par->img, par->box, Scalar(0, 0, 255), 1, 1, 0);
	}
	else if (event == CV_EVENT_MOUSEMOVE)
	{
		par->pt1 = pt;
		//par->img.copyTo(par->imgZoomBackup);

		//-1*
	}
	
}

int main()
{
	//Mat img(512, 512, CV_8UC3, Scalar::all(255));
	Mat img = imread("D:\\program\\data1\\2.png");
	MouseParam mouseParam;
	mouseParam.img = img.clone();
	mouseParam.imgBackup = img.clone();
	mouseParam.imgZoomBackup = img.clone();
	mouseParam.mouseLflag = false;
	
	namedWindow("Box Example");
	//namedWindow("Box Example1");
	setMouseCallback("Box Example", on_mouse, &mouseParam);

	//imshow(WINNAME, mouseParam.imgTmp);

	int key;
	while (1)
	{
		mouseParam.imgTmp = mouseParam.img.clone();
		
		if (mouseParam.mouseLflag == true)
		{
			rectangle(mouseParam.imgTmp, mouseParam.box, Scalar(255, 0, 255), 1, 1, 0);

		}
			//draw_rectangle(mouseParam.imgTmp, mouseParam.pt1, mouseParam.pt2);
		imshow("Box Example", mouseParam.imgTmp);
		//imshow("Box Example1", mouseParam.img);
		key = waitKey(40);
		
	}

	return 0;
}

头文件

#prama once

#include

读取图像

assert = 0,多半是图像路径不对

Mat src = imread("D:/test/1.png");
if(src.empty())
{
	printf("could not load image...\n");
	return -1;
}
namedWindow("1", WINDOW_FREERATIO);
imshow("1", src);
waitKey(0);
destroyAllWindows();
return 0;

色彩空间转换

HSV 空间更擅长选泽处于不同光线下的区域。 RGB-HSV (色度、饱和度、明度)
色调H 用角度度量 取值范围一般为0~360度 从红色开始按逆时针方向算 红色为0度 绿色为120度 蓝色为240度 它们的互补色为黄色60度 青色180度 品红300度。
饱和度S表示颜色接近光谱色的程度 一种颜色可以看作是某种光谱色与白色混合的结果 其中光谱色占的比例越大 颜色接近光谱色的程度就越高 颜色的饱和度就越高 饱和度高 颜色则深而艳 光谱色的白光成分为0 饱和度达到最高 通常取值范围为0%~100% 值越大颜色越饱和。
明度V表示颜色明亮的程度 对于光源色 明度值与发光体的光亮度有关 对于物体色 此值和物体的透射比或反射比有关 通常取值范围为0%(黑)到100%。

基于OPENCV的C++项目开发常用函数及代码_第3张图片

H - Hue            if Color1 is Max  H = ( Color2 - Color3 ) / ( Max - Min )  
S - Saturation     S = (Max - Min ) / Max;  
V - Value        V = max of either R,G or B

cvtColor(image, gray, COLOR_BGR2GRAY);

颜色表操作

opencv支持20多种的颜色转换
index%19 不管多少种,始终在0~18种

int colormap[] = 

applyColorMap() 
 //灰度图像伪彩色增强,彩色图像颜色变换

通道分离

from_to:指定被复制通道与要复制到的位置组成的索引对
vector<Mat> mv;
split(image, mv); //通道分离
merge(mv, dst); //通道合并
int from_to[] = {0,2,1,1,2,0];
mixChannels(&image, &dst, 1, from_to, 3); // 3 索引对的数目

彩色图像阈值分割

inRange(hsv, Scalar(35,43,46), Scalar(77,255,255), mask);

参数1:输入要处理的图像,可以为单通道或多通道。
参数2:包含下边界的数组或标量。
参数3:包含上边界数组或标量。
参数4:输出图像,与输入图像src 尺寸相同且为CV_8U 类型。

image.copyTo(dst, mask); // mask中不为0的像素拷贝过来

创建图像

Mat 基本结构
基于OPENCV的C++项目开发常用函数及代码_第4张图片

m1 = image.clone();
image.copyTo(m2);
Mat dst = Mat::zeros(Size(m,n), CV_8UC3);
dst = Scalar(127,127,127);

计时

#include 
clock_t startTime, endTime;
startTime = clock();//计时开始


endTime = clock();//计时结束
cout << "The run time is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;


读取文件夹所有图像




#include      //for _finddata_t、 _findnext

std::vector<std::string> getFilesList(std::string dir)
{
	std::vector<std::string> allPath;
	// 在目录后面加上"\\*.*"进行第一次搜索
	std::string dir2 = dir + "\\*.*";

	intptr_t handle;
	_finddata_t findData;

	handle = _findfirst(dir2.c_str(), &findData);
	if (handle == -1) {// 检查是否成功
		std::cout << "can not found the file ... " << std::endl;
		return allPath;
	}
	do
	{
		if (findData.attrib & _A_SUBDIR) 是否含有子目录
		{
			//若该子目录为"."或"..",则进行下一次循环,否则输出子目录名,并进入下一次搜索
			if (strcmp(findData.name, ".") == 0 || strcmp(findData.name, "..") == 0)
			{
				continue;
			}
			// 在目录后面加上"\\"和搜索到的目录名进行下一次搜索
			std::string dirNew = dir + "\\" + findData.name;
			std::vector<std::string> tempPath = getFilesList(dirNew);
			allPath.insert(allPath.end(), tempPath.begin(), tempPath.end());
		}
		else //不是子目录,即是文件,则输出文件名和文件的大小
		{
			std::string filePath = dir + "\\" + findData.name;

			std::string fileFormat = filePath.substr(filePath.find_last_of('.') + 1);//获取文件后缀
			if (fileFormat == "jpg")
			{
				//cout << findData.name << endl;
				allPath.push_back(filePath);
			}
			//allPath.push_back(filePath);
			//cout << filePath << "\t" << findData.size << " bytes.\n";
		}
	} while (_findnext(handle, &findData) == 0);
	_findclose(handle);    // 关闭搜索句柄
	return allPath;
}

遍历图像

std::string pattern_jpg = "D:\\program\\imagebases\\*.jpg";

std::vector<cv::String> image_files;
cv::glob(pattern_jpg, image_files);
if (image_files.size() == 0) {
	std::cout << "No image files[jpg]" << std::endl;
	return 0;
}

for (unsigned int i = 0; i < image_files.size(); i++)
{
	Mat template_image = cv::imread(image_files[i]);
}

新建图像

dstImg.create(search_image.dims, search_image.size, CV_32FC3);

加载图像并判断是否读入

	srcImg = imread("flower.jpg");
	if (srcImg.empty())
	{
		printf("could not load image...\n");
		return -1;
	}

转为灰度图


if (src.channels() == 3)
{
	cvtColor(src, src_gray, CV_BGR2GRAY);// 
	// flag >0,三通道,flag = 0,单通道;flag <0, 不转换
}

遍历图像像素值/像素值读写

//像素值的读写
// 彩色图
for (int i = 0; i < src.rows; i++)
{
	for (int j = 0; j < src.cols; j++)
	{
		//Vec3b :长度为3的uchar类型的vector向量
		Vec3b bgr = src.at<Vec3b>(i,j);
		src.at<Vec3b>(i, j)[0] = 0;
		src.at<Vec3b>(i, j)[1] = 0;
		src.at<Vec3b>(i, j)[2] = 0;
		
		
	}
}
// 灰度图
for (int i = 0; i < graySrc.rows; i++)
{
	for (int j = 0; j < graySrc.cols; j++)
	{
		graySrc.at<uchar>(i, j) = 255;


	}
}
//使用迭代器
	MatIterator_<uchar> grayit, grayend;
	for ( grayit = graySrc.begin<uchar>(), grayend = graySrc.end<uchar>(); grayit != grayend; grayit++)
	{
		*grayit = 0;

	}

	MatIterator_<Vec3b> colorit, colorend;
	for (colorit = src.begin<Vec3b>(), colorend =
		src.end<Vec3b>(); colorit != colorend; ++colorit)
	{
		(*colorit)[0] = rand() % 255; //Blue
		(*colorit)[1] = rand() % 255; //Green
		(*colorit)[2] = rand() % 255; //Red
	}

//指针 编译通过,执行出错
	for (int i = 0; i < graySrc.rows; i++)
	{
		//获取第I行首像素指针
		uchar *p = graySrc.ptr<uchar>(i);
		for (int j = 0; j < graySrc.cols; j++)
		{
			p[j] = 255;
		}

	}
	for (int i = 0; i < src.rows; ++i)
	{
		//获取第 i 行首像素指针
		Vec3b * p = src.ptr<Vec3b>(i);
		for (int j = 0; j < src.cols; ++j)
		{
			p[j][0] = i % 255; //Blue
			p[j][1] = j % 255; //Green
			p[j][2] = 0; //Red
		}
	}
//Mat : row col channel dim
	//flag 头文件  opencv: BGR

	/*  data step[0]:行的数据大小
	step[1]: 每个元素的大小
	*/

	for (int j = 0; j < graySrc.cols; j++)
	{
		for (int i = 0; i < graySrc.rows; i++)
		{

			*(graySrc.data + graySrc.step[0] * i + graySrc.step[1] * j) = 255;
		}

	}

	for (int j = 0; j < src.cols; j++)
	{
		for (int i = 0; i < src.rows; i++)
		{
			*(src.data + src.step[0] * i + src.step[1] * j  ) = 255;
			*(src.data + src.step[0] * i + src.step[1] * j + src.elemSize1()) = 255;
			*(src.data + src.step[0] * i + src.step[1] * j + src.elemSize1() * 2) = 255;
		}

	}
	//Mat_类 :声明时指明数据类型,使用矩阵元素读写
	Mat_<uchar> img = (Mat_<uchar>&)graySrc;
	for (int i = 0; i < img.rows; i++)
	{
		
		for (int j = 0; j < img.cols; j++)
		{
			img(i, j) = 255;
		}

	}

像素值算数操作

Scalar是一个由长度为4的数组作为元素构成的结构体
溢出保护

// 
addWeighted(image, 0.7,bg,  0.3, 0, dst);
// 加法
dst = image + Scalar(50,50,50);
add(image, m, dst);
// 减法
substract(image, m ,dst);
// 除法
divide(image, m ,dst);
// 乘法
multipy(image, m2, dst);
 // 当运算完之后,结果为负,则转为0,结果超出255,则为255
saturate_cast<uchar>(p1 + p2);

像素逻辑操作

bitwise_and();
bitwise_or();
bitwise_not(); // 非操作
bitwise_xor(); //异或

几何形状绘制

绘制矩形

rectangle(img, Rect(100,100,80,80), Scalar(255,255,0), -1, LINE_8, 0);
/*rect: x, y, w, h
 -1  填充  1/2 线宽
LINE_AA:反锯齿
速度:LINE_8>LINE_AA
美观:LINE_AA>LINE_8
*/

circle(image, Point(300,200), 15, Scalar(255,255,0), -1, LINE_8, 0);

// 中心点 半径

line(bg, Point(100,100), Point(200,200), Scalar(255,255,0), 4, LINE_8, 0);

// 椭圆
RotatedRect rrt;
rrt.center = Point(200,200);
rrt.size = Size(100,200);
rrt.angle = 90.0;
ellipse(image, rrt, Scalar(0,255,255),2,8);

随机颜色

RNG rng(12345); 随机数产生器
rng.uniform(1, 3); 在[1,3)区间,随机生成一个整数

绘制多边形

true :是否闭合

polylines(img, pts, true, Scalar(0,255,255),2,8,0); //不能进行填充

fillPoly(); //填充

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

格式转换

// 转换格式
img_dist.convertTo(img_dist, CV_8UC1);

滤波算法

//高斯滤波
GaussianBlur(src_gray, src_gray, Size(5, 5), 3);
cv::GaussianBlur(bilater_img, guass_img, cv::Size(5, 5), 3, 3, cv::BORDER_DEFAULT);

双边滤波

int d = 20;
int sigmaColor = 20;
int sigmaSpace = 20;
cv::Mat bilater_img;
bilateralFilter(img, bilater_img, d, sigmaColor, sigmaSpace);

增强算法

equalizeHist(diff_img, equ_img);
// 伪彩色
applyColorMap(equ_img, im_color, COLORMAP_JET);

阈值分割

threshold(src_diff, src_r, 35, 255, CV_THRESH_BINARY);

threshold(diff_img, bin_img, 0, 255, THRESH_BINARY | THRESH_OTSU);
//自适应阈值分割
int blockSize = 31;
int constValue = 7;
cv::Mat local;
cv::adaptiveThreshold(src_open, local, 255, ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, blockSize, constValue);

模板匹配

matchTemplate(search_image, template_image, dstImg, TM_SQDIFF); //归一化互相关

边缘检测


cv::Mat grad_x, grad_y;
cv::Sobel(img, grad_x, CV_8U, 0, 1, 3, 1, 1, BORDER_DEFAULT);
cv::Sobel(img, grad_y, CV_8U, 1, 0, 3, 1, 1, BORDER_DEFAULT);
cv::Canny(dstImg, img_edge, 70, 150, 3);

直线检测

cv::HoughLinesP(img_dist, lines, 1, CV_PI / 180, 80, 100, 50);

形态学操作


erode(src_gray, src_gray, mask);
dilate(src_gray, src_open, mask);
Mat kernel = getStructuringElement(MORPH_RECT, Size(35, 35), Point(-1, -1));  // MORPH_ELLIPSE
//morphologyEx(src_gray, src_open, CV_MOP_CLOSE, kernel);
																				  // Mat src_open;
morphologyEx(src_gray, src_open, CV_MOP_OPEN, kernel);

// 底帽变换
morphologyEx(src_guass, src_h, CV_MOP_BLACKHAT, kernel);

图像运算

//图像相减
subtract(src_gray, src_open, src_diff);

cv::absdiff(img, bilater_img, diff_img);
//或运算
bitwise_or(res, dst, res);

像素值统计

求最大值、最小值

double minv, maxv;
Point pt_min, pt_max;
minMaxLoc(imgn, &minv, &maxv, &pt_min, &pt_max);

求均值和方差

Mat mean, stddev;
meanStdDev(image, mean,stddev);

像素归一化

四种归一化方法:

  1. NORM_MINMAX
  2. MORM_INF
  3. MORM_L1
  4. MORN_L2

如果图像数据类型是32F(32位浮点数)或64F(64位浮点数),则imshow函数内部会自动将每个像素值乘以255并显示,即将原图像素值的范围由[01]映射到[0255](注意:原图像素值必须要归一化)


image.convertTo(dst, CV_32F);

// 像素类型转换后要记得归一化到0~1才能显示
normalize()

距离变换

cv::distanceTransform(img, img_dist, cv::DIST_L1, cv::DIST_MASK_PRECISE);

寻找轮廓

vector<vector<Point>> bin_contours;
vector<Vec4i> bin_hireachy;
findContours(img_roi, bin_contours, bin_hireachy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE, Point());

// 删除面积较小的轮廓
contours.erase(std::remove_if(contours.begin(), contours.end(),
		[](const std::vector<cv::Point>& c) {return cv::contourArea(c) < 100; }), contours.end());
// 绘制
for (int i = 0; i < contours.size(); i++)
{
	//绘制轮廓  
	cv::drawContours(res, contours, i, cv::Scalar(255, 0, 0), 1, 8, hierarchy);
}


得到矩形中心

Point2f getRectCenter(Rect rect)
{
	Point2f center;
	center.x = rect.x + rect.width * 0.5;
	center.y = rect.y + rect.height * 0.5;
	return center;
}

图像上绘制文字

cv::Point origin;
origin.x = center[i].x + 3;
origin.y = center[i].y - 3;
putText(src, diameter, origin, CV_FONT_HERSHEY_SCRIPT_SIMPLEX, 0.55, Scalar(0, 255, 0));

计算图像直方图

// 直方图统计

float hist[256] = {0};
for (int i = 0; i < src_gray.rows; i++)
{
	for (int j = 0; j < src_gray.cols; j++)
	{
		
		int val = src_gray.at<uchar>(i, j);
		if (val > 0)
		{
			hist[val]++;
		}
		
	}
}

float hist_prob[256] = { 0 };
int hist_sum = 0;

float hist_sum = accumulate(histm, histm + 256, 0); // 求和

//cout << hist_sum << endl;
for (int k = 0; k < 256; k++)
{
	hist_prob[k] = float(histm[k]) / float(hist_sum);
	//cout << hist_prob[k] << endl;

}
//分通道显示
vector<Mat> bgr_planes;
split(src, bgr_planes);

//设定像素取值范围
int bins= 256;
float range[] = { 0,256 };
const float *ranges= { range };

calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges):
  • 二维直方图

直方图均衡化

histogram
equalizeHist(gray, hist);

图像卷积

blur(image, dst, Size(3,3), Point(-1,-1));
//  Size(3,3)卷积核大小

高斯双边模糊

bilaterFilter(image, dst, 0, 100,10);

提取图像骨架

//提取图像的骨架
void ImgThin(cv::Mat src, int maxIterations = -1)
{
	if (src.empty())
	{
		return;//图像为空,直接返回
	}
	//cv::threshold(src, src, m_dThreshold, 1, CV_THRESH_BINARY);//转为0或1的图像

	int ImgHeight = src.rows;
	int ImgWidth = src.cols;

	int count = 0;  //记录迭代次数
	while (true)
	{
		count++;
		if (maxIterations != -1 && count > maxIterations) //限制次数并且迭代次数到达
			break;

		vector<pair<int, int> > mFlag; //用于标记需要删除的点
									   //对点标记
		for (int i = 0; i < ImgHeight; ++i)
		{
			for (int j = 0; j < ImgWidth; ++j)
			{
				//如果满足四个条件,进行标记
				//  p9 p2 p3
				//  p8 p1 p4
				//  p7 p6 p5
				int p1 = src.at<uchar>(i, j);
				int p2 = (i == 0) ? 0 : src.at<uchar>(i - 1, j);
				int p3 = (i == 0 || j == ImgWidth - 1) ? 0 : src.at<uchar>(i - 1, j + 1);
				int p4 = (j == ImgWidth - 1) ? 0 : src.at<uchar>(i, j + 1);
				int p5 = (i == ImgHeight - 1 || j == ImgWidth - 1) ? 0 : src.at<uchar>(i + 1, j + 1);
				int p6 = (i == ImgHeight - 1) ? 0 : src.at<uchar>(i + 1, j);
				int p7 = (i == ImgHeight - 1 || j == 0) ? 0 : src.at<uchar>(i + 1, j - 1);
				int p8 = (j == 0) ? 0 : src.at<uchar>(i, j - 1);
				int p9 = (i == 0 || j == 0) ? 0 : src.at<uchar>(i - 1, j - 1);

				if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
				{
					int ap = 0;
					if (p2 == 0 && p3 == 1) ++ap;
					if (p3 == 0 && p4 == 1) ++ap;
					if (p4 == 0 && p5 == 1) ++ap;
					if (p5 == 0 && p6 == 1) ++ap;
					if (p6 == 0 && p7 == 1) ++ap;
					if (p7 == 0 && p8 == 1) ++ap;
					if (p8 == 0 && p9 == 1) ++ap;
					if (p9 == 0 && p2 == 1) ++ap;

					if (ap == 1)
					{
						if (p2*p4*p6 == 0)
						{
							if (p4*p6*p8 == 0)
							{
								//标记
								mFlag.push_back(make_pair(i, j));
							}
						}
					}
				}
			}
		}

		//将标记的点删除
		for (vector<pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
		{
			src.at<uchar>(i->first, i->second) = 0;
		}

		//直到没有点满足,算法结束
		if (mFlag.size() == 0) break;
		else mFlag.clear();//将mFlag清空

						   //对点标记
		for (int i = 0; i < ImgHeight; ++i)
		{
			for (int j = 0; j < ImgWidth; ++j)
			{
				//如果满足四个条件,进行标记
				//  p9 p2 p3
				//  p8 p1 p4
				//  p7 p6 p5
				int p1 = src.at<uchar>(i, j);
				if (p1 != 1) continue;
				int p2 = (i == 0) ? 0 : src.at<uchar>(i - 1, j);
				int p3 = (i == 0 || j == ImgWidth - 1) ? 0 : src.at<uchar>(i - 1, j + 1);
				int p4 = (j == ImgWidth - 1) ? 0 : src.at<uchar>(i, j + 1);
				int p5 = (i == ImgHeight - 1 || j == ImgWidth - 1) ? 0 : src.at<uchar>(i + 1, j + 1);
				int p6 = (i == ImgHeight - 1) ? 0 : src.at<uchar>(i + 1, j);
				int p7 = (i == ImgHeight - 1 || j == 0) ? 0 : src.at<uchar>(i + 1, j - 1);
				int p8 = (j == 0) ? 0 : src.at<uchar>(i, j - 1);
				int p9 = (i == 0 || j == 0) ? 0 : src.at<uchar>(i - 1, j - 1);

				if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) >= 2 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) <= 6)
				{
					int ap = 0;
					if (p2 == 0 && p3 == 1) ++ap;
					if (p3 == 0 && p4 == 1) ++ap;
					if (p4 == 0 && p5 == 1) ++ap;
					if (p5 == 0 && p6 == 1) ++ap;
					if (p6 == 0 && p7 == 1) ++ap;
					if (p7 == 0 && p8 == 1) ++ap;
					if (p8 == 0 && p9 == 1) ++ap;
					if (p9 == 0 && p2 == 1) ++ap;

					if (ap == 1)
					{
						if (p2*p4*p8 == 0)
						{
							if (p2*p6*p8 == 0)
							{
								//标记
								mFlag.push_back(make_pair(i, j));
							}
						}
					}
				}
			}
		}
		//删除
		for (vector<pair<int, int> >::iterator i = mFlag.begin(); i != mFlag.end(); ++i)
		{
			src.at<uchar>(i->first, i->second) = 0;
		}

		//直到没有点满足,算法结束
		if (mFlag.size() == 0) break;
		else mFlag.clear();//将mFlag清空
	}

	//cv::threshold(src, src, 0, 255, CV_THRESH_BINARY);//二值化图像
}

ROI

roi.setTo(Scalar(255, 255, 255));

图像缩放

  • 四种插值方法

assertion failed 可能是显示的图像没赋值

cv::Size dsize = cv::Size(src.cols*scale, src.rows*scale);
cv::resize(src, src, dsize, 0, 0, INTER_LINEAR);

图像翻转

0 上下翻转
1 左右翻转
-1 180°翻转

flip(image,dst,0);

图像旋转

M 两行三列

c o s ( θ ) cos(\theta) cos(θ) s i n ( θ ) sin(\theta) sin(θ) 0
− s i n ( θ ) -sin(\theta) sin(θ) cos(\theta) 0
M = getROtationMatrix2D(Point2f(), angle, 1.0);
wrapAffine(src, dst, M, dsize, INTER_linear, 0, Scalar(255,255,255));

求直线交点

cv::Point2f get_cross_pts(cv::Vec4i line_1, cv::Vec4i line_2)
{
	Point2f crossPoint;
	double ka = (double)(line_1[3] - line_1[1]) / (double)(line_1[2] - line_1[0]);//直线的斜率

	if (line_2[2] - line_2[0] == 0)
	{
		crossPoint.x = line_2[0];
		crossPoint.y = ka * (line_2[0] - line_1[0]) + line_1[1];

	}
	else
	{
		
		double kb = (double)(line_2[3] - line_2[1]) / (double)(line_2[2] - line_2[0]);//直线的斜率

		crossPoint.x = (ka*line_1[0] - line_1[1] - kb*line_2[0] + line_2[1]) / (ka - kb);
		crossPoint.y = (ka*kb*(line_1[0] - line_2[0]) + ka*line_2[1] - kb*line_1[1]) / (ka - kb);
	}
	
	return crossPoint;
}


// 调用
std::vector<cv::Point2f> cross_pts;
for (int i = 0; i < 2; i++)
{
	for (int j = 2; j < 4; j++)
	{
		Point2f pt = get_cross_pts(border_line[i], border_line[j]);
		cross_pts.push_back(pt);
	}

}

区域生长

/**
* @brief 区域生长算法,输入图像应为灰度图像
* @param srcImage 区域生长的源图像
* @param pt 区域生长点
* @param ch1Thres 通道的生长限制阈值,临近像素符合±chxThres范围内才能进行生长
* @param ch1LowerBind 通道的最小值阈值
* @param ch1UpperBind 通道的最大值阈值,在这个范围外即使临近像素符合±chxThres也不能生长
* @return 生成的区域图像(二值类型)
*/Mat RegionGrow(Mat srcImage, Point pt, int ch1Thres, int ch1LowerBind = 0, int ch1UpperBind = 255) {
	Point pToGrowing;                       //待生长点位置
	int pGrowValue = 0;                             //待生长点灰度值
	Scalar pSrcValue = 0;                               //生长起点灰度值
	Scalar pCurValue = 0;                               //当前生长点灰度值
	Mat growImage = Mat::zeros(srcImage.size(), CV_8UC1);   //创建一个空白区域,填充为黑色
															//生长方向顺序数据
	int DIR[8][2] = { { -1,-1 },{ 0,-1 },{ 1,-1 },{ 1,0 },{ 1,1 },{ 0,1 },{ -1,1 },{ -1,0 } };
	vector<Point> growPtVector;                     //生长点栈
	growPtVector.push_back(pt);                         //将生长点压入栈中
	growImage.at<uchar>(pt.y, pt.x) = 255;              //标记生长点
	pSrcValue = srcImage.at<uchar>(pt.y, pt.x);         //记录生长点的灰度值

	while (!growPtVector.empty())                       //生长栈不为空则生长
	{
		pt = growPtVector.back();                       //取出一个生长点
		growPtVector.pop_back();        //分别对八个方向上的点进行生长
		for (int i = 0; i<9; ++i)
		{
			pToGrowing.x = pt.x + DIR[i][0];
			pToGrowing.y = pt.y + DIR[i][1];            //检查是否是边缘点
			if (pToGrowing.x < 0 || pToGrowing.y < 0 ||
				pToGrowing.x >(srcImage.cols - 1) || (pToGrowing.y > srcImage.rows - 1))                continue;

			pGrowValue = growImage.at<uchar>(pToGrowing.y, pToGrowing.x);       //当前待生长点的灰度值
			pSrcValue = srcImage.at<uchar>(pt.y, pt.x);            if (pGrowValue == 0)                    //如果标记点还没有被生长
			{
				pCurValue = srcImage.at<uchar>(pToGrowing.y, pToGrowing.x);                if (pCurValue[0] <= ch1UpperBind && pCurValue[0] >= ch1LowerBind)
				{
					if (abs(pSrcValue[0] - pCurValue[0]) < ch1Thres)                   //在阈值范围内则生长
					{
						growImage.at<uchar>(pToGrowing.y, pToGrowing.x) = 255;      //标记为白色
						growPtVector.push_back(pToGrowing);                 //将下一个生长点压入栈中
					}
				}
			}
		}
	}    return growImage.clone();
}

坐标变换

cv::Mat  coord_wrap(cv::Mat &img, std::vector<cv::Point2f> pts_src)
{
	/*int pad = 0;
	
	

	*/
	Point2f pt1 = pts_src[0];
	Point2f pt2 = pts_src[1];
	Point2f pt3 = pts_src[2];
	std::vector<cv::Point2f> pts_dst;
	int w = sqrt(pow(pt2.x - pt1.x, 2) + pow(pt2.y - pt1.y, 2));
	int h = sqrt(pow(pt1.x - pt3.x, 2) + pow(pt1.y - pt3.y, 2));

	
	pts_dst.push_back(cv::Point2f(0, 0));
	pts_dst.push_back(cv::Point2f(w, 0));
	
	pts_dst.push_back(cv::Point2f(0, h));
	pts_dst.push_back(cv::Point2f(w, h));

	cv::Mat H = cv::findHomography(pts_src, pts_dst);


	cv::Mat im_out;

	cv::warpPerspective(img, im_out, H, cv::Size(w, h));

	return im_out;
}

边界填充

copyMakeBorder(canny_edges, mask, 0, 2, 0, 2, 0, Scalar(0));

自己写的没有自带的好

void border_padding(cv::Mat &img, int pad, cv::Mat& res)
{
	int width = img.cols;
	int height = img.rows;
	int res_width = width + 2 * pad;
	int res_height = height + 2 * pad;
	res.create(res_height, res_width, img.type());
	int val = 255;
	for (int i = 0; i < height + 2 * pad; i++)
	{
		cv::Vec3b *p = res.ptr<cv::Vec3b>(i);//彩色
		for (int j = 0; j < width + 2 * pad; j++)
		{
			
			cv::Vec3b &pix = *p++;//彩色图
			if (i < pad || i >= height + pad || j < pad || j >= width + pad)
			{
				pix[0] = 255;
				pix[1] = 255;
				pix[2] = 255;
			}
			else
			{
					
				pix[0] = img.at<cv::Vec3b>(i - pad, j - pad)[0];
				pix[1] = img.at<cv::Vec3b>(i - pad, j - pad)[1];
				pix[2] = img.at<cv::Vec3b>(i - pad, j - pad)[2];
			}
		}

	}

}

漫水填充

Point seed = Point(800, 600);
int loDiff = 5;
int upDiff = 5;
Scalar newVal = Scalar(0, 0, 255);
Mat mask = Mat::zeros(img.rows + 2, img.cols + 2, CV_8UC1);
//copyMakeBorder(canny_edges, mask, 0, 2, 0, 2, 0, Scalar(0));
Rect ccomp;
int flg = 8;
floodFill(src, mask, seed, newVal, &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flg);//避免边缘被填充
	

滚轮放大和缩小

#include 
#include "global.h"
using namespace cv;

struct MouseParam
{
	Mat img; 
	Mat g_image_show;//用于画好一个后显示
	Mat imgZoom; //缩放图像
	Mat imgTmp; //实时显示图像
	Point g_location_win; //鼠标相对于窗口的坐标
	Point location_win;  //窗口相对于图片的坐标
	 
	Point pt1;  //鼠标右键单击的位置
	Point pt2;//鼠标右键释放的位置
	Point show_wh;  //实际显示图片的宽高
	bool mouseLflag;
	float g_zoom;
	
};



//计算窗口在图像中的位置
//  win_xy : 窗口在图片的位置
void calLocation(Point win_xy, int src_width, int src_height, int window_width, int window_height)
{
	//int window_width = windowSize.x;
	//int window_height = windowSize.y;
	if (win_xy.x < 0)
	{
		win_xy.x = 0;
	}
	else if (win_xy.x + window_width > src_width && window_width < src_width)
	{
		win_xy.x = src_width - window_width;
	}
	else if (win_xy.x + window_width > src_width && window_width > src_width)
	{
		win_xy.x = 0;
	}


	if (win_xy.y < 0)
	{
		win_xy.y = 0;
	}
	else if (win_xy.y + window_height > src_height && window_height < src_height)
	{
		win_xy.y = src_height - window_height;
	}
	else if (win_xy.x + window_height > src_height && window_height > src_height)
	{
		win_xy.y = 0;
	}
}

// 计算缩放倍数
// flag:鼠标滚轮上移或下移的标识, step:缩放系数,滚轮每步缩放0.1, zoom:缩放倍数
float calZoom(int flag, int step, float zoom)
{
	
	if (flag > 0) //滚轮前移
	{
		zoom = zoom + step;
		if (zoom > 1 + step * 20)
		{
			zoom = 1 + step * 20; //最多只能放大3倍
		}
		
	}
	else if (flag < 0)
	{
		zoom = zoom - step;
		if (zoom < step)
		{
			zoom = step; //最多只能缩小到0.1

		}
	}
	return zoom;
}


void on_mouse(int event, int x, int y, int flags, void* param)
{
	MouseParam *par = (MouseParam*)param;
	Point click_location(x, y); //鼠标点击的位置
	
	if (event == EVENT_RBUTTONDOWN) //右键点击
	{
		par->pt1 = click_location;
		par->location_win.x = par->g_location_win.x;
		par->location_win.y = par->g_location_win.y;

	}
	else if (event == CV_EVENT_MOUSEMOVE && flags == CV_EVENT_FLAG_RBUTTON) //按住且拖拽
	{
		par->pt2 = click_location;
		//Mat imgzoom = par->imgZoom;
		int w1 = par->imgZoom.cols; //缩放图像的宽高
		int h1 = par->imgZoom.rows;
		int w2 = 800;
		int h2 = 600;
		par->show_wh.x = 0;
		par->show_wh.y = 0;
		if (w1 < w2 && h1 < h2)   //图片的宽高小于窗口宽高,无法移动
		{
			par->show_wh.x = w1;
			par->show_wh.y = h1;
		
			par->g_location_win.x = 0;
			par->g_location_win.y = 0;
		}
		else if (w1 >= w2 && h1 < h2) //图片的宽度大于窗口的宽度,可左右移动
		{
			par->show_wh.x = w2;
			par->show_wh.y = h1;

			par->g_location_win.x = par->location_win.x + par->pt1.x - par->pt2.x;
			//par->g_location_win.y = 0;
		}
		else if (w1 < w2 && h1 >= h2) //图片的高度大于窗口的高度,可上下移动
		{
			par->show_wh.x = w1;
			par->show_wh.y = h2;

			par->g_location_win.y = par->location_win.y + par->pt1.y - par->pt2.y;
			//par->g_location_win.y = 0;
		}
		else //图片的宽高大于窗口宽高,可左右上下移动
		{
			par->show_wh.x = w2;
			par->show_wh.y = h2;
			par->g_location_win.x = par->location_win.x + par->pt1.x - par->pt2.x;
			par->g_location_win.y = par->location_win.y + par->pt1.y - par->pt2.y;


		}
		calLocation(par->g_location_win, w1, h1, w2, h2); // 矫正窗口在图片中的位置
		int x2 = par->g_location_win.x + par->show_wh.x;
		int y2 = par->g_location_win.y + par->show_wh.y;
		Rect g_image_zoom(par->g_location_win, Point(x2, y2));
		par->g_image_show = par->img(g_image_zoom).clone();


	}
	else if (event == CV_EVENT_MOUSEWHEEL)
	{
		float z = par->g_zoom;
		float gzoom = calZoom(flags, step, z);
		//缩放后图片的宽高
		int w1 = (int)(par->img.cols*gzoom );
		int h1 = (int)(par->img.rows*gzoom );
		//窗口的宽高
		int w2 = 800;
		int h2 = 600;
		Point pt(0, 0);
		par->show_wh = pt;
		resize(par->img, par->g_image_show, Size(), gzoom, gzoom, INTER_LINEAR); //图片缩放
		if (w1 < w2 && h1 < h2) //缩放后,图片宽高小于窗口宽高
		{
			
			par->show_wh.x = w1;
			par->show_wh.y = h1;
			resizeWindow("1", w1, h1);
		}
		else if (w1 >= w2 && h1 < h2) //缩放后,图片高度小于窗口高度
		{
			par->show_wh.x = w2;
			par->show_wh.y = h1;
			resizeWindow("1", w2, h1);
		}
		else if (w1 < w2 && h1 >= h2) //缩放后,图片宽度小于窗口宽度
		{
			par->show_wh.x = w1;
			par->show_wh.y = h2;
			resizeWindow("1", w1, h2);
		}
		else  //缩放后,图片宽高大于窗口宽高
		{
			par->show_wh.x = w2;
			par->show_wh.y = h2;
			resizeWindow("1", w2, h2);
			

		}
		//缩放后,窗口在图片的位置
		par->g_location_win.x = (int)(par->g_location_win.x + x) * gzoom / z - x;

		par->g_location_win.y = (int)(par->g_location_win.y + y) * gzoom / z - y;
		calLocation(par->g_location_win, w1, h1, w2, h2);
		int x2 = par->g_location_win.x + par->show_wh.x;
		int y2 = par->g_location_win.y + par->show_wh.y;
		Rect g_image_zoom(par->g_location_win, Point(x2, y2));
		par->g_image_show = par->img(g_image_zoom).clone();
	}
	imshow("1", par->g_image_show);
	
}


int main()
{
	Mat src = imread("D:\\program\\data1\\2.png");
	int src_width = src.rows;
	int src_height = src.cols;
	MouseParam mouseParam;
	mouseParam.img = src.clone();
	//mouseParam.imgTmp = src.clone();
	mouseParam.g_image_show = src.clone();
	/*src.copyTo(mouseParam.img);
	src.copyTo(mouseParam.imgTmp);
	src.copyTo(mouseParam.g_image_show);*/
	mouseParam.g_zoom = 1.0;
	Point pt(0, 0);
	mouseParam.g_location_win = pt;
	mouseParam.location_win = pt;

	/*int x2 = mouseParam.g_location_win.x +800;
	int y2 = mouseParam.g_location_win.y + 600;
	Rect g_image_zoom(mouseParam.g_location_win, Point(x2, y2));
	mouseParam.g_image_show = mouseParam.img(g_image_zoom).clone();*/
	Point win_xy;
	Point windowSize(800, 600); //设定窗口的尺寸
	namedWindow("1", CV_WINDOW_NORMAL);
	resizeWindow("1", windowSize.x, windowSize.y);
	setMouseCallback("1", on_mouse, &mouseParam);
	//while (1)
	//{

	//	mouseParam.imgTmp = mouseParam.img.clone();

	imshow("1", mouseParam.g_image_show);
	//	int k = waitKey(40);
	//}
	//imshow("1", src);
	waitKey(0);
	//int k = waitKey(40);
	return 0;


}

鼠标绘制矩形

#include 

using namespace std;
using namespace cv;



struct MouseParam
{
	Mat img; //用于画好一个后显示
	Mat imgZoomBackup; //用于zoom的还原备份
	Mat imgTmp; //用于实时显示
	Mat imgBackup; //清空,显示最初的图
	
	Point pt1;
	Point pt2;
	Rect box;
	bool mouseLflag;

};

void draw_rectangle(Mat &img, const Point &pt1, const Point &pt2)
{
	//rectangle函数参数: 图片, 左上角, 右下角, 颜色, 线条粗细, 线条类型,点类型    
	rectangle(img, pt1, pt2, Scalar(0, 255, 0), 1, 0, 0);
}




void on_mouse(int event, int x, int y, int flags, void* param)
{
	MouseParam *par = (MouseParam*)param;
	Point pt;
	pt.x = x;
	pt.y = y;
	

	if (event == EVENT_RBUTTONDOWN) //按下右键,重画
	{
		par->img = par->imgBackup.clone();
	}
	else if (event == EVENT_LBUTTONDOWN)
	{
		par->box = Rect2d(x, y, 0, 0);
		par->pt1 = pt;
		//par->pt2 = pt;
		par->mouseLflag = true;
	}
	else if (event == CV_EVENT_MOUSEMOVE && flags == CV_EVENT_FLAG_LBUTTON)
	{
		par->pt2 = pt;
		par->box.width = x - par->box.x;
		par->box.height = y - par->box.y;
	}
	else if (event == CV_EVENT_LBUTTONUP)
	{
		par->pt2 = pt;
		//draw_rectangle(par->imgTmp, par->pt1, par->pt2);
		//par->imgZoomBackup = par->img.clone();
		par->mouseLflag = false;

		if (par->box.width < 0)
		{
			par->box.x += par->box.width;
			par->box.width *= -1;
		}
		if(par->box.height < 0)
		{
			par->box.y += par->box.height;
			par->box.height *= -1;
		}
		rectangle(par->img, par->box, Scalar(0, 0, 255), 1, 1, 0);
	}
	else if (event == CV_EVENT_MOUSEMOVE)
	{
		par->pt1 = pt;
		//par->img.copyTo(par->imgZoomBackup);

		//-1*
	}
	
}

int main()
{
	//Mat img(512, 512, CV_8UC3, Scalar::all(255));
	Mat img = imread("D:\\program\\data1\\2.png");
	MouseParam mouseParam;
	mouseParam.img = img.clone();
	mouseParam.imgBackup = img.clone();
	mouseParam.imgZoomBackup = img.clone();
	mouseParam.mouseLflag = false;
	
	namedWindow("Box Example");
	//namedWindow("Box Example1");
	setMouseCallback("Box Example", on_mouse, &mouseParam);

	//imshow(WINNAME, mouseParam.imgTmp);

	int key;
	while (1)
	{
		mouseParam.imgTmp = mouseParam.img.clone();
		
		if (mouseParam.mouseLflag == true)
		{
			rectangle(mouseParam.imgTmp, mouseParam.box, Scalar(255, 0, 255), 1, 1, 0);

		}
			//draw_rectangle(mouseParam.imgTmp, mouseParam.pt1, mouseParam.pt2);
		imshow("Box Example", mouseParam.imgTmp);
		//imshow("Box Example1", mouseParam.img);
		key = waitKey(40);
		
	}

	return 0;
}

创建文件夹

bool createFolder(CString m_sCurPath)
{
	CString folderPath = m_sCurPath + "//labels";

	//判断目录是否存在,如果不存在,则创建一个
	if (_access(folderPath, 0) != 0)
	{
		int flag = CreateDirectory(folderPath, NULL);
		//判断目录是否创建成功 非零表示成功,零表示失败。
		if (flag != 0)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	return true;
}

创建txt

bool saveBox(BoxList boxList, CString m_sCurPath)
{
	CString folderPath = m_sCurPath + "//labels";
	if (_access(folderPath, 0) == -1) //不存在,返回-1
	{
		createFolder(m_sCurPath);
	}

	CString FilefullName = boxList.fileName;
	int nPos = FilefullName.ReverseFind('.');
	CString  csFileName = FilefullName.Left(nPos); // 获取文件名,去掉扩展名
	CString FILENAME = folderPath + "//" + csFileName + ".txt";
	std::ofstream ofs;
	
	ofs.open(FILENAME, std::ios::out);//用输出的方式打开文件  --写文件
	std::vector<labelBox> bbox = boxList.boundingbox;
	//将每个边界框的数据写入文件
	for (int i = 0; i < bbox.size(); i++)
	{
		ofs << bbox[i].className << " "
			<< bbox[i].boundingbox.x << " "
			<< bbox[i].boundingbox.y << " "
			<< bbox[i].boundingbox.width << " "
			<< bbox[i].boundingbox.height << std::endl;
	}
	//关闭文件
	ofs.close();
	//判断文件是否存在
	if (_access(FILENAME, 0) != -1) //不存在,返回-1
	{
		return true;
	}
	else
	{
		return false;
	}
}

视频处理

读取摄像头

VideoCapture capture(1); 
// 0 是我的前摄像头,1是我的后摄像头

Mat frame;
while (true)
{
	capture.read(frame);
	flip(frame, frame, 1);
	if (frame.empty())
	{
		break;
	}
	imshow("frame", frame);
	int c = waitKey(10);
	if (c == 27)
	{
		break;
	}
	capture.release();
}
int framw_width = capture.get(CAP_PROP_FRAME_WIDTH);
int count = capture.get(CAP_PROP_FRAME_COUNT);
// 帧数
int fps = capture.get(CAP_PROP_FPS);

保存视频一般不超过2G

VideoWriter writer("VideoTest.avi", capture.get(CAP_CV_FOURCC), 25.0, Size(640, 480));
writer.writer(frame);

深度学习

opencv4.4.0

#include 

你可能感兴趣的:(年底总结,c++,opencv,算法)