Opencv4学习-1、全面入门到完成人脸识别_人的右眼能识别的颜色的grb误差是多少(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取


### 九、opencv自带的颜色表applyColorMap;查找表LUT


Look Up Table(LUT) 查找表  
 查找表在我们的图像基础或简单的颜色匹配或伪色彩增强等当中是十分重要的,有时候我们可以看到红外的照片或各种各样风格的照片是怎么样做到的呢?这就涉及到具体细节的知识点就是颜色查找表,就是要把原来的某种颜色匹配到另外一种颜色上面去,就是把低对比度的图转换到高对比度的图上去。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/330cdb92de614541ba5f895fa4984bff.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


这个查找表可以是一个单调的增函数或者单调的减函数或者直接值匹配的这种也行,在图像像素这个层次而言为什么需要查找表,就是一副图像大概几万个像素而每个像素都要进行转换都要去进行函数转换的话那么十分耗资源,而查找表就是预先做一次转换把0-255都做一次匹配,再图像转换的时候就需要去查找匹配的值就可以不需要每次都去调用函数进行转换了,节约了大量的资源,这才是我们使用LUT查找表的真正原因。LUT查找表在实践很多数据处理场景当中只要你知道你需要处理的数据范围是什么,变化公式是什么,最后的结果是什么,那么对单个元素的变化,很多时候我们都通过LUT查找表来做,这样来节省资源时间,将计算步骤得到很多的减少。


作用;伪色彩加强和灰度或RGB图像的二值图像,加快计算速度


伪色彩函数applyColorMap



CV_EXPORTS_W void applyColorMap(InputArray src, OutputArray dst, int colormap);
src;源图像
dst;目标图像
colormap;提供的色彩图代码值。(参见:ColormapTypes 枚举数据类型)也可以自定义。


可以实现彩色图片颜色变化,以及灰度图颜色增强。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/fd411b59335544f3b012e302a4ec7532.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//色彩表
void QuickDemo::color_style_demo(Mat &image)
{
//enum ColormapTypes 支持的20种色彩空间
int colorMap[] =
{
COLORMAP_AUTUMN,
COLORMAP_BONE,
COLORMAP_JET,
COLORMAP_WINTER,
COLORMAP_RAINBOW,
COLORMAP_OCEAN,
COLORMAP_SUMMER,
COLORMAP_SPRING,
COLORMAP_COOL,
COLORMAP_HSV ,
COLORMAP_PINK ,
COLORMAP_HOT,
COLORMAP_PARULA,
COLORMAP_MAGMA,
COLORMAP_INFERNO,
COLORMAP_PLASMA,
COLORMAP_VIRIDIS,
COLORMAP_CIVIDIS,
COLORMAP_TWILIGHT,
COLORMAP_TWILIGHT_SHIFTED
};
Mat dst;
int index = 0;
while (true)
{
int c = waitKey(2000);//Waits for a pressed key. 等待100ms
if (c == 27)//esc 退出27
{
break;
}
//伪色彩函数
applyColorMap(image, dst, colorMap[index%19]);
index++;
imshow(“颜色风格”, dst);
}
}


### 十、图像像素的逻辑操作(位操作)bite oprocted


位操作对应与或非操作 而opencv中是哪些API与之对应的。



CV_EXPORTS_W void rectangle(InputOutputArray img, Rect rec,
const Scalar& color, int thickness = 1,
int lineType = LINE_8, int shift = 0);
Rect_<_Tp>::Rect_(_Tp _x, _Tp _y, _Tp _width, _Tp _height)
: x(_x), y(_y), width(_width), height(_height) {}

InputOutputArray img传入输出
Rect rec,矩形 传入xy宽高
const Scalar& color,填充颜色
int thickness = 1,1表示一个像素去绘制,相当于描边
传入<0表示填充这个矩形的意思.
opencv当中绘制和填充矩形都是这一个函数,用这个大于0 小于0来进行区分
int lineType = LINE_8 图形绘制方面的知识,与像素排列锯齿有关系。



//对Mat实现像素的位操作
void QuickDemo::bitwise_demo(Mat &image)
{
Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
Mat m3 = Mat::zeros(Size(256, 256), CV_8UC3);
rectangle(m1,Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);//-1表示填充矩形,LINE_8表示利用周围8个像素
rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);
rectangle(m3, Rect(0, 0, 80, 80), Scalar(255, 0, 255),5, LINE_8, 0);//绘制 相当于描边
imshow(“m1”, m1);
imshow(“m2”, m2);
imshow(“m3”, m3);

Mat dst;
bitwise\_and(m1, m2, dst);//相当于交集
imshow("像素位操作与", dst);
bitwise\_or(m1, m2, dst);//全集 
imshow("像素位操作或", dst);
bitwise\_not(image, dst);
//dst = ~image;//也是可以的
imshow("像素位操作非", dst);
bitwise\_xor(m1, m2, dst);
imshow("像素位操作异或", dst);
return;

}


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210411222726997.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


