2024年Opencv4学习-3、进阶图像基本操作1_opencv怎么找图片最远端,C C++常见面试题知乎

img
img

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

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

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

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);

}


### 2、二维直方图


一维直方图是统计灰度图中0-255像素值出现在整个图像中的频率,而二维的就是将图像转换为HSV色彩空间之后对HS色彩度和饱和度两个维度进行统计,统计对应H与对应V组合值出现在整个图像中的频率,因为要结合HV两个维度因此是二维的直方图。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a27af99e27f74ab9b18df10253593c02.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 实践代码



//获取二维直方图
void QuickDemo::histogram_2d_demo(Mat &image)
{
//2D直方图
imshow(“原图”, image);
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个通道多少个取值范围
//这个就是在得出每个h与v对应的像素点的个数
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);
}
}
imshow(“黑白二维直方图”, hist2d_image);
applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);//变成色彩图
imshow(“彩色二维直方图”, hist2d_image);
}


### 3、图像直方图均衡化


图像直方图均衡化可以用于图像增强、对输入图像进行直方图均衡化处理,提升后续对象检测的准确率、在医学图像和卫星摇杆图像是很有增强的。在人脸检测的官方代码中就是摄像头获取每帧图像然后对每帧图像进行的操作就是将图像进行灰度化,灰度化之后进行一个直方图均衡化,然后再送给人脸检测器从而达到提升图像质量从而提供人脸检测的准确率。  
 该算法对图像的亮度进行归一化,提高了图像的对比度。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/79d7e67aff8940498b3c37089924d915.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 图像直方图均衡化原理


