重磅干货,第一时间送达
环境配置了一个早上,到10.48分配置完毕,有点难受。还好最后显示出第一张图片。 #include<opencv2/opencv.hpp> #include<iostream>
using namespace std; using namespace cv; int main() {
Mat src = imread('D:/images/011.jpg',IMREAD_GRAYSCALE);//读取进来的数据以矩阵的形势,第二个参数代表显示一张灰度图像。 if (src.empty()) { printf('could not load image');//如果图片不存在 将无法读取,打印到终端。 } //超过屏幕的图像无法显示时候调用此函数。 namedWindow('输入窗口', WINDOW_FREERATIO);//创建了一个新窗口,参数1表示名称,第二个参数代表一个自由的比例 imshow('输入窗口', src);//表示显示在新创建的输入窗口上,第一个参数表示窗口名称,src表示数据对象Mat waitKey(0);//执行到这句,程序阻塞。参数表示延时时间。单位ms destroyAllWindows();//销毁前面创建的显示窗口 return 0; }
第一节课介绍了如何读取第一张图片,并且显示出来,通过调用imread函数读取照片,再调用imshow显示图片到窗口。同时,讲述了如何打印灰度图像,图片读取失败的处理方式,代码注释详细介绍了每条语句的意思。 1、色彩空间转换函数 cvtColor 2、图像的保存 #include<opencv2/opencv.hpp> using namespace cv;
class QuickDemo //创建一个QuickDemo对象 { public: void colorSpace_Demo(Mat &imge); //定义一个类,里面包含输入一个图片,对图片操作 };
#include<quickopencv.h> void QuickDemo::colorSpace_Demo(Mat &image) { Mat gray, hsv;//定义2个矩阵类的图像gray和hsv, cvtColor(image,hsv,COLOR_BGR2HSV);//图像转换函数,可以把image转成hsv,第三个参数是转成的类型 cvtColor(image,gray,COLOR_BGR2GRAY);//图像转换函数,可以把image转成hsv,第三个参数是转成的类型 imshow('HSV',hsv); imshow('灰度',gray); imwrite('D:/hsv.jpg',hsv);//保存图片,前面是保存图的地址,后面是保存图的名称 imwrite('D:/gray.jpg',gray); }
#include<opencv2/opencv.hpp> #include<iostream> #include<quickopencv.h>
using namespace std; using namespace cv; int main() {
Mat src = imread('D:/images/1.jpg',IMREAD_ANYCOLOR);//B,G,R实际上0-255三色。3通道 //读取进来的数据以矩阵的形势,第二个参数代表显示一张灰度图像。 if (src.empty()) { printf('could not load image');//如果图片不存在 将无法读取,打印到终端。 return -1; } //超过屏幕的图像无法显示时候调用此函数。
namedWindow('输入窗口', WINDOW_FREERATIO);//创建了一个新窗口,参数1表示名称,第二个参数代表一个自由的比例 imshow('输入窗口', src);//表示显示在新创建的输入窗口上,第一个参数表示窗口名称,src表示数据对象Mat //在主函数中调用之前创建的类对象 QuickDemo qd; qd.colorSpace_Demo(src);
waitKey(0);//执行到这句,程序阻塞。参数表示延时时间。单位ms destroyAllWindows();//销毁前面创建的显示窗口 return 0; }
这节主要介绍了创建一个类对象,然后通过类对象调用函数,在main主函数中进行调用实现类对象中的功能,比如转换成HSV类型图片和GRAY类型图片,最后通过imwrite函数进行图像的保存。 1、怎么操作mat 2、怎么访问每一个像素点 3、怎么创建一个空图或者mat void QuickDemo::mat_creation_demo(Mat &image) { Mat m1, m2; m1 = image.clone(); image.copyTo(m2);
//创建空白图像 Mat m3 = Mat::ones(Size(400, 400), CV_8UC3);//创建8*8的CV8位的无符号的n通道的unsigned char //ones&zeros是初始化的方法 m3 = Scalar(255, 0, 0);//给三个通道都赋值127 ,单通道赋值方法 m3 = 127; //m3初始为蓝色 //数据的宽度和长度是由通道数决定的。 //std::cout << 'width:'<<m3.cols<<'height'<< m3.rows <<'channels'<<m3.channels()<< std::endl; //用来查看宽度,高度与通道数。 /*std::cout << m3 << std::endl;*/ Mat m4 = m3.clone();//赋值M4就是M3 M4改变了,M3也改变了,没有产生新的自我(M4与M3同体) //M4为M3的克隆,M3还是原来的颜色,不会改变。(M4与M3不同体,各自是各自的颜色) //m3.copyTo(m4);//把M3赋值给M4,M4就是蓝色 m4 = Scalar(0, 255, 255);//改变m4的颜色为黄色 ,m4也改变 imshow('图像3', m3);//标题和图像名称 显示图像m3 纯蓝色 imshow('图像4', m4);//标题和图像名称 }
本节课介绍了如何创建一个Mat对象,通过创建新的Mat对象来创建用户的特定的底色画布,创建图像的基本类型有两种一种是ones一种是zeros,ones()中的第一个参数代表图像的大小,第二个参数代表创建几维的图像,UC代表无符号字符型,数组3代表通道数。克隆和赋值的区别,克隆就是产生一个新的对象,新对象改变属性,旧对象属性不变(各自为政)。赋值是二者同体,当新属性发生改变,旧属性也发生改变(二者同体)。 如何遍历和修改每个像素点的数值,分为单通道和多通道。访问模式模式也有两种。第一种是数组访问模式,用最常规的数组下标访问像素值。 void QuickDemo::pixel_visit_demo(Mat &image) { int dims = image.channels(); int h = image.rows; int w = image.cols; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { if (dims == 1) //单通道的灰度图像 { int pv = image.at<uchar>(row, col);//得到像素值 image.at<uchar>(row, col) = 255 - pv;//给像素值重新赋值
} if (dims == 3) //三通道的彩色图像 { Vec3b bgr = image.at<Vec3b>(row, col); //opencv特定的类型,获取三维颜色,3个值 image.at<Vec3b>(row, col)[0] = 255 - bgr[0]; image.at<Vec3b>(row, col)[1] = 255 - bgr[1]; image.at<Vec3b>(row, col)[2] = 255 - bgr[2];//对彩色图像读取它的像素值,并且对像素值进行改写。 } } } namedWindow('像素读写演示', WINDOW_FREERATIO); imshow('像素读写演示', image); }
第二种为指针访问模式,指定一个指针为图片的首地址,通过循环遍历,指针++,一次往后推。 void QuickDemo::pixel_visit_demo(Mat &image) { int dims = image.channels(); int h = image.rows; int w = image.cols; for (int row = 0; row < h; row++) { uchar *current_row = image.ptr<uchar>(row);
for (int col = 0; col < w; col++) { if (dims == 1) //单通道的灰度图像 { int pv = *current_row;//得到像素值 *current_row++ = 255 - pv;//给像素值重新赋值
} if (dims == 3) //三通道的彩色图像 { *current_row++ = 255 - *current_row; //指针每做一次运算,就向后移动一位 *current_row++ = 255 - *current_row; *current_row++ = 255 - *current_row; } } } namedWindow('像素读写演示', WINDOW_FREERATIO); imshow('像素读写演示', image);
}
本节主要介绍了通过两种遍历的方式访问图像的像素值,并且改变图像的像素值。 对图像的各个像素点实现加减乘除的操作。介绍了常用的除爆函数saturate_cast,防止数值过界。 void QuickDemo::operators_demo(Mat &image) { Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); dst = image - Scalar(50, 50, 50); m = Scalar(50, 50, 50); multiply(image,m,dst);//乘法操作 api imshow('乘法操作', dst); add(image, m, dst);//加法操作 api imshow('加法操作', dst); subtract(image, m, dst);//减法操作 api imshow('减法操作', dst); divide(image, m, dst);//除法操作 api namedWindow('加法操作', WINDOW_FREERATIO); imshow('加法操作', dst); //加法操作底层 int dims = image.channels(); int h = image.rows; int w = image.cols; for (int row = 0; row < h; row++) { for (int col = 0; col < w; col++) { Vec3b p1 = image.at<Vec3b>(row, col); //opencv特定的类型,获取三维颜色,3个值 Vec3b p2 = m.at<Vec3b>(row, col); dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);//saturate_cast用来防爆,小于0就是0,大于255就是255 dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]); dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);//对彩色图像读取它的像素值,并且对像素值进行改写。 } } imshow('加法操作', dst); }
介绍了四种不同的API实现,并且演示了一种加法的算法。 本节介绍怎么通过createTrackbar来设置一个进度条,实现图片的亮度调节。 Mat src, dst, m; int lightness = 50;//定义初始的亮度为50 static void on_track(int ,void*) { m = Scalar(lightness,lightness,lightness);//创建调整亮度的数值 subtract(src, m, dst);//定义亮度变化为减 imshow('亮度调整', dst);//显示调整亮度之后的图片 } void QuickDemo::tracking_bar_demo(Mat &image) { namedWindow('亮度调整',WINDOW_AUTOSIZE); dst = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像 m = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像 src = image;//给src赋值 int max_value = 100;//定义最大值为100 createTrackbar('Value Bar:', '亮度调整', &lightness, max_value,on_track);//调用函数实现功能。 on_track(50, 0); }
无类型指针类型转换与参数传递! static void on_lightness(int b ,void* userdata) { Mat image = *((Mat*)userdata); Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); m = Scalar(b,b,b); addWeighted(image,1.0,m,0,b,dst);//融合两张图 imshow('亮度&对比度调整', dst); } static void on_contrast(int b, void* userdata) { Mat image = *((Mat*)userdata); Mat dst = Mat::zeros(image.size(), image.type()); Mat m = Mat::zeros(image.size(), image.type()); double contrast = b / 100.0; addWeighted(image, contrast, m, 0.0, 0, dst);//融合两张图 imshow('亮度&对比度调整', dst); } void QuickDemo::tracking_bar_demo(Mat &image) { namedWindow('亮度&对比度调整',WINDOW_AUTOSIZE); int lightness = 50; int max_value = 100; int contrast_value = 100; createTrackbar('Value Bar:', '亮度&对比度调整', &lightness, max_value, on_lightness,(void*)(&image)); createTrackbar('Contrast Bar:', '亮度&对比度调整', &contrast_value, 200, on_contrast, (void*)(&image)); on_lightness(50, &image); }
本节介绍通过键盘输入,终端能够读取响应的信息。 void QuickDemo::key_demo(Mat &image) { Mat dst= Mat::zeros(image.size(), image.type()); while (true) { char c = waitKey(100);//停顿100ms 做视频处理都是1 if (c == 27) { //esc 退出应用程序 break; } if (c == 49)//key#1 { std::cout <<'you enter key #1' << std::endl; cvtColor(image, dst, COLOR_BGR2GRAY); } if (c == 50)//key#1 { std::cout << 'you enter key #2' << std::endl; cvtColor(image, dst, COLOR_BGR2HSV); } if (c == 51)//key#1 { std::cout << 'you enter key #3' << std::endl; dst = Scalar(50, 50, 50); add(image,dst,dst); } imshow('键盘响应',dst); std::cout << c << std::endl; } }
通过键盘输入,在终端得到响应,输入不同的键值,得到不一样的结果。 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; int index = 0; while (true) { char c = waitKey(100);//停顿100ms 做视频处理都是1 if (c == 27) { //esc 退出应用程序 break; } if (c == 49)//key#1 按下按键1时,保存图片到指定位置 { std::cout << 'you enter key #1' << std::endl; imwrite('D:/gray.jpg', dst); } applyColorMap(image, dst, colormap[index%19]);//循环展示19种图片 index++; imshow('循环播放', dst); } }
伪色彩填充与颜色表匹配 本节介绍如何对图像的像素进行操作,包括与、或、非、异或,矩形在图像中的绘制。 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);//小于0表示填充,大于0表示绘制 rectangle(m2,Rect(150,150,80,80), Scalar(0,255,255), -1, LINE_8, 0); imshow('m1', m1); imshow('m2', m2); Mat dst; bitwise_and(m1, m2, dst);//位操作与 bitwise_or(m1, m2, dst);//位操作或 bitwise_not(image, dst);//取反操作 bitwise_xor(m1, m2, dst);//异或操作 imshow('像素位操作', dst); }
rectangle(m1,Rect(100,100,80,80),Scalar(255,255,0),-1,LINE_8,0);
这个函数参数1是图片名称,参数2是矩形的起始&末尾位置,参数3 Scalar表示将要绘制图像的颜色,参数4表示小于0表示填充,大于0表示绘制,参数5表示四邻域或者八邻域的绘制,参数6表示中心坐标或者半径坐标的小数位数。 本节介绍如何把不同的通道给分离,归并,使得能显现出来不同的通道颜色。 void QuickDemo::channels_demo(Mat &image) { std::vector<Mat>mv; split(image, mv); //imshow('蓝色', mv[0]); //0,1,2三个通道分别代表BGR。 //关闭2个通道意味着开启一个通道。 //imshow('绿色', mv[1]); //imshow('红色', mv[2]); Mat dst; mv[0] = 0; mv[2] = 0; merge(mv, dst); imshow('蓝色', dst); int from_to[] = { 0,2,1,1,2,0 }; //把通道相互交换,第0->第2,第一->第一,第二->第0 mixChannels(&image,1,&dst,1,from_to,3);//3表示3个通道 //参数1指针引用图像->参数2引用到dst imshow('通道混合', dst); }
M[0],M[1],M[2]分别代表BGR个不同的通道。要开启某个通道只需要关闭另外的一个通道即可。第二个内容为通道的合并,将不同通道的像素值进行转换操作,使图片呈现出不同的效果。
本节内容实现任务是提取任务的轮廓,首先把RGB色彩空间的图片转换到HSV空间中,其次,提取图片的mask,通过使用inrangle提取hsv色彩空间的颜色。HSV色彩空间的颜色 void QuickDemo::inrange_demo(Mat &image) { Mat hsv; cvtColor(image, hsv, COLOR_BGR2HSV); Mat mask; inRange(hsv,Scalar(35,43,46),Scalar(77,255,255),mask); //35,43,46根据图片中绿色最低来确定最小值。 //77,255,255 提取 //参数1低范围,参数2高范围 //将hsv中的由低到高的像素点提取出来并且存储到mask当中。 imshow('mask',hsv); Mat redback = Mat::zeros(image.size(), image.type()); redback = Scalar(40, 40, 200); bitwise_not(mask, mask); imshow('mask', mask); image.copyTo(redback, mask);//把redback复制到mask,mask通过inRange得到。 imshow('roi提取', hsv); }
分别定义双精度型变量 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, Mat());//求出图像的最大值和最小值。 std::cout <<'No.channels:'<<i<<'minvalue:' << minv << 'maxvalue:' << maxv << std::endl; } Mat mean, stddev; meanStdDev(image, mean, stddev);//求出图像的均值和方差 std::cout << 'mean:' << mean << std::endl; std::cout << 'stddev:' << stddev << std::endl; }
本节课介绍如何绘制椭圆,矩形,直线,圆等 void QuickDemo::drawing_demo(Mat &image) { Rect rect; rect.x = 200; rect.y = 200; rect.width = 100; rect.height = 100; Mat bg = Mat::zeros(image.size(),image.type()); rectangle(image, rect, Scalar(0, 0, 255), -1, 8, 0); //参数1为绘图的底图或者画布名称,参数2位图片的起始,宽度,高度 //参数3代表填充颜色。参数4大于0是线小于0是填充 //参数5表示邻域填充,参数6默认值为0 circle(bg, Point(350, 400), 15, Scalar(0, 0, 255), 2, LINE_AA, 0); //参数2位图片中心位置,参数3为半径为15的圆 Mat dst; //addWeighted(image, 0.7, bg, 0.3, 0, dst); RotatedRect rtt; rtt.center = Point(200, 200); rtt.size = Size(100, 200); rtt.angle = 0.0; line(bg,Point(100,100),Point(350,400), Scalar(0, 0, 255), 8, LINE_AA, 0);//line_AA表示去掉锯齿 ellipse(bg,rtt, Scalar(0, 0, 255), 2, 8); imshow('矩形的绘制',bg); }
本节主要介绍如何能产生一个随机数字和随机颜色,并且用线条的方式显示出来。 void QuickDemo::random_drawing() { Mat canvas = Mat::zeros(Size(512,512), CV_8UC3); int w = canvas.cols; int h = canvas.rows; RNG rng(12345); while (true) { int c = waitKey(10); if (c == 27) { break; } int x1 = rng.uniform(0,canvas.cols); int y1 = rng.uniform(0, h); int x2 = rng.uniform(0, canvas.cols); int y2 = rng.uniform(0, h); int b = rng.uniform(0, 255); int g = rng.uniform(0, 255); int r = rng.uniform(0, 255); canvas = Scalar(0,0,0); line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b,g,r), 8, LINE_AA,0);//line_AA表示去掉锯齿 imshow('随机绘制演示', canvas); } }
这节课介绍了2种多边形绘制的实现方式。 void QuickDemo::polyline_drawing_demo(Mat &image) { Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3); Point p1(100, 100); Point p2(350, 100); Point p3(450, 280); Point p4(320, 450); Point p5(80, 400); std::vector<Point>pts;//将5个点装入一个容器内。 pts.push_back(p1);//未初始化数组容量,只能用pushback操作 //如果初始化,可以用数组下标操作。 pts.push_back(p2); pts.push_back(p3); pts.push_back(p4); pts.push_back(p5); //fillPoly(canvas, pts, Scalar(122, 155, 255), 8, 0);//填充多边形 //polylines(canvas, pts, true, Scalar(0, 0, 255), 2, 8, 0);//绘制多边形 /* 参数1表示画布,参数2表示点集,参数3表示true,参数4颜色 参数5表示线宽,参数6表示渲染方式,参数7表示相对左上角(0,0)的位置 */ //单个API搞定图片的绘制填充 std::vector<std::vector<Point>>contours; contours.push_back(pts); drawContours(canvas,contours,-1, Scalar(0, 0, 255),-1); //参数2表示容器名称,参数3为正表示多边形的绘制,为负表示多边形的填充 imshow('多边形绘制', canvas); }
第一种方式,通过标记各个点,然后存储到容器中,之后对容器中的点进行操作。填充多边形调用fillPoly,绘制多边形调用polylines。第二种方式,使用一个API接口绘制。通过一个容器中的存储的点组成的另一个容器。 鼠标事件响应与绘制!考察基本的图形绘制编程能力。 //参数1表示鼠标事件。 Point sp(-1, -1);//鼠标的开始的位置 Point ep(-1, -1); Mat temp; static void on_draw(int event,int x,int y,int flags,void *userdata) { Mat image = *((Mat*)userdata); if(event == EVENT_LBUTTONDOWN)//如果鼠标的左键按下 { sp.x = x; sp.y = y; std::cout << 'start point' <<sp<< std::endl; } else if (event == EVENT_LBUTTONUP) { ep.x = x; ep.y = y; int dx = ep.x - sp.x; int dy = ep.y - sp.y; if (dx > 0 && dy > 0) { Rect box(sp.x, sp.y, dx, dy); imshow('ROI区域', image(box)); rectangle(image, box, Scalar(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_draw,(void*)(&image)); //设置窗口的回调函数。参数1表示名称,参数2表示调用on_draw imshow('鼠标绘制', image); temp = image.clone(); }
像素值归一化是很常见的预处理方式,OpenCV支持字节与浮点数的图象显示。 void QuickDemo::norm_demo(Mat &image) { Mat dst;//定义一个名为dst的二值化类型的数据 std::cout << image.type() << std::endl;//打印出来图片的类型 image.convertTo(image,CV_32F);//将dst数据转换成浮点型float32位数据。 std::cout << image.type() << std::endl;//再次打印转换后的数据类型 normalize(image, dst, 1.0, 0, NORM_MINMAX);//进行归一化操作 std::cout << dst.type() << std::endl;//打印归一化操作之后的数据 imshow('图像的归一化', dst);//显示归一化的图像 //CV_8UC3 ,CV_32FC3 //3通道每个通道8位的UC类型 //转换后 3通道 每个通道32位的浮点数 }
介绍基本的图像变换大小的方法。图像的差值处理主要有线性、双线性差值、卢卡斯差值、双立方差值。 void QuickDemo::resize_demo(Mat &image) { Mat zoomin, zoomout; int h = image.rows; int w = image.cols; resize(image, zoomin, Size(w/2, h/2),0,0,INTER_LINEAR); //线性差值操作。 imshow('zoomin', zoomin);; resize(image, zoomout, Size(w*1.5, h*1.5), 0, 0, INTER_LINEAR); imshow('zoomin', zoomout);// }
图像的上下、左右、对角线翻转 void QuickDemo::flip_demo(Mat &image) { Mat dst; flip(image, dst, 0);//上下翻转 x对称 flip(image, dst, 1);//左右翻转 y对称 flip(image, dst, -1);//旋转180° imshow('图像翻转',dst); }
图像旋转的基本原理,变换矩阵M的计算,中心位置偏移计算等。 void QuickDemo::rotate_demo(Mat &image) { Mat dst, M; int h = image.rows;//定义图片的高度 int w = image.cols;//定义图片的宽度 M = getRotationMatrix2D(Point(w / 2, h / 2),45,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); //参数1原来图像的中心位置。参数2角度是多少。参数3是图像本身大小的放大缩小 warpAffine(image, dst, M,Size(nw,nh),INTER_LINEAR,0, Scalar(0, 0, 255)); imshow('旋转演示', dst); }
本节介绍了如何读取一个视频,以及调用电脑的摄像头。并且对读取到的视频进行操作。 void QuickDemo::video_demo(Mat &image) { VideoCapture capture('D:/images/123.mp4'); //读取视频的地址 Mat frame;//定义一个二值化的 frame
while (true) { capture.read(frame); //读取视频 //flip(frame, frame, 1);//图像镜像操作 if(frame.empty())//如果视频为空的话 跳出操作 { break; } imshow('frame', frame);//显示视频 colorSpace_Demo(frame);//对视频调用之前的demo int c = waitKey(100);//停顿100ms 做视频处理都是1 if (c == 27) { //esc 退出应用程序 break; } } capture.release();//释放相机的资源 }
对读取到的视频 操作方式有镜像对称。加各种滤镜等等。 视频的属性,SD(标清),HD(高清),UHD(超清),蓝光。如何读取视频文件,以及读取视频文件的属性,衡量视频处理指标:FPS。保存视频时的编码格式。保存视频的实际size和create的size大小保持一致。 void QuickDemo::video_demo(Mat &image) { VideoCapture capture('D:/images/123.mp4'); int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);//获取视频的宽度 int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);//获取视频的高度 int count = capture.get(CAP_PROP_FRAME_COUNT);//视频总的帧数 //fps是衡量处理视频的能力 double fps = capture.get(CAP_PROP_FPS); std::cout << 'frame width' << frame_width << std::endl; std::cout << 'frame height' << frame_height << std::endl; std::cout << 'frame FPS' << fps << std::endl; std::cout << 'frame count' << count << std::endl; VideoWriter writer('D:/test.mp4',capture.get(CAP_PROP_FOURCC),fps,Size(frame_width, frame_height),true); //参数1 保存地址。参数2 获取图片的格式 参数3 图片的帧数 参数4 视频宽高 参数5 真 Mat frame; while (true) { capture.read(frame); //flip(frame, frame, 1);//图像镜像操作 if(frame.empty()) { break; } imshow('frame', frame); colorSpace_Demo(frame); writer.write(frame);
int c = waitKey(100);//停顿100ms 做视频处理都是1 if (c == 27) { //esc 退出应用程序 break; } } capture.release();//释放相机的资源 writer.release();//释放存放的资源 }
本节课,介绍了视频的一些基本熟悉,紧接介绍如何获取视频的属性,并且通过特定的格式保存到相应的存储位置上。 直方图是图像的统计学特征。表示了图像的各个像素在0-255出现的频率。图像的平移旋转都不会对性质进行改变。缺点:不能表征一张图像。 用途:用于图像增强,人脸检测,卫星遥感。均衡化的图像只支持单通道。 void QuickDemo::histogram_eq_demo(Mat &image) { Mat gray; cvtColor(image, gray, COLOR_BGR2GRAY); //直方图均衡化只支持灰度图像,不支持彩色图像。 imshow('灰度图像', gray); Mat dst; equalizeHist(gray, dst); imshow('直方图均衡化', dst); }
卷积的作用,高的往下降,低的往上升。但是会造成信息丢失。产生模糊效果。是一种线性操作,点乘,之后相加。 void QuickDemo::blur_demo(Mat &image) { Mat dst; blur(image, dst, Size(15, 15), Point(-1, -1)); //参数1原始图像,参数2卷积之后的图像,参数3卷积的矩阵大小,支持单行或者单列的卷积操作,参数4卷积的起始点。 imshow('图像卷积操作', dst); }
中心的数值最大,离中心距离越远,数值越小。高斯卷积数学表达式说明: 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 }
边缘保留的滤波算法!去噪! void QuickDemo::bifilter_demo(Mat &image) { Mat dst; bilateralFilter(image,dst,0,100,0); //参数1代表原图,参数2代表处理之后的图像,参数3色彩空间。参数4表示坐标空间,双边是指 色彩空间和坐标空间。 namedWindow('双边模糊', WINDOW_FREERATIO);//创建了一个新窗口,参数1表示名称,第二个参数代表一个自由的比例 imshow('双边模糊', dst);//表示显示在新创建的 }
OpenCV4.x中基于深度神经网络模型的高实时,稳定的人脸检测演示。 dnn::Net net = dnn::readNetFromTensorflow(root_dir+ 'opencv_face_detector_uint8.pb', root_dir+'opencv_face_detector.pbtxt'); VideoCapture capture('D:/images/video/example_dsh.mp4'); Mat frame; while (true) { capture.read(frame); if (frame.empty()) { break; } Mat blob = dnn::blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 177, 123), false, false); net.setInput(blob);// NCHW Mat probs = net.forward(); // Mat detectionMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>()); // 解析结果 for (int i = 0; i < detectionMat.rows; i++) { float confidence = detectionMat.at<float>(i, 2); if (confidence > 0.5) { int x1 = static_cast<int>(detectionMat.at<float>(i, 3)*frame.cols); int y1 = static_cast<int>(detectionMat.at<float>(i, 4)*frame.rows); int x2 = static_cast<int>(detectionMat.at<float>(i, 5)*frame.cols); int y2 = static_cast<int>(detectionMat.at<float>(i, 6)*frame.rows); Rect box(x1, y1, x2 - x1, y2 - y1); rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0); } } imshow('人脸检测演示', frame); int c = waitKey(1); if (c == 27) { // 退出 break; } }
OpenCV-C++/Python视频教程30课时,请看B站:
https://www.bilibili.com/video/BV1hM4y1M7vQ (python版本) https://www.bilibili.com/video/BV1i54y1m7tw (C++版本) 下载1:OpenCV-Contrib扩展模块中文版教程
|