### 十一、图像通道的分离合并混合


分离split



CV_EXPORTS_W void split(InputArray m, OutputArrayOfArrays mv);
OutputArrayOfArrays mv 输出的通道数组 可以用std::vector mv;来接收
实践
//图像分离split函数
std::vector mv;
//opencv顺序是BGR的
split(image, mv); //分离成三个通道对应三种颜色
imshow(“蓝色”, mv[0]);
imshow(“绿色”, mv[1]);
imshow(“红色”, mv[2]);


合并merge



CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst);
InputArrayOfArrays mv 多通道的集合
使用
//合并通道到目标mat
Mat dst;
mv[1] = 0;//只保留蓝色
mv[2] = 0;
merge(mv, dst);
imshow(“只显示蓝色”, dst);


混合mixChannels



CV_EXPORTS void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts,
const int* fromTo, size_t npairs);
const Mat* src 原图数组
size_t nsrcs 操作多少张图像
Mat* dst 目标图数组
size_t ndsts 操作多少张图像
const int* fromTo 转换从通道几到通道几 如{0,2, 1,1, 2,0}第一张0通道到第二张2通道
size_t npairs 转换的数据有几对 3对

实践
//混合交互通道
int from_to[] = {0,2, 1,1, 2,0};//通道交互从哪里到哪里 第一张0通道到第二张2通道
mixChannels(&image, 1, &dst, 1, from_to, 3);//支持多张图片 1表示多少张图片 3表示有几对通道交换数据
imshow(“通道混合”, dst);


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210411230927880.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


### 十二、色彩空间转换-提取图像替换背景


色彩空间转换cvtColor  
 提取指定色彩空间范围区域imRange  
 hsv色彩空间的红橙黄绿蓝顶紫与rgb比较明显的区别界限,  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210412184824980.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 因为hsv相对于rgb的颜色类型要少一些,只有hs两个表示并且只有180\*255个颜色,从而就有hsv相对grb有一些纯颜色就有一定的界限,可以使用imRange提取出来的,利用上面那个表的最大最小值。



inRange可实现二值化功能,主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),该功能类似于之间所讲的双阈值化操作。
CV_EXPORTS_W void inRange(InputArray src, InputArray lowerb,
InputArray upperb, OutputArray dst);
InputArray lowerb;下限值
InputArray upperb; 上限值
OutputArray dst输出的图像 只有01的,再结合copyto可以实现图像叠加
实践
Mat hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
Mat mask;
inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);//输入hsv图像、 根据绿色作为分界则传入hsv绿色的hsv最大最下值
imshow(“inRange mask”, mask);


首先要提取像素值在rgb中是很难的,因为rgb255*255*255颜色值太多太广了而hsv当中只有180\*255的色彩空间值,就比较容易对颜色进行提取。  
 所以当看到一个单一颜色的时候要想去提取他,那么你的第一个反应就应该是要转换为一个色彩比较度高的色彩空间去,如hsv当中去。处理完成之后再返回rgb。


图像叠加copyto函数;源于可以根据mask进行有选择性的拷贝,结合inrange就可以完成不规则图像的iro图像提取。  
 **不规则图像的读取就是将不规则图像生成一个闭合区域,生成一个mask再利用copyto完成任意提取。**



inline
void GpuMat::copyTo(OutputArray dst, InputArray mask) const
{
copyTo(dst, mask, Stream::Null());
}
关键在于mask,
image.copyTo(reaback, mask);
/*
//mask的意思就是image在copy的时候只有像素不为0的像素点部分才会copy到reaback上去。
只是把mask中像素值不为0的像素点从image复制到reaback上去。
*/


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210412194835344.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//色彩空间的转换
void QuickDemo::inrange_demo(Mat &image)
{
Mat hsv;
cvtColor(image, hsv, COLOR_BGR2HSV);
Mat mask;
/*
inRange可实现二值化功能,
主要是将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0),
该功能类似于之间所讲的双阈值化操作。
*/
inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);//输入hsv图像、 根据绿色作为分界则传入hsv绿色的hsv最大最下值
imshow(“inRange mask”, mask);//只有黑白 把绿色都变成1白色 其他变成0黑色

Mat reaback = Mat::zeros(image.size(), image.type());
reaback = Scalar(40, 40, 200);//红底
bitwise\_not(mask, mask);//取反 黑变白 白变黑 就是相当于把绿色部分变成0黑色、人部分变成1白色
imshow("bitwise\_not mask", mask);
/\*