![在这里插入图片描述](https://img-blog.csdnimg.cn/060b1f5eec3646c5a527e9efa3fb46aa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 图像直方图均衡化实践效果图


实践代码



//对彩色图像进行直方图的均衡化
void QuickDemo::histogram_eqRGB_demo(Mat &image)
{
namedWindow(“RGB图像”, WINDOW_FREERATIO);
namedWindow(“直方图均衡化”, WINDOW_FREERATIO);//图片太大了
Mat ycrcb;
cvtColor(image, ycrcb, COLOR_BGR2YCrCb);
std::vector vcChannels;
split(ycrcb, vcChannels);
Mat dst;
/*
均衡化灰度图像的直方图。
该算法对图像的亮度进行归一化,提高了图像的对比度。
输入为8位单通道图像。输出于输入相同大小类型
*/
equalizeHist(vcChannels[0], vcChannels[0]);//只能处理灰度图
merge(vcChannels, ycrcb);
cvtColor(ycrcb, dst, COLOR_YCrCb2BGR);
imshow(“RGB图像”, image);
imshow(“直方图均衡化”, dst);
}


### 4、图像直方图比较


一次计算多个通道的直方图并且还合并到一个图像进行输出,还支持这种方式。因此我们的直方图计算还可以一次计算多个通道的直方图。  
 图像直方图比较,就是计算两幅图像的直方图数据,比较两组数据的相似性,从而得到两幅图像之间的相似程度。这个在早期的CBIR(基于内容的图像检索)中就有使用,特别是在边缘处理、图像特征和词袋技术(词袋模型是一种用机器学习算法对文本进行建模时表示文本数据的方法)一起使用。


API介绍



CV_EXPORTS_W double compareHist( InputArray H1, InputArray H2, int method );
返回值double类型的数据表示我们用第三个参数比较两幅直方图相似性之后得出来的结论,
参数H1,H2就是两幅直方图的数据,对于数据而言,比较他们的相似性相关性计算。
我要比较两个两幅直方图,就是有两堆数据在哪里你要进行比较,那么你最先想到的就是数据相似关的计算。
则第三个参数就是数据相关性计算的方法类别。
HistCompMethods{ Correlation、 Chi-Square、 Intersection 、Bhattacharyya distance }


在opencv当中我们比较直方图数据的相关性程度的最常用的几种方法就是;相关性,卡方,交叉,巴士。  
 **correlation相关性方法**: 相似性程度越高那么最后的结果就越趋近于正1,而如果相关的程度非常非常低那么他的结果也就趋近于-1。-1就表示这两个东西压根就没有相关性的存在。+1就表示两幅图像非常相似。这个取值范围是在0-1之间的,就是实际上他的值是需要0-1之间,但他的相关性计算并没有做归一化处理的,因此我们会在直方图比较之前先做归一化处理到0-1之间,这样出来的值才会满足条件。  
 **卡方计算**:卡方就是这两组数据的方差,这个是比较常规的计算欧几里距离(欧几里得距离来计算两个向量之间的相似度),我们之前是两个点之间就是两维的欧几里得距离,三个点三维的,如这个方差就相当于是N维的欧几里得距离。卡方计算出来的值是可能很大也可能很小,同样也是没有做归一化处理的。他计算的值越小就是相似度越高的,值越大的就相似度越低的。  
 **交叉**;就是取这两个当中较小的那个。如果这两个min,我们一计算他们的mins加起来就是我们的交叉性验证。最终我们交叉性验证得出来的他们的和,如果他是一个完完整整的值的话,那么我们交叉性验证的结果他就应该是在 如果图像完全相同的话那么他就是所有的和加起来,如果不相同的话那么他们两个最小的和加起来是都比较大或者小,越小那么相似的可能性大一点,反之可能性小,交叉的方式十分不准的。  
 **巴氏距离**;这个而是比较准的,,我们在做直方图比较最好是选择第一个相关性或巴氏距离这个是比较准确的。在数学上本来是巴氏距离越靠近1的相似相关性越高,但是opencv中做了一个1减,那么也就变成了越靠近0的相似度越高,0就是没有距离的就非常相似,而1是有距离的则不相似。


**实践**  
 常规经验性的处理都是要转换到HSV的类型,H S分量就是颜色。  
 如果是int类型的数归一化到0-1之间那么就需要dtip,还有提前把1转换为浮点数才行。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/a7f47dc0da544e8a9e3bbbc7c7356eaa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 直方图比较效果图


直方图比较源代码



//对直方图进行比较
void QuickDemo::histogram_compare_demo()
{
//读取图片
Mat src1 = imread(“D:/images/m1.png”);
Mat src2 = imread(“D:/images/m2.png”);
Mat src3 = imread(“D:/images/pedestrian.png”);
Mat src4 = imread(“D:/images/persons.png”);

namedWindow("input1", WINDOW_FREERATIO);
namedWindow("input2", WINDOW_FREERATIO);
namedWindow("input3", WINDOW_FREERATIO);
namedWindow("input4", WINDOW_FREERATIO);
imshow("input1", src1);
imshow("input2", src2);
imshow("input3", src3);
imshow("input4", src4);

//转换hsv
Mat hsv1, hsv2, hsv3, hsv4;
cvtColor(src1, hsv1, COLOR_BGR2HSV);
cvtColor(src2, hsv2, COLOR_BGR2HSV);
cvtColor(src3, hsv3, COLOR_BGR2HSV);
cvtColor(src4, hsv4, COLOR_BGR2HSV);

//输出直方图
int h_bins = 60;
int sbins = 64;
int histSize[] = {h_bins, sbins};
float h_ranges[] = { 0, 180 };
float s_ranges[] = { 0, 256 };
const float\* ranges[] = { h_ranges , s_ranges };
int channels[] = { 0, 1 };//处理两个通道
Mat hist1, hist2, hist3, hist4;
calcHist(&hsv1, 1, channels, Mat(), hist1, 2, histSize, ranges, true, false);
calcHist(&hsv2, 1, channels, Mat(), hist2, 2, histSize, ranges, true, false);
calcHist(&hsv3, 1, channels, Mat(), hist3, 2, histSize, ranges, true, false);
calcHist(&hsv4, 1, channels, Mat(), hist4, 2, histSize, ranges, true, false);

//归一化处理
normalize(hist1, hist1, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist2, hist2, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist3, hist3, 0, 1, NORM_MINMAX, -1, Mat());
normalize(hist4, hist4, 0, 1, NORM_MINMAX, -1, Mat());

//直方图比较
for (int i = 0; i < 4; i++)
{
	int compare_method = i;//四种算法
	/\*

CV_EXPORTS_W double compareHist( InputArray H1, InputArray H2, int method );
返回值double类型的数据表示我们用第三个参数比较两幅直方图相似性之后得出来的结论,
参数H1,H2就是两幅直方图的数据,对于数据而言,比较他们的相似性相关性计算。
我要比较两个两幅直方图,就是有两堆数据在哪里你要进行比较,那么你最先想到的就是数据相似关的计算。
则第三个参数就是数据相关性计算的方法类别。HistCompMethods{ Correlation、 Chi-Square、 Intersection 、Bhattacharyya distance }
*/
double src1_src2 = compareHist(hist1, hist2, compare_method);
double src3_src4 = compareHist(hist3, hist4, compare_method);
printf(" Method [%d] : src1_src2 : %f, src3_src4: %f, \n", i, src1_src2, src3_src4);
}
}


### 5、直方图反向投影


图像直方图的反向投影其实就是通过构建指定模板图像的二维直方图空间于目标的直方图空间,进行直方图归一化之后,进行比率操作得到的非零数值,生成查找表对原图像进行像素映射之后,再进行图像模糊输出的结果。



> 
> 举例来说就是现在所有人都在用手机,那么手机就有基站,手机有一种定位技术就是通过基站来进行定位的,其实手机底层是有一个发射层来进行信号的发射于接收,并且手机不是于一个基站进行通信的,而是多个,在通信的时候去连接匹配选择最强的那个进行通信。而基站也是可以通过这个信号的传输进行反向定位手机的,并且手机连接的基站还会把手机于其他基站的查找也找到,从而通过几个基站的于手机反射的信号线那么就会有一个交点从而定位到手机。通过这个例子从而在图像中就是通过图像直方图这个特征来反向投影找到自己感兴趣的ROI区域。
> 
> 
> 


### 5.1、直方图方向投影的实现的具体流程


1、 对指定模板图像进行直方图数据解析(其实就是将图像转换为hsv,解析出二维直方图)  
 2、 对要查找的目标图像进行直方图数据解析,同样也是生成hsv的二维直方图  
 3、 目标图像直方图中数据分布中存在与模板图像直方图数据向叠合的地方,那么可能目标图像中存在模板图像的画面。  
 4、 从而需要将两幅直方图数据进行&操作,从而得出的直方图就是两幅图像的交集(就是hs像素频率相同的部分)  
 5、 再最后对得出的直方图中的范围值设置LUT查找表去遍历原图像(在范围类则为,不再则为0)从而可以绘制出模型  
 直方图;白色区域就是表示这个像素值信号很强,出现的频率很高,而黑色灰色的区域就表示该像素值信号弱,出现频率低。  
 中间还可以进行一下卷积模糊,就是将像素值尽量集中一些,便于反向投影操作


![在这里插入图片描述](https://img-blog.csdnimg.cn/b4874322c0924e6c9cf305347543f41b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


### 5.2、Opencv中直方图反向投影的实现


首先计算模板图像的直方图,然后进行归一化,直接拿过去进行LUT查找表进行反向投影查找,opencv就做了进行节省从而提高效率。


![在这里插入图片描述](https://img-blog.csdnimg.cn/802222ba13c644cf9e2462aa67995009.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


代码实践:  
 后续还可以对视频进行实时目标匹配查找的,但是注意实现的时候要注意视频差帧的区别。



//直方图反向投影
void QuickDemo::histogram_backProjection(Mat &image, Mat &model)
{
/*
images 原图像 一个数值
nimages 原图像个数 支持多张图片
channels 图像通道
hist 输入模板的直方图
backProject 反向输出结果
ranges 像素范围 如h为0-180 s0-255
scale 是否放缩
CV_EXPORTS void calcBackProject(const Mat* images, int nimages,
const int* channels, InputArray hist,
OutputArray backProject, const float** ranges,
double scale = 1, bool uniform = true);
*/
imshow(“模板图像”, model);
imshow(“目标图像”, image);
//转换为hsv色彩空间
Mat model_hsv, image_hsv;
cvtColor(model, model_hsv, COLOR_BGR2HSV);
cvtColor(image, image_hsv, COLOR_BGR2HSV);

//对模板图像进行直方图 归一化处理
int h_bins = 32;
int s_bins = 32;
int histSize[] = { h_bins , s_bins };
float h_ranges[] = { 0, 180 };
float s_ranges[] = {0, 256};
const float\* ranges[] = { h_ranges ,s_ranges };
int channels[] = {0,1};
Mat roiHist;
calcHist(&model_hsv, 1, channels, Mat(), roiHist, 2, histSize, ranges, true, false);
normalize(roiHist, roiHist, 0, 255, NORM_MINMAX, -1, Mat());

//直接反向查找
Mat backproj;
calcBackProject(&image_hsv, 1, channels, roiHist, backproj, ranges, 1.0);
imshow("BackProj", backproj);

}


### 6、图像卷积操作


这是均值卷积,每个像素的卷积核都是相同的,  
 线性卷积;就是卷积和与图像对应位置进行点乘累加得到的结果除以卷积和个数,得到的结果替换到之前进行操作的图像区域的中心位置。这个位置在opencv中有一个名称叫锚点anchor(默认是中心输出)。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d7e4e16999004c4b8193354672f71425.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 这种处理在原图像的边缘是与锚点没有关系的,因此对待这些边缘有两种做法一是扔掉,二是做填充边缘化处理。


实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/5ce590f2f19d4a3ab9bdff68b44d6459.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


实践:



//计算卷积
void QuickDemo::blur_demo(Mat &image)
{
Mat dst;
namedWindow(“卷积”, WINDOW_FREERATIO);
//卷积核越大图像越模糊 根据Size的传入还可以进行水平方向卷积Size(15, 1),竖直方向卷积Size(1,15
blur(image, dst, Size(5, 5), Point(-1, -1))😉
imshow(“卷积”, dst);
}


### 7、高斯模糊


非均值卷积,之前的卷积是每个像素的卷积核是相同的,而高斯模糊就是可以达到不同的卷积核。  
 高斯模糊的卷积核就是中心位置最大,离中心位置越远的就越小。  
 高斯核函数  
 中心化效应,考虑了图形中心像素对图像的一个贡献,而均值卷积就没有考虑到,中心位置比例最大。  
 高斯模糊和均值卷积的区别就是他们的卷积核不一样,一个是均值都是1一个是通过高斯核函数计算的,是根据中心占比的得到的。  
 实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2d71c2fb3c6a42c1b777419442178916.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)



//计算高斯模糊
void QuickDemo::gaussian_blur_demo(Mat &image)
{
Mat dst;
//卷积核的大小,注意一定要是基数 偶数就是错误的(违反了高斯中心化的原则)
//sigma 当窗口设置Size(0,0)的时候opencv就会从sigma反算窗口大小,
//当窗口大小已经设定一个值之后那么sigma这边无论设什么都没有效果,他会从窗口计算得到sigma的
//并且窗口或者sigma都是值越大图形越模糊,sigma对图形的模糊效果更加明显所以很多时候都会设置size而是直接设计sigma的来查看模糊效果
GaussianBlur(image, dst, Size(5, 5), 15);
imshow(“高斯模糊”, dst);
}


### 8、中值滤波


中值滤波本质上是统计排序滤波器的一种,中值滤波对图像特定噪声类型(椒盐噪声)会取得比较好的去噪效果,也是常见的图像去噪声与增强的方法之一。常见的统计排序滤波器还有最小值滤波和最大值滤波。  
 中值滤波的工作方式与卷积的非常相似:也是类似移动的操作,但是不会去做卷积那种点乘操作而是对覆盖区域的ROI区域的像素值进行排序取中值作为其ROI区域中心像素值的输出。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9d2482bcc0d94b4da52682c9e3f9e3ee.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 最终的效果其实就是使得局部的像素区域减少波动,让局部区域的像素更加平滑,减少噪声。  
 实践![在这里插入图片描述](https://img-blog.csdnimg.cn/c960ed18510449e793f039b9141829dd.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 实践代码



//中值滤波
void QuickDemo::medianblur_demo(Mat &image)
{
Mat dst;
imshow(“原图像”, image);
//注意 ksize参数必须是奇数并且大于1的,只有奇数才有中值
medianBlur(image, dst, 3);
imshow(“中值滤波”, dst);
}


### 9、图像噪声


前面介绍的三种卷积方法(均值卷积,高斯卷积,中值卷积)都是在图像预处理当中都是可以对图像进行去噪的。  
 **图像的噪声**:就是本来拍摄出来的图像应该是这样的,但是经过设备拍摄的干扰,传输时干扰导致的成像出来的图像质量有问题,反应到图像上就是图像的亮度与颜色呈现某种程度的不一致性。  
 **根据图像噪声的分类可以分为**


### 9.1、椒盐噪声


是一种随机在图像中出现的稀疏分布的黑白像素点, 对椒盐噪声一种有效的去噪手段就是图像中值滤波(因为均值滤波就是取中值,而椒盐噪声又是黑白像素点,因此中值滤波去椒盐噪声的效果是比较好的)。它的本质其实就是在图像中部分像素是0或255的杂像素点。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/262adf68c074459aaaca2e371465b9de.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 代码实践:



//产生椒盐噪声
void QuickDemo::add_saltPepperNoise(Mat &image)
{
imshow(“原图”, image);
RNG rng(12345);//分配随机数种子
int h = image.rows;
int w = image.cols;
int nums = 10000;
for (int i = 0; i < nums; i++)
{
int x = rng.uniform(0, w);//0到w之间参数一个随机数
int y = rng.uniform(0, h);
if (i % 2 == 1) {
image.at(y, x) = Vec3b(255, 255, 255);//椒盐噪声的核心就是产生黑白像素点
}
else
{
image.at(y, x) = Vec3b(0, 0, 0);
}
}
imshow(“salt pepper”, image);
}


### **9.2、高斯噪声/符合高斯分布**


一般会在数码相机的图像采集(acquisition)阶段发生的一些错误,这个时候它的物理/电/光等各种信号都可能导致产生高斯分布噪声。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/22146121c70645d1b246a924ba42fcbf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


代码实践:



//产生高斯噪声
void QuickDemo::add_gaussianNoise(Mat &image)
{
imshow(“原图”, image);
Mat nosi = Mat::zeros(image.size(), image.type());
randn(nosi, (15, 15, 15), (30, 30, 30));//用正态分布的随机数填充数组。 产生均值15, 方差30的高斯图像
Mat dst;
add(image, nosi, dst);//叠加
imshow(“gaussian Noise”, dst);
}


### **9.3、均匀分布噪声**


这种噪声都是由于某种规律性的错误导致的,这样消除在硬件上成本就比较高一点一般会考虑在软件上进行消除的。


### 10、图像去噪声


图像去噪对图像二值化与二值分析是很有作用的,opencv当中常见的去噪方法有。  
 **均值去噪声**:也就是我们的卷积模糊的作用,  
 **高斯模糊去噪**;非均值卷积模糊的作用  
 **非局部均值去噪声**:opencv中提供了专门的API;fastNlMeansDenoisingColored  
 **双边滤波去噪**;  
 **形态学去噪**:主要是针对二值图像的灰度图像效果相对明显一点。


均值和中值去噪的区别。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/414a7b464dc84525be23e960f7d0d25b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


实践  
 卷积我们最好是选择3*3的,最多选择5*5,再选择大了的话就会影响原本图像了。


![在这里插入图片描述](https://img-blog.csdnimg.cn/38aeaab30bfd40d3a790faeb468b108b.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 代码实际 :



//去除噪声
void QuickDemo::pictureNoise_wipe(Mat &image)
{
Mat ret1, ret2, ret3, ret4;
blur(image, ret1, Size(3,3));//最好选择3*3的卷积,最多选择5*5 再大就会影响原图像数据了
imshow(“卷积模糊去噪”, ret1);

GaussianBlur(image, ret2, Size(3, 3), 0);
imshow("高斯模糊去噪", ret2);

medianBlur(image, ret3, 3);
imshow("中值卷积去噪", ret3);

/\*

非局部均值去噪声:他是有两个版本的一个是针对灰度图,一个是有颜色的图像
第3、4个参数分别是表示我们的颜色分量和亮度分量,取一个阈值分量,这个分量值越大去噪的程度越大那么图像原本的细节也会被去除掉。通常不用超过10或15,默认3
第5,6个参数,搜索窗口大小,和模板窗口的大小 ; 注意模板窗口一个不能大过搜索窗口,因为模板窗口是在搜索窗口里面进行搜索的
非局部均值去噪;实际上是一个大的窗口上有一个小的搜索窗口和模糊窗口,通过这两个窗口进行搜索或移动来达到非局部均值去噪
*/
fastNlMeansDenoisingColored(image, ret4, 15,15,10, 30);
imshow(“非局部均值去噪声”, ret4);
}


### 11、边缘保留滤波算法


边缘保留滤波算法(EPF):之前介绍的滤波去噪都是模糊卷积(均值卷积或高斯卷积),他们都是模糊之后图像的边缘信息不复存在,受到了破坏。而边缘保留滤波算法就是在通过卷积处理实现图像模糊的同时对图像边缘不会造成破坏,滤波之后的输出完整的保存了图像整体边缘(轮廓)信息。常见的边缘保留滤波算法有以下几种:  
 **高斯双边模糊**、PS的磨皮算法一般用这个精度高只是耗时需要一定条件。  
 **MeanShitf均值迁移模糊**、实际当中是基于金字塔的  
 **局部均方差模糊**、基于积分图思想的快速计算的边缘保留滤波算法,在深度学习没有大热起来之前美图pp他是最常用的。  
 **opencv当中专门提供的一个边缘保留滤波的API函数**;对我们去噪是很有作用的


### 11.1、高斯双边模糊


高斯模糊是考虑图像空间位置对权重的影响(离输出中心点越近的那么他所占的权重也就越大,考虑的输出位置对我们卷积的影响),但是它没有考虑图像像素分布对图像卷积输出的影响(其实正确的方式应该只有周围像素值相差不大的才进行卷积计算的,说明他们有相同的像素分布,如果他们像素值相差比较大那么他们就应该从计算当中剥离出来不参与本个像素点值的卷积计算),双边模糊考虑了像素值分布的影响,对像素值空间分布差异较大的进行保留从而完整的保留了图像的边缘信息。  
 不是整个图形都进行模糊,这样把图像原有的信息丢掉了。高斯双边模糊就是处理这个问题的,他模糊的同时保留了较大区别的边缘(如亮暗的边缘)而模糊掉了一些细节处理。  
 双边是指的空间的和色彩的。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/546b20f43e01465d872dd05414451d92.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


实践  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/c7360d731126414483822709adf212b3.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)



CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace,
int borderType = BORDER_DEFAULT );
InputArray src, OutputArray dst, 原图像和目标图像
int d,窗口大小,之前说了可以填0 由后面的sigma来反推计算
double sigmaColor, 这个值要大一点,用于色彩的卷积核
double sigmaSpace, 空间的卷积核
int borderType = BORDER_DEFAULT 边缘的处理方式


### 11.1.1、使用高斯双边模糊还可以实现防盗水印的去除的


![在这里插入图片描述](https://img-blog.csdnimg.cn/3d63263d938c4e9ebaafb3abfeb4f710.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


图19;图片来自于贾志刚老师的知识星球内容



//计算双边高斯卷积
void QuickDemo::Bilateralgaussian_blur_demo(Mat &image)
{
/*namedWindow(“原图”, WINDOW_FREERATIO);
imshow(“原图”, image);*/
Mat dst;
//100表示色彩的卷积核 要大一点
//10表示空间距离的卷积核
//d表示我们输出的维度是否放大缩小,一般我们都是与输入保持一致的
// sigmaColor, 表示色彩空间色彩相差多大范围之内的他才参与计算,这个可以填大一点
// sigmaSpace,表示高斯模糊里面的 sigmaX,sigmaY,空间的的sigma,这个值要选择小一点 5,10,15
bilateralFilter(image, dst, 0, 100, 10);
Mat combine1, combine;
hconcat(image, dst, combine1);//水平拼接函数
vconcat(image, dst, combine);//垂直拼接函数
namedWindow(“高斯模糊1”, WINDOW_FREERATIO);
imshow(“高斯模糊1”, combine);
namedWindow(“高斯模糊”, WINDOW_FREERATIO);
imshow(“高斯模糊”, combine1);
}


图像水平竖直拼接函数实际  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9ecc8fc1660644f7b6839cb25535a020.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


### 11.2、均值迁移模糊(mean-shift blur)


**概念**:是边缘保留滤波算法中的一种,经常用来对图像分水岭分割之前对图像去噪,并且均值迁移模糊不仅可以对图像进行模糊还可以实现对图像的目标跟踪,图像模式识别 视频分析对比等多环节都可以进行应用是很经典的图像处理算法。  
 **基本原理**  
 前面卷积就是要在图片上开窗点乘,这是空间域卷积上要做的事情。而我们均值迁移模糊就是在图像进行开窗的时候同样,不仅考虑像素值空间范围分布(窗口位置中心点与窗口位置的空间关系),而且还要考虑空间关系中像素值的分布, 只有符合像素值分布的像素点才参与计算,他们参与计算之后得到像素均值与空间位置xy也有均值(对于彩色图像而言我们就是要找到均值迁移当中的五个向量RGB三通道和空间的xy分别在这些方向上移动了多少,再对比原来的中心位置的差那么就可以得到新的均值位置),使用新的均值位置作为窗口中心位置继续基于给定像素值空间分布计算均值与均值位置,如此不断迁移中心位置直到不再变化位置(dx=dy=0)(就是向密度高的方向不断迁移直到来到密度最高的中心位置,因为这个时候得到的均值与所有像素去得到的均值也是一致的了,当一致之后就不会再有data数据移动了,如果有一个像素点没有参数计算那么他的均值不可能是0,就是因为他周围的像素点分布已经与中心点已经很相似了,符合我们要求,因此这个算法是基于均值的因此会不断迁移),但是在实际情况中数据可能不会那么理想化,因此我们会人为设置一个停止条件比如迁移几次,这样就可以把最后的RGB均值赋值给中心位置。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/12e2e01c9f364bfaa4ce1c09031c4f57.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/2e37190c37304bb9b782fdc4251363ae.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 **实际**  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/e3d43bbd76fe41b9bfabe9e9dee8baf0.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 代码



//均值迁移滤波
void QuickDemo::MeanShift(Mat &image)
{
/*
sp 空间大小就是我们的空间域
sr颜色大小就是我们色彩空间分布值
在什么情况停止:5是迁移5次,或者两次迁移从差值小于1的时候停止
void pyrMeanShiftFiltering( InputArray src, OutputArray dst,
double sp, double sr, int maxLevel = 1,
TermCriteria termcrit=TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS,5,1) );
*/
Mat ret1, combine1;
pyrMeanShiftFiltering(image, ret1, 15, 50, 1, TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 5, 1));
hconcat(image, ret1, combine1);//水平拼接函数
imshow(“均值迁移滤波”, combine1);
}


### 11.3、图像积分图算法


**概念**:为了在多尺度透视投影中提高渲染速度,是一种快速计算图像区域和与平方和的算法。他可以很快速度的计算一幅图像的任意区域的像素和或者平方和,也就是我们卷积窗口下对应区域的像素和和平方和他都可以很快获取到。  
 \*\*其核心思想:\*\*对每个图像建立自己的积分图查找表(如果是计算和那么就要积分和图查找表、如果是计算平方和那么就是建立自己的 积分图平方和查找表),在图像积分处理计算阶段根据预先建立的积分图查找表,直接查找从而实现对均值卷积线性时间计算(我们进行卷积的时候就可以实现对图像线性时间的查找计算),做到了卷积执行的时间与半径窗口大小的无关联。图像积分图在图像特征提取HAAR/SURF、二值图像分析、图像相似相关性NCC计算、图像卷积快速计算等方面均有应用,是图像处理中的经典算法之一。  
 **图像积分图建立与查找 :**  
 在积分图像(Integral Image - ii)上任意位置(x, y)处的ii(x, y)表示该点左上角所有像素之和, 其中(x,y)是图像像素点坐标。  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/aab26ef9fb8745c7aabb3ab46e216895.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/db9588e3c7b640b0b932743d667df230.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)


基于图像平方和表来做一个图像均值模糊,也就是基于图像积分图  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/e62d3512f2c04b42a6c3d802aba231fa.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y2W6YWS55qE5bCP56CB5Yac,size_20,color_FFFFFF,t_70,g_se,x_16)  
 代码