//mask的意思就是image在copy的时候只有像素不为0的点才会copy到reaback上去
相当于只人部分变成1白色部分从image上叠加上去,其他不变
(就是把image人所在区域拷贝到reaback上来)
*/
image.copyTo(reaback, mask);
imshow(“roi区域提取”, reaback);
}


### 十三、像素值统计信息


像素值统计一般就是最小值 最大值 均值mean 标准方差standard deviationn  
 涉及的API就是  
 最大最小值;minMaxLoc 只用于单通道 使用前要split分割  
 计算均值与标准方差;meanStdDev 方差低表示图片差异度低从而在图像处理中可以过滤掉一些信息  
 **如何学习opencvAPI以及在合适的场景选择他们完成功能这就是学习opencv的目标。**



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());
值和坐标点都是传入指针,最后的mask就是用于iro非规则范围读取使用的
一定要记得只使用与单通道 要进行split分割的

CV_EXPORTS_W void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev,
InputArray mask=noArray());
OutputArray mean,均值输出
OutputArray stddev 方差输出
InputArray mask=noArray() iro非规则图像范围类读取
使用
Mat mean, stddev;
meanStdDev(image,mean,stddev);
输出是每个通道一个值 mean.at(0)表示通道1的均值


实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021041220320046.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//进行像素值统计
void QuickDemo::pixel_statistic_demmo(Mat &image)
{
double minv, maxv;//注意是double类型
Point minLoc, maxLoc;//最大最小的地址
std::vector mv;
split(image, mv);
for (int i = 0; i < mv.size(); i++)
{
minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());//传入参数必须是单通道的、得先split
std::cout << “minvalue:” << minv << " maxvalue:" << maxv << std::endl;
}

Mat mean, stddev;
meanStdDev(image,mean,stddev);//第四个参数mask就是iro不规则区域计算
std::cout << "mean:" << mean << std::endl;
std::cout << "mean[0]:" << mean.at<double>(0) << std::endl;
//方差小就是图像的对比度差异度低、有效信息低
//因此在图像分析当中,可以根据方差值小表示周围差异性小 从而是可以过滤掉一些东西的
std::cout << "stddev:" << stddev << std::endl;

}


### 十四、图像几何形状绘制


分为各种形状的绘制和填充绘制,  
 如rectangle 中的int thickness参数 -1表示填充、其他的表示绘制宽度。


### 1、常规图形绘制



Rect 矩形对象类
rectangle 绘制矩形的方法
Point 表示点坐标
circle 绘制园
line 绘制线
RotatedRect 椭圆对象
ellipse绘制椭圆

可以使用addWeighted根据与图像的占比不同实现在图像上关键部位进行叠加几何形状


实际  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2021041221502453.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//几何形状绘制
void QuickDemo::drawing_demo(Mat &image)
{
//坐标的原点是从左上角为0 0
//绘制矩形是可以有类型定义的
Rect rect;
rect.x = 150;
rect.y = 20;
rect.width = 70;
rect.height = 100;

Mat bg = Mat::zeros(image.size(), image.type());
//rectangle(bg, rect, Scalar(0, 0, 255), 5, 0);
rectangle(bg, rect, Scalar(0, 0, 255), -1, 0);//thickness参数改成小于0则表示填充
circle(bg, Point(200, 200), 50, Scalar(255, 0, 0), -1, 0);//画圆
line(bg, Point(100, 100), Point(255, 255), Scalar(0,0,0),8, 8, 0);//lineType图像锯齿问题 shift参数0 是相对左上角位置偏移亮的意思
RotatedRect rrt;//椭圆
rrt.center = Point(300,300);
rrt.size = Size(50,100);
rrt.angle = 0;
ellipse(bg, rrt, Scalar(0, 255, 255), 2, 8);
Mat dst;
addWeighted(image, 0.7, bg, 0.3, 0, dst);//image占比0.7,bg占比0.3 叠加

imshow("绘制几何", dst);

}


### 2、随机数产生绘制


RNG 产生随机数对象 传入种子一般为时间  
 RNG::uniform(int a, int b) 产生a-b范围内的随机值  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210412221449674.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 实践



//随机数和随即绘制
void QuickDemo::randomdrawing_demo(Mat &image)
{
Mat canvas = Mat::zeros(Size(500, 500), 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, w);//在0 - w范围内产生一个随机数
int y1 = rng.uniform(0, h);
int x2 = rng.uniform(0, w);//在0 - w范围内产生一个随机数
int y2 = rng.uniform(0, h);
int b = rng.uniform(0, 255);
int g = rng.uniform(0, 255);
int r = rng.uniform(0, 255);
//canvas = Scalar(0, 0, 0);每次画布更新 达到每次图像只有一条线
line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, LINE_AA, 0);//lineType图像锯齿问题
imshow(“随机绘制”, canvas);
}
return;
}


### 3、多边形的填充绘制fillPoly、polylines、drawContours


fillPoly 只能填充  
 polylines 只能绘制 如果需要表现出来既有填充又会绘制需要注意两者顺序  
 drawContours 可以绘制多个多边形


实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210412224529222.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//绘制多边形
void QuickDemo::polylinedrawing_demo(Mat &image)
{
Mat canvas = Mat::zeros(Size(500, 500), CV_8UC3);
Point p1(100, 100);
Point p2(350, 100);
Point p3(450, 280);
Point p4(320, 450);
Point p5(80, 400);
std::vector vc_pts;
vc_pts.push_back(p1);
vc_pts.push_back(p2);
vc_pts.push_back(p3);
vc_pts.push_back(p4);
vc_pts.push_back(p5);
//要实现边框加填充就要注意fillPoly、polylines的顺序
fillPoly(canvas, vc_pts, Scalar(255, 0, 255), 8, 0);//只能填充
polylines(canvas, vc_pts, true, Scalar(0, 0, 255), 1, LINE_AA, 0);//这里的thickness只能传大于0 不能填充只能绘制
imshow(“多边形绘制”, canvas);

//显示多个多边形函数 绘制填充都可以选择
std::vector<std::vector<Point>> contous;
contous.push\_back(vc_pts);
//contourIdx索引显示第几个,-1表示都显示出来 
//thickness 可以传入-1表示填充
drawContours(canvas, contous, -1, Scalar(255, 0, 0), -1);
imshow("多边形绘制1", canvas);

return;

}


### 十五、鼠标操作及响应 在图形上提取鼠标框选区域setMouseCallback


绑定鼠标事件setMouseCallback



void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
const String& winname;窗口名字
MouseCallback onMouse 鼠标响应后调用的回调函数、注意其原型需要是
typedef void(* cv::mouseCallBack)(int event, int x, int y, int flags, void *userdata)
void* userdata = 0 用于传给回调函数参数的
int event 表示当前发生的鼠标事件是什么
int x, int y 当前鼠标的坐标位置
int flags, 当前鼠标事件的flags值


鼠标event事件的官方截图  
 [opencv官方文档链接](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413190950882.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


实践 还实现了ROI区域的读取、注意几个细节  
 每次都在最新的image上绘制采用一个临时mat先clone再每次绘制前进行copyto最初的到image上  
 注意鼠标回调事件的传参,如当前demo传入Mat image 在该图像上操作  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413193144250.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 代码



//鼠标操作及相应
Point sp(-1, -1);
Point ep(-1, -1);
Mat tempImage;
static void on_draw(int event, int x, int y, int flags, void *usrdata)
{
Mat image = *((Mat *)usrdata);
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);
//注意顺序 不然提取区域会有红色矩形框 并且要保证矩形范围在图形内否则会崩溃
tempImage.copyTo(image);
imshow(“ROI区域”, image(box));//截取鼠标款选的区域
rectangle(image, box, Scalar(0, 0 , 255), 2);
imshow(“鼠标绘制”, image);

		//给下次绘制初始化
		sp.x = -1;
		sp.y = -1;
	}
	std::cout << "end Point: " << ep << std::endl;
}
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);
			tempImage.copyTo(image);//保证每次 绘制image都是最初的
			rectangle(image, box, Scalar(0, 0, 255), 2);
			imshow("鼠标绘制", image);
		}
	}
	
}
return;

}
void QuickDemo::setMouseCallback_demo(Mat &image)
{
namedWindow(“鼠标绘制”, WINDOW_AUTOSIZE);
setMouseCallback(“鼠标绘制”, on_draw, (void *)&image);
tempImage = image.clone();
imshow(“鼠标绘制”, image);
}


### 十六、图像像素数据类型转换及归一化convertTo、normalize


//归一化处理就是处理浮点数,到0-1之间,因此执行归一化处理首先要将图像数据转换为浮点数类型再进行归一化。并且浮点数类型的图片显示必须经过归一化处理把数据变成0-1之间才能正常显示出来,之后如何转换回来,就是归一化处理之后的像素值乘以255,再converto转换会int类型  
 数据类型转换converto  
 归一化处理normalize



inline
void GpuMat::convertTo(OutputArray dst, int rtype, double alpha, double beta) const
{
convertTo(dst, rtype, alpha, beta, Stream::Null());
}
int rtype 数据类型 CV_32F 32位浮点数类型
double alpha;每个像素值乘以alpha
double beta;每个像素值加上beta
输出的时候每个像素值是否需要乘以alpha加上beta值