//图像积分图算法
int getblockSum(Mat &sum, int x1, int y1, int x2, int y2, int i) {//获取指定区域的和
int tl = sum.at(y1, x1)[i];
int tr = sum.at(y2, x1)[i];
int bl = sum.at(y1, x2)[i];
int br = sum.at(y2, x2)[i];
int s = (br - bl - tr + tl);
return s;
}
void blurintegral_demo(Mat &image, Mat &sum) {//图像积分图算法
int w = image.cols;
int h = image.rows;
Mat result = Mat::zeros(image.size(), image.type());
int x2 = 0, y2 = 0;
int x1 = 0, y1 = 0;
int ksize = 5;//图像直径
int radius = ksize / 2;
int ch = image.channels();
int cx = 0, cy = 0;
//遍历像素点
for (int row = 0; row < h + radius; row++) {
y2 = (row + 1)>h ? h : (row + 1);
y1 = (row - ksize) < 0 ? 0 : (row - ksize);
for (int col = 0; col < w + radius; col++) {
//然后要找到区域四个点的位置传过去得到区域积分和
x2 = (col + 1)>w ? w : (col + 1);
x1 = (col - ksize) < 0 ? 0 : (col - ksize);
cx = (col - radius) < 0 ? 0 : col - radius;
cy = (row - radius) < 0 ? 0 : row - radius;
int num = (x2 - x1)*(y2 - y1);//有多少个像素参与计算
for (int i = 0; i < ch; i++) {
// 积分图查找和表,计算卷积
int s = getblockSum(sum, x1, y1, x2, y2, i);
//和表除以个数就是得到中心像素点的值 均值滤波的一个输出 也就是这块区域积分和表均值
result.at(cy, cx)[i] = saturate_cast(s / num);
}
}
}
Mat combine1;
hconcat(image, result, combine1);//水平拼接函数
imshow(“output”, combine1);
//imwrite(“D:/result.png”, result);
}
void QuickDemo::integral_demo()
{
/*
sum 和表opencv中有API可以输出和表的

img
img

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

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

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


for (int i = 0; i < ch; i++) {
// 积分图查找和表,计算卷积
int s = getblockSum(sum, x1, y1, x2, y2, i);
//和表除以个数就是得到中心像素点的值 均值滤波的一个输出 也就是这块区域积分和表均值
result.at(cy, cx)[i] = saturate_cast(s / num);
}
}
}
Mat combine1;
hconcat(image, result, combine1);//水平拼接函数
imshow(“output”, combine1);
//imwrite(“D:/result.png”, result);
}
void QuickDemo::integral_demo()
{
/*
sum 和表opencv中有API可以输出和表的

[外链图片转存中…(img-YUSr6Ddk-1715585281623)]
[外链图片转存中…(img-Q3g9he2D-1715585281624)]

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值