归一化处理
CV_EXPORTS_W void normalize( InputArray src, InputOutputArray dst, double alpha = 1, double beta = 0,
int norm_type = NORM_L2, int dtype = -1, InputArray mask = noArray());
double alpha = 1, double beta = 0,归一化处理值的上下限
int norm_type = NORM_L2 归一化处理的算法
int dtype = -1, 通道数变化 -1表示与原图像一致
InputArray mask = noArray() ROI图像提取 也就是只归一化mat为非0的区域、可以使用inRange函数提取mask


图像归一化处理主要用在深度学习的建立模型的时候使用  
 实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413201142711.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//像素类型转换及归一化处理
void QuickDemo::norm_demo(Mat &image)
{
Mat dst;
std::cout << image.type() << std::endl;
image.convertTo(image, CV_32F);//转化为32位的浮点数
std::cout << image.type() << std::endl;//type()输出的值是opencv重定义的枚举类型值
normalize(image, dst, 1.0, 0, NORM_MINMAX);
std::cout << dst.type() << std::endl;
imshow(“图像数据归一化”, dst);
}


### 十七、图像放缩及 插值算法 resize


图像的resize大小放大缩小实现中就使用了插值算法、其中插值算法有很多种如双立方插值、双线性内插值、Lanczos采用放缩算法等,其实只要涉及图像的像素提取移动都是用到插值算法的(几何变化、透视变化、插值计算像素resize等系列情况)



CV_EXPORTS_W void resize( InputArray src, OutputArray dst,
Size dsize, double fx = 0, double fy = 0,
int interpolation = INTER_LINEAR );
Size dsize, 目标图像的大小
double fx = 0, double fy = 0 如果size写入的是0,0 则安装fx,fy进行水平 竖直方向的放缩
int interpolation = INTER_LINEAR 选择的插值方法


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413214422152.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//图像放缩及插值
void QuickDemo::resize_demo(Mat &image)
{
Mat zoomin;
Mat zoomax;
int w = image.cols;
int h = image.rows;
resize(image, zoomin, Size(w/2, h/2), 0, 0, INTER_LINEAR);
imshow(“zoomin”, zoomin);
resize(image, zoomax, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);
imshow(“zoomax”, zoomax);
}


### 十八、图像的翻转 平移


### 1、图像翻转flip


图像的翻转其实就是获取他的镜像、



CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode);
int flipCode指定如何翻转数组的标志;
0表示绕x轴翻转,就是上下翻转
正值(例如1)表示绕y轴翻转。就是左右翻转
负值(例如-1)意味着在两个轴上翻转。上下左右都翻转


![在这里插入图片描述](https://img-blog.csdnimg.cn/2021041321564087.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//翻转
void QuickDemo::flip_demo(Mat &image)
{
Mat dst;
flip(image, dst, 0);//上下翻转
imshow(“上下翻转”, dst);
flip(image, dst, 1);//大于0 左右翻转
imshow(“左右翻转”, dst);
flip(image, dst, -1);//小于0 上下左右都翻转 就是180度旋转
imshow(“180度旋转”, dst);
}


### 2、图像旋转getRotationMatrix2D、warpAffine


图像的旋转 opencv也是提供了api的是warpAffine()函数,  
 注意图像的旋转是需要借助矩阵的,因为图像旋转其实每个像素点都是有变化的。矩形其实opencv中提供了api,getRotationMatrix2D()方法传入旋转角度就会返回矩阵的



inline
Mat getRotationMatrix2D(Point2f center, double angle, double scale)
{
return Mat(getRotationMatrix2D_(center, angle, scale), true);
}
Point2f center 图像的中心位置
double angle 旋转角度
double scale 这个还可以进行图像的放缩,输入目标图像的放缩比例


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413224540406.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413224933495.bmp?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 计算旋转后图形的宽高几何公式来源图  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210413225041659.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 实现旋转代码



//旋转
void QuickDemo::rotate_demo(Mat &image)
{
Mat dst, M;
int w = image.cols;
int h = image.rows;
M = getRotationMatrix2D(Point2f(w/2, h/2), 45, 1.0);//得到旋转45度的矩阵
//计算旋转后的图片大小 这个是得到旋转Mat可以知道的对应的cos、sin
double cos = abs(M.at(0, 0));
double sin = abs(M.at(0, 1));
//计算旋转后的新宽高 根据几何知识
int nh = cos*w + sin*h;
int nw = sin*w + cos*h;
//计算偏移量 修改中心的偏移量 因为warpAffine需要传入的mat的中心坐标
M.at(0, 2) += (nw/2 - w/2);
M.at(1, 2) += (nh / 2 - h / 2);
warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 0, 0));
imshow(“旋转演示”, dst);//没有遮挡的
}


### 十九、摄像头及视频处理


opencv保存视频是只处理视频不处理音频的,如果要处理音视频那么就只能使用ffmpeg了,但是在使用ffmpeg的时候可以合理利用opencv处理视频图像的手段,更好的完成功能。  
 注意opencv保存视频也是有大小限制的,最大2G。


视频的访问及相关属性



VideoCapture类 用于抽象化视频文件的对象
VideoCapture captureCamear(0);
传入0表示获取默认摄像头设备 打开摄像头流之后captureCamear对象就抽象为摄像头设备进行读取视频流等操作

VideoCapture captureCamear(“C:/Users/20531/Desktop/1.mp4”);
也可以传入视频文件,但是注意读取视频文件的时候可能读取失败 读取出来的宽高为0 ,这需要添加一个库
/*
//如果读取视频文件的帧宽高失败则需要
将opencv安装目录D:\opencv\build\x64\vc14\bin中的opencv_videoio_ffmpeg440_64.dll复制
到生成项目的.exe所在的文件(Debug/Release)中。
(因为我用OpenCV版本是4.4,所以ffmpeg440)
*/

VideoCapture的系列API
如captureCamear.get(属性)//有很多宏控制,可以返回对应的视频参数,
当然也可以set,但是注意如果是摄像头的话 set的参数摄像头硬件是否支持
captureCamear.read(Mat)//读取视频帧图像
将视频读取成一帧帧的mat图像之后就可以对其每个mat做处理就达到对视频流做处理的效果。

注意VideoCapture对象占用资源 需要在不再使用之前进行realease()


视频的写入



VideoWriter类对象完成对视频的写入
其中一种构造函数
CV_WRAP VideoWriter(const String& filename, int fourcc, double fps,
Size frameSize, bool isColor = true);

const String& filename写入视频的文件全路径
int fourcc, 采用的编解码类型,这个可以从源视频流对象那里获取、captureCamear.get(CAP_PROP_FOURCC)
double fps,帧率
Size frameSize,宽高 最好也从原视频获取,但是如果就是要对视频进行放缩的话 要与后面的mat写入对应

VideoWriter writer(“D:/test.mp4”, captureCamear.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);

//写入API write即可
writer.write(frameCamear);//写入文件


注意视频写入的时候可能会报一个错误 提示  
 could not open codec “libopenh264” : Unspecified error错误,  
 则需要根据提示下载对应版本的dll库,放到.exe的运行目录下即可  
 [参考博客](https://bbs.csdn.net/topics/618668825)  
 [openh264库下载网站](https://bbs.csdn.net/topics/618668825)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414211433711.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)


缺少一个从一个视频扣ROI图到另外一个视频上叠加的案例  
 实践



//视频读取
void QuickDemo::video_demo(Mat &image)
{
//VideoCapture captureCamear(0);//传入0表示读取摄像头 还可以传入视频文件的全路径字符串
/*
//如果读取视频文件的帧宽高失败则需要
将opencv安装目录D:\opencv\build\x64\vc14\bin中的opencv_videoio_ffmpeg440_64.dll复制
到生成项目的.exe所在的文件(Debug/Release)中。
(因为我用OpenCV版本是4.4,所以ffmpeg440)
*/
VideoCapture captureCamear(“C:/Users/20531/Desktop/1.mp4”);
//get视频属性 注意也是可以set设置的 但是注意如果是摄像头的话他硬件是否支持set的参数
int frame_width = captureCamear.get(CAP_PROP_FRAME_WIDTH);
int frame_height = captureCamear.get(CAP_PROP_FRAME_HEIGHT);
int frame_counts = captureCamear.get(CAP_PROP_FRAME_COUNT);
int fps = captureCamear.get(CAP_PROP_FPS);
std::cout << "frame_width = " << frame_width << std::endl;
std::cout << "frame_height = " << frame_height << std::endl;
std::cout << "frame_counts = " << frame_counts << std::endl;
std::cout << "fps = " << fps << std::endl;

//CAP\_PROP\_FOURCC 获取原来的编码类型 注意size的大小不能变
VideoWriter writer("D:/test.mp4", captureCamear.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);

Mat frameCamear;
Mat frameVedio;
while (true)
{
	captureCamear.read(frameCamear);//读取视频 每一帧 从而操作视频就是操作图像了
	//captureVedio.read(frameVedio);
	flip(frameCamear, frameCamear, 1);//左右翻转一下 与实际保持一致
	writer.write(frameCamear);//写入文件
	if (frameCamear.empty())
	{
		break;
	}
	imshow("frame", frameCamear);
	int c = waitKey(10);
	if (c == 27)
		break;
}
//captureVedio.release();
captureCamear.release();//摄像头资源需要释放
writer.release();//视频写入变量也需要释放资源

}


### 二十、图像直方图


### 1、直方图概念


在计算机当中图像的保存也都是只有数字0和1,其中如rgb这样的存储模式中每位数字也都是有范围的0-255,因此那么我们就应该可以统计出一副图像当中RGB中那个数字出现的频率最高,展示出来这也就是直方图了。直方图反应的就是0-255个灰度等级在一幅图像这么多像素点当中他们各种出现的次数是多少。最后得出的直方图也就很直观的可以得到某个灰度等级在这张图片的所有像素当中出现的次数。


直方图的作用;是图像像素值的统计学特征,在于对图像进行众多处理如翻转平移放缩等手段的时候直方图始终是可以保持他的不变性的。广泛应用与图像处理的各个领域,特别是灰度图像的阈值分割,基于颜色的图像检索及图像分类,反向投影跟踪。


相同的直方图不一定是同一张图像,因为直方图只是保留了图像的像素值特征,而丢失了图像的空间坐标等特征。因此直方图只是图像的特征,但是真正能够唯一表示某种图像的特征只有图像特征本身,opencv中也支持很多种。


直方图分为 灰度直方图 彩色直方图


### 2、一维直方图



CV_EXPORTS void calcHist( const Mat* images, int nimages,
const int* channels, InputArray mask,
OutputArray hist, int dims, const int* histSize,
const float** ranges, bool uniform = true, bool accumulate = false );
const Mat* images; 传入的指针是可以接收多张图片的 同时显示他们的直方图解析
int nimages 传入的图片个数
const int* channels 传入的通道数
InputArray mask 掩码 与之前一致 只处理mask非0的区域
OutputArray hist 直方图输出
int dims 几维的
const int* histSize, 取值范围个数
const float** ranges 直方图的取值范围
案例
//分割通道
std::vector bgr_plane;
split(image, 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;

//计算直方图
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);


实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210414222318286.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//获取直方图
void QuickDemo::histogram(Mat &image)
{
//分割通道
std::vector bgr_plane;
split(image, 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;

//计算直方图
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]);//每个bin是256个,总宽度除以它得到每个灰度值在图像中占几个像素点,最终是要画图的
Mat hisImage = Mat::zeros(hist_h, hist_w, CV_8UC3);//绘制直方图的底布

//归一化处理 因为三个通道出现的相同灰度值次数差值太大了因此需要归一化到一段范围内 (底布高度范围这么大)来显示在一张图片上
normalize(b_hist, b_hist, 0, hisImage.rows, NORM_MINMAX, -1, Mat());
normalize(g_hist, g_hist, 0, hisImage.rows, NORM_MINMAX, -1, Mat());
normalize(r_hist, r_hist, 0, hisImage.rows, NORM_MINMAX, -1, Mat());
//绘制直方图曲线
for (int i = 1; i < bins[0]; i++)
{
	//点的坐标都是基于屏幕坐标的 因此要做转换 hist\_h -cvRound(b\_hist.at<float>(i-1)))
	line(hisImage, 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(hisImage, 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(0, 255, 0), 2, 8, 0);
	line(hisImage, 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(0, 0, 255), 2, 8, 0);
}
//显示直方图
namedWindow("Histogram Deemo", WINDOW_AUTOSIZE);
imshow("Histogram Deemo", hisImage);

}


### 3、二维直方图


之前的一维直方图是我们将rgb图片split分割成三个通道了,然后进行显示,现在说的二维直方图是讲的hsv图像,将rgb图像转换为hsv图像,因为hsv格式只有hs是表示颜色的,v表示的是亮度。(注意h的取值范围是0-180,s的取值范围是0-255)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210415220838224.PNG?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)  
 实践  
 这部分没有看太懂,直方图与原图如何对应解析的  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210417164808670.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//获取二维直方图
void QuickDemo::histogram_2d_demo(Mat &image)
{
//2D直方图
Mat hsv, hsv_hist;
cvtColor(image, hsv, COLOR_BGR2HSV);
int hbins = 30;//h的灰度只是0-179,一共180个灰度值,30个灰度值为一个单位统计在该范围内的像素频率
int sbins = 32;//s的灰度值实时0-255
int hist_bins[] = {hbins, sbins};//2d将两组bins设置进去
float h_range[] = {0, 180};//灰度值的取值范围
float s_range[] = { 0, 256 };
const float* hs_range[] = {h_range, s_range};
int hs_channels[] = { 0, 1 };
//hsv 只有一张图像 第0个和第一个通道、mask就是与之前的一样 只对非0的做处理 产生的mat 2维的 第1、2个通道多少个bins 第1、2个通道多少个取值范围
这个就是在得出每个灰度值对应的像素点的个数
calcHist(&hsv, 1, hs_channels, Mat(), hsv_hist, 2, hist_bins, hs_range, true, false);
//2维的一图图像里面 则首先要进行归一化处理
double maxVal = 0;
minMaxLoc(hsv_hist, 0, &maxVal, 0, 0);//最大值
int scale = 10;
Mat hist2d_image = Mat::zeros(sbins*scale, hbins*scale, CV_8UC3);//创建空白图像 将数据往上面填入即可
for (int h = 0; h < hbins; h++)
{
for (int s = 0; s < sbins; s++)
{
float binVal = hsv_hist.at(h, s);
int intensity = cvRound(binVal * 255 / maxVal);//取整
rectangle(hist2d_image, Point(h*scale, s*scale),
Point((h+1)*scale -1, (s+1)*scale -1),
Scalar::all(intensity), -1);
}
}
applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);//变成色彩图
imshow(“H-S Histogram”, hist2d_image);
}


### 4、直方图均衡化


图像直方图均衡化主要是可以用于图像增强,对图像直方图均衡处理之后会提升后续对象检测的准确率,提升图像质量。  
 该opencv提供的均衡化API(equalizeHist)只支持单通道。



void equalizeHist( InputArray src, OutputArray dst );
原图像和目标图像,但是注意该API只支持单通道,则使用前需要把输入图像转换为灰度图cvtColor(image, gray, COLOR_BGR2GRAY);//灰度图像


实践;可以看出均衡化大致就是把图像亮暗更加明显一点  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210417183141144.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



//获取直方图的均衡化
void QuickDemo::histogram_eq_demo(Mat &image)
{
namedWindow(“灰度图像”, WINDOW_FREERATIO);
namedWindow(“直方图均衡化”, WINDOW_FREERATIO);//图片太大了
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);//灰度图像
imshow(“灰度图像”, gray);
Mat dst;
equalizeHist(gray, dst);
imshow(“直方图均衡化”, dst);
}


问题;后期再补  
 这里只支持单通道那就是灰度图像,那么彩色图像如何均衡化  
 大致就是将BGR转为hsv通道 对v通道进行均衡化处理完成之后在合并到hsv,最后再转换为BGR完成彩色图像的直方图均衡化。


如何局部范围直方图均衡化


### 二十一、卷积操作


### 1、图像卷积操作


这是均值卷积,每个像素的卷积核都是相同的,  
 线性卷积;就是卷积和与图像对应位置进行点乘累加得到的结果除以卷积和个数,得到的结果替换到之前进行操作的图像区域的中心位置。这个位置在opencv中有一个名称叫锚点anchor(默认是中心输出)


这种处理在原图像的边缘是与锚点没有关系的,因此对待这些边缘有两种做法一是扔掉,二是做填充边缘化处理  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210417185457504.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3p3MTk5Ng==,size_16,color_FFFFFF,t_70#pic_center)



CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
Size ksize, Point anchor = Point(-1,-1),
int borderType = BORDER_DEFAULT );
InputArray src, OutputArray dst原图像 目标图像
Size ksize 卷积核的大小 默认值都是1的 Size(3,3)就是3X3的卷积核
注意;卷积核越大图像越模糊 根据Size的传入还可以进行水平方向卷积Size(15, 1),竖直方向卷积Size(1,15)
Point anchor = Point(-1,-1) 锚点 默认-1就是卷积核的中心位置
int borderType = BORDER_DEFAULT opencv对边缘化的处理方式


实践  
 ![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/e64916af0da3d408c10ee0dc0821bb91.png#pic_center)



//计算卷积
void QuickDemo::blur_demo(Mat &image)
{
Mat dst;
namedWindow(“卷积”, WINDOW_FREERATIO);

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

16,color_FFFFFF,t_70#pic_center)

CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
                        Size ksize, Point anchor = Point(-1,-1),
                        int borderType = BORDER_DEFAULT );
InputArray src, OutputArray dst原图像 目标图像
Size ksize  卷积核的大小 默认值都是1的  Size(3,3)就是3X3的卷积核
注意;卷积核越大图像越模糊  根据Size的传入还可以进行水平方向卷积Size(15, 1),竖直方向卷积Size(1,15)
Point anchor = Point(-1,-1) 锚点 默认-1就是卷积核的中心位置
int borderType = BORDER_DEFAULT opencv对边缘化的处理方式

实践
在这里插入图片描述

//计算卷积
void QuickDemo::blur\_demo(Mat &image)
{
	Mat dst;
	namedWindow("卷积", WINDOW_FREERATIO); 


[外链图片转存中...(img-Jz8jm08L-1715839148476)]
[外链图片转存中...(img-8owE4zzK-1715839148477)]

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值