当前位置: 首页 > 编程日记 > 正文

12、OpenCV实现图像的空间滤波——图像平滑

1、空间滤波基础概念

1、空间滤波基础

空间滤波一词中滤波取自数字信号处理,指接受或拒绝一定的频率成分,但是空间滤波学习内容实际上和通过傅里叶变换实现的频域的滤波是等效的,故而也称为滤波。空间滤波主要直接基于领域(空间域)对图像中的像素执行计算,用滤波器(也成为空间掩膜、核、模板和窗口)直接作用于图像本身完成类似的平滑。

2、空间滤波机理

对空间域中的每一点(x,y),重复如下操作:

  • 对预先定义的以(x,y)为中心的领域内的像素进行预定义运算。
  • 将(1)中运算的结果作为(x,y)点新的响应。

上述过程就称为邻域处理或空间域滤波。

3、空间滤波分类

根据预定义的操作,可以将滤波器分为:

  • 线性滤波器
  • 非线性滤波器

而根据滤波器最终对图像造成的影响,可以将滤波器分为:

  • 平滑滤波器 ,通常用于模糊图像或者去除图像中的噪声
  • 锐化滤波器,突出图像中的边缘细节部分

4、空间相关和卷积

在执行线性空间vlbo时,有两个相近的概念需要理解,相关和卷积。相关是滤波器模板移过图像并计算每个位置乘积之和的处理。卷积机理类似,但是滤波器首先需要旋转180°。关于卷积的详细说明,可以参考文章

卷积在图像处理中的应用(转自https://medium.com/@irhumshafkat/intuitively-understanding-convolutions-for-deep-learning-1f6f42faee1)

一些算子及不同类型的卷积

这里不再做重复说明。

2、为图片添加噪声

使用摄像头拍摄图片,噪声是难以避免的,这里重点提下两种噪声:

  • 椒盐噪声:噪声的幅值基本上相同,但是噪声出现的位置是随机的;(中值滤波效果好)
  • 高斯噪声:每一点都存在噪声,但噪声的幅值是随机分布的。

但是,我们网上找到的图片大多是经过处理的,或者说噪声不是明显的,这里为了研究,为图片添加噪声。

椒盐噪声表现在图片上是图片中离散分布的黑点和白点

// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}

高斯噪声是一种加性噪声,为图像添加高斯噪声的代码如下:

// 添加Gussia噪声
// 使用指针访问
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}

示例:

为图像添加两种噪声的程序如下:

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
using namespace cv;
using namespace std;
// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}int main()
{Mat srcImage = imread("111.jpg");Mat src_salt, src_gaussian;src_salt = imread("111.jpg");src_gaussian = imread("111.jpg");if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);imshow("原图", srcImage);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);waitKey();return 0;
}

添加完噪声后的图像如下:

3、线性平滑空间滤波

平滑滤波器主要用于模糊处理和降低噪音。

1、平滑线性滤波器(又称均值滤波)

首先需要说明的是图像平滑是一种减少和抑制图像噪声的实用数字图像处理技术,一般来说,图像具有局部连续性质,即相邻像素的数值相近,而噪声的存在使得在噪声点处产生灰度跳跃,但一般可以合理的假设偶尔出现的噪声并没有改变图像局部连续的性质。其中,线性滤波是现实将空域模板对应邻域内像素的灰度值加权之和作为邻域内中心像素的相应输出。线性平滑模板的权系数全为正值而且系数之和等于1,如下图所示:

因此这种方法不会增加图像中总体的灰度程度。也就是说在灰度一致的区域,线性平滑滤波的相应输出不变。此外,可以知道,线性平滑滤波等效于低通滤波。但是同样的我们可以看出,线性平滑模板在模糊和降噪的同时,图像中的边缘和细节的锐度也都丢失了,也就是说,在平滑的过程中,使得图像的一部分细节信息丢失。

在OpenCV中,函数blur表示使用该模板的均值滤波器,其声明如下:

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

src是输入图像,dst为输出图像;ksize是滤波器模板窗口的大小;后两个参数分别表示,待处理像素在模板窗口的位置,默认值是窗口的中心位置,所以窗口的大小一般为奇数,最后一个参数表示对编解类型的处理,使用默认值即可。其调用示例blur(src,dst,Size(5,5),模板窗口的大小为5×55×5。

示例:

下面通过程序对两种含噪声的图片进行线性平滑滤波处理

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;
using namespace std;// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}int main()
{Mat srcImage = imread("111.jpg");Mat src_salt, src_gaussian;src_salt = imread("111.jpg");src_gaussian = imread("111.jpg");if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);//平滑滤波Mat aft_mean_filter_salt, aft_mean_filter_gaussian;blur(src_salt, aft_mean_filter_salt, Size(5, 5), Point(-1, -1));blur(src_gaussian, aft_mean_filter_gaussian, Size(5, 5), Point(-1, -1));//增强图像对比度//Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//filter2D(src_salt, src_salt, src_salt.depth(), kernel, Point(-1, -1));imshow("原图", srcImage);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);imshow("经过平滑滤波后含椒盐噪声的的结果", aft_mean_filter_salt);imshow("经过平滑滤波后含高斯噪声的的结果", aft_mean_filter_gaussian);waitKey();return 0;
}

2、高斯平滑

均值平滑对于领域内的像素一视同仁,为了减少平滑处理中的模糊,得到更自然的平滑效果,我们可以很自然的想到适当加大模板中心点的权重,随着距离中心点的距离增加,权重迅速减小,从而可以确保中心点看起来更接近于与它距离更近的点,基于这样的考虑得到的模板即为高斯模板。常用的3X3高斯模板如下所示:

高斯模板名字的由来是高斯函数,即二维正态分布密度函数,一个二维的高斯函数如下,

$$
  h(x, y)=e^{-\frac{x^{2}+y^{2}}{2 \sigma^{2}}}
  $$

公式中(x,y)是点坐标,在图像处理中可认为是整数,σ是标准差。

对于窗口模板的大小为 (2K+1)X(2K+1)的矩阵,其(i,j)位置的元素值可如下确定:

高斯模板的生成方法如下所示:

void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{static const double pi = 3.1415926;int center = ksize / 2; // 模板的中心位置,也就是坐标的原点double x2, y2;for (int i = 0; i < ksize; i++){x2 = pow(i - center, 2);for (int j = 0; j < ksize; j++){y2 = pow(j - center, 2);double g = exp(-(x2 + y2) / (2 * sigma * sigma));g /= 2 * pi * sigma;window[i][j] = g;}}double k = 1 / window[0][0]; // 将左上角的系数归一化为1for (int i = 0; i < ksize; i++){for (int j = 0; j < ksize; j++){window[i][j] *= k;}}
}

需要一个二维数组,存放生成的系数(这里假设模板的最大尺寸不会超过11);第二个参数是模板的大小(不要超过11);第三个参数就比较重要了,是高斯分布的标准差。

生成的过程,首先根据模板的大小,找到模板的中心位置ksize/2。 然后就是遍历,根据高斯分布的函数,计算模板中每个系数的值。

需要注意的是,最后归一化的过程,使用模板左上角的系数的倒数作为归一化的系数(左上角的系数值被归一化为1),模板中的每个系数都乘以该值(左上角系数的倒数),然后将得到的值取整,就得到了整数型的高斯滤波器模板。

至于小数形式的生成也比较简单,去掉归一化的过程,并且在求解过程后,模板的每个系数要除以所有系数的和。具体代码如下:

void generateGaussianTemplate(double window[][11], int ksize, double sigma)
{static const double pi = 3.1415926;int center = ksize / 2; // 模板的中心位置,也就是坐标的原点double x2, y2;double sum = 0;for (int i = 0; i < ksize; i++){x2 = pow(i - center, 2);for (int j = 0; j < ksize; j++){y2 = pow(j - center, 2);double g = exp(-(x2 + y2) / (2 * sigma * sigma));g /= 2 * pi * sigma;sum += g;window[i][j] = g;}}//double k = 1 / window[0][0]; // 将左上角的系数归一化为1for (int i = 0; i < ksize; i++){for (int j = 0; j < ksize; j++){window[i][j] /= sum;}}
}

σ选取技巧

当标准差σ选取不同的值是,二维高斯函数形状会发生很大的变化,

  • 如果σ过小,偏离中心的所有像素权杖重将会非常小,相当于加权和响应基本不考虑领域像素的作用,这样无法起到滤波平滑的效果。
  • 如果σ过大,而领域相对较小,这样在领域内高斯模板则退化为平均模板。

在matlab中,σ的默认值为0.5,在实际应用中,通常3X3的模板取σ为0.8左右,更大的模板可以适当增加σ的值。

在OpenCV中使用GuassianBlur()函数来实现高斯平滑,函数的声明如下:

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

这里由于高斯函数的可分离性,尺寸较大的高斯滤波器可以分成两步进行:首先将图像在水平(竖直)方向与一维高斯函数进行卷积;然后将卷积后的结果在竖直(水平)方向使用相同的一维高斯函数得到的模板进行卷积运算。

你也可以用如下源码进行实现:

void separateGaussianFilter(const Mat &src, Mat &dst, int ksize, double sigma)
{CV_Assert(src.channels()==1 || src.channels() == 3); // 只处理单通道或者三通道图像// 生成一维的高斯滤波模板double *matrix = new double[ksize];double sum = 0;int origin = ksize / 2;for (int i = 0; i < ksize; i++){// 高斯函数前的常数可以不用计算,会在归一化的过程中给消去double g = exp(-(i - origin) * (i - origin) / (2 * sigma * sigma));sum += g;matrix[i] = g;}// 归一化for (int i = 0; i < ksize; i++)matrix[i] /= sum;// 将模板应用到图像中int border = ksize / 2;copyMakeBorder(src, dst, border, border, border, border, BorderTypes::BORDER_REFLECT);int channels = dst.channels();int rows = dst.rows - border;int cols = dst.cols - border;// 水平方向for (int i = border; i < rows; i++){for (int j = border; j < cols; j++){double sum[3] = { 0 };for (int k = -border; k <= border; k++){if (channels == 1){sum[0] += matrix[border + k] * dst.at<uchar>(i, j + k); // 行不变,列变化;先做水平方向的卷积}else if (channels == 3){Vec3b rgb = dst.at<Vec3b>(i, j + k);sum[0] += matrix[border + k] * rgb[0];sum[1] += matrix[border + k] * rgb[1];sum[2] += matrix[border + k] * rgb[2];}}for (int k = 0; k < channels; k++){if (sum[k] < 0)sum[k] = 0;else if (sum[k] > 255)sum[k] = 255;}if (channels == 1)dst.at<uchar>(i, j) = static_cast<uchar>(sum[0]);else if (channels == 3){Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };dst.at<Vec3b>(i, j) = rgb;}}}// 竖直方向for (int i = border; i < rows; i++){for (int j = border; j < cols; j++){double sum[3] = { 0 };for (int k = -border; k <= border; k++){if (channels == 1){sum[0] += matrix[border + k] * dst.at<uchar>(i + k, j); // 列不变,行变化;竖直方向的卷积}else if (channels == 3){Vec3b rgb = dst.at<Vec3b>(i + k, j);sum[0] += matrix[border + k] * rgb[0];sum[1] += matrix[border + k] * rgb[1];sum[2] += matrix[border + k] * rgb[2];}}for (int k = 0; k < channels; k++){if (sum[k] < 0)sum[k] = 0;else if (sum[k] > 255)sum[k] = 255;}if (channels == 1)dst.at<uchar>(i, j) = static_cast<uchar>(sum[0]);else if (channels == 3){Vec3b rgb = { static_cast<uchar>(sum[0]), static_cast<uchar>(sum[1]), static_cast<uchar>(sum[2]) };dst.at<Vec3b>(i, j) = rgb;}}}delete[] matrix;
}

示例:

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;
using namespace std;// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}int main()
{Mat srcImage = imread("111.jpg");Mat src_salt, src_gaussian;src_salt = imread("111.jpg");src_gaussian = imread("111.jpg");if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);//平滑滤波Mat aft_mean_filter_salt, aft_mean_filter_gaussian;blur(src_salt, aft_mean_filter_salt, Size(5, 5), Point(-1, -1));blur(src_gaussian, aft_mean_filter_gaussian, Size(5, 5), Point(-1, -1));//增强图像对比度//Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//filter2D(src_salt, src_salt, src_salt.depth(), kernel, Point(-1, -1));imshow("原图", srcImage);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);imshow("经过平滑滤波后含椒盐噪声的的结果", aft_mean_filter_salt);imshow("经过平滑滤波后含高斯噪声的的结果", aft_mean_filter_gaussian);waitKey();return 0;
}

4、非线性平滑空间滤波

1、中值滤波

中值滤波本质上是一种统计排序滤波器,对于原图像中某点(i,j),中值滤波以该点为中心的领域内的所有像素的统计排序中值作为(i,j)的响应,即它将每一像素点的灰度值设置为该点某邻域窗口内的所有像素点灰度值的中值,也就是将中心像素的值用所有像素值的中间值(不是平均值)替换。中值滤波不同于均值,是指排序队列中位于中间位置的元素的值。

中值滤波对于某些类型的随机噪声具有非常理想的降噪能力,对于线性平滑滤波而言,在处理的像素领域之内包含噪声点时,噪声的存在总会或多或少的影响该点的像素值的计算(对于高斯平滑,影响程度同噪声点到中点的距离成正比),但在中值滤波中,噪声点则常常是直接忽略掉的;而且在同线性平滑滤波器相比,中值滤波在降噪的同时引起的模糊效应较低。

OpenCV提供了中值滤波的API函数如下:

void medianBlur(InputArray src,OutputArray dst,int ksize)

参数解释:原图像,目标图像,模板尺寸。

示例:

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;
using namespace std;// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}int main()
{Mat srcImage = imread("111.jpg");Mat src_salt, src_gaussian;src_salt = imread("111.jpg");src_gaussian = imread("111.jpg");if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);//平滑滤波Mat aft_median_filter_salt, aft_median_filter_gaussian;medianBlur(src_salt, aft_median_filter_salt, 3);medianBlur(src_gaussian, aft_median_filter_gaussian, 3);//增强图像对比度//Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//filter2D(src_salt, src_salt, src_salt.depth(), kernel, Point(-1, -1));imshow("原图", srcImage);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);imshow("经过平滑滤波后含椒盐噪声的的结果", aft_median_filter_salt);imshow("经过平滑滤波后含高斯噪声的的结果", aft_median_filter_gaussian);waitKey();return 0;
}

2、自适应中值滤波

中值滤波的效果依赖于滤波窗口的大小,太大会使图像边缘模糊,太小则去噪效果不好。因为噪声点和边缘点同样是灰度变化较为剧烈的像素,普通中值滤波在改变噪声点灰度值的时候,会一定程度上改变边缘像素的灰度值。但是噪声点几乎就是邻近像素的极值,而边缘往往不是,我们可以通过这个特性来限制中值滤波。

具体的改进方法如下:追行扫描图像, 当处理每一个像素时,判断该像素是否是滤波窗口所覆盖下领域像素的极大或者极小值,如果是,则采用正常的中值滤波处理该像素,如果不是,则不予处理。这种方法能够非常有效的去除突发噪声点,尤其是椒盐噪声,而且几乎不影响边缘。这种方法又称为自适应中值滤波。

算法实现如下所示:

uchar adaptiveProcess(const Mat &im, int row, int col, int kernelSize, int maxSize)
{vector<uchar> pixels;for (int a = -kernelSize / 2; a <= kernelSize / 2; a++)for (int b = -kernelSize / 2; b <= kernelSize / 2; b++){pixels.push_back(im.at<uchar>(row + a, col + b));}sort(pixels.begin(), pixels.end());auto min = pixels[0];auto max = pixels[kernelSize * kernelSize - 1];auto med = pixels[kernelSize * kernelSize / 2];auto zxy = im.at<uchar>(row, col);if (med > min && med < max){// to Bif (zxy > min && zxy < max)return zxy;elsereturn med;}else{kernelSize += 2;if (kernelSize <= maxSize)return adaptiveProcess(im, row, col, kernelSize, maxSize); // 增大窗口尺寸,继续A过程。elsereturn med;}
}

示例:

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;
using namespace std;
uchar adaptiveProcess(const Mat &im, int row, int col, int kernelSize, int maxSize);
// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}uchar adaptiveProcess(const Mat &im, int row, int col, int kernelSize, int maxSize)
{vector<uchar> pixels;for (int a = -kernelSize / 2; a <= kernelSize / 2; a++)for (int b = -kernelSize / 2; b <= kernelSize / 2; b++){pixels.push_back(im.at<uchar>(row + a, col + b));}sort(pixels.begin(), pixels.end());auto min = pixels[0];auto max = pixels[kernelSize * kernelSize - 1];auto med = pixels[kernelSize * kernelSize / 2];auto zxy = im.at<uchar>(row, col);if (med > min && med < max){// to Bif (zxy > min && zxy < max)return zxy;elsereturn med;}else{kernelSize += 2;if (kernelSize <= maxSize)return adaptiveProcess(im, row, col, kernelSize, maxSize); // 增大窗口尺寸,继续A过程。elsereturn med;}
}int main()
{Mat srcImage = imread("111.jpg",0);Mat src_salt, src_gaussian;src_salt = imread("111.jpg",0);src_gaussian = imread("111.jpg",0);if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);int minSize = 3; // 滤波器窗口的起始尺寸int maxSize = 7; // 滤波器窗口的最大尺寸//平滑滤波Mat aft_admedian_filter_salt, aft_admedian_filter_gaussian;// 扩展图像的边界copyMakeBorder(src_salt, aft_admedian_filter_salt, maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, BorderTypes::BORDER_REFLECT);for (int j = maxSize / 2; j < aft_admedian_filter_salt.rows - maxSize / 2; j++){for (int i = maxSize / 2; i < aft_admedian_filter_salt.cols * aft_admedian_filter_salt.channels() - maxSize / 2; i++){aft_admedian_filter_salt.at<uchar>(j, i) = adaptiveProcess(aft_admedian_filter_salt, j, i, minSize, maxSize);}}copyMakeBorder(src_salt, aft_admedian_filter_gaussian, maxSize / 2, maxSize / 2, maxSize / 2, maxSize / 2, BorderTypes::BORDER_REFLECT);for (int j = maxSize / 2; j < aft_admedian_filter_gaussian.rows - maxSize / 2; j++){for (int i = maxSize / 2; i < aft_admedian_filter_gaussian.cols * aft_admedian_filter_gaussian.channels() - maxSize / 2; i++){aft_admedian_filter_gaussian.at<uchar>(j, i) = adaptiveProcess(aft_admedian_filter_gaussian, j, i, minSize, maxSize);}}medianBlur(src_gaussian, aft_admedian_filter_gaussian, 3);//增强图像对比度//Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);//filter2D(src_salt, src_salt, src_salt.depth(), kernel, Point(-1, -1));imshow("原图", srcImage);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);imshow("经过平滑滤波后含椒盐噪声的的结果", aft_admedian_filter_salt);imshow("经过平滑滤波后含高斯噪声的的结果", aft_admedian_filter_gaussian);waitKey();return 0;
}

3、双边滤波

双边滤波(Bilateral filter)是一种非线性的滤波方法,是结合图像的空间邻近度和像素值相似度的一种折衷处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的。双边滤波能够提供一种不会将边缘平滑掉的方法,但作为代价,需要更多的处理时间。与高斯滤波类似,双边滤波会依据每个像素及其领域构造一个加权平均值,加权计算包括两个部分,其中第一部分加权方式与高斯平滑中相同,第二部分也属于高斯加权,但不是基于中心像素点与其他像素点的空间距离之上的加权,而是基于其他像素与中心像素的亮度差值的加权。可以将双边滤波视为高斯平滑,对相似的像素赋予较高的权重,不相似的像素赋予较小的权重,也可用于图像分割之中。

双边滤波器之所以能够做到在平滑去噪的同时还能够很好的保存边缘(Edge Preserve),是由于其滤波器的核由两个函数生成:

  • 一个函数由像素欧式距离决定滤波器模板的系数
  • 另一个函数由像素的灰度差值决定滤波器的系数

其综合了高斯滤波器(Gaussian Filter)和α-截尾均值滤波器(Alpha-Trimmed mean Filter)的特点。高斯滤波器只考虑像素间的欧式距离,其使用的模板系数随着和窗口中心的距离增大而减小;α-截尾均值滤波器则只考虑了像素灰度值之间的差值,去掉α%小值和最大值后再计算均值。

双边滤波器使用二维高斯函数生成距离模板,使用一维高斯函数生成值域模板。
距离模板系数的生成公式如下:

$$
d(i, j, k, l)=\exp \left(-\frac{(i-k)^{2}+(j-l)^{2}}{2 \sigma_{d}^{2}}\right)
$$

其中,(k,l)为模板窗口的中心坐标;(i,j)为模板窗口的其他系数的坐标;σ为高斯函数的标准差。 使用该公式生成的滤波器模板和高斯滤波器使用的模板是没有区别的。

值域模板系数的生成公式如下:

$$
r(i, j, k, l)=\exp \left(-\frac{\|f(i, j)-f(k, l)\|^{2}}{2 \sigma_{r}^{2}}\right)
$$

其中,函数f(x,y)表示要处理的图像,f(x,y)表示图像在点(x,y)处的像素值;(k,l)为模板窗口的中心坐标;(i,j)为模板窗口的其他系数的坐标;σr为高斯函数的标准差。

将上述两个模板相乘就得到了双边滤波器的模板

$$
w(i, j, k, l)=d(i, j, k, l) * r(i, j, k, l)=\exp \left(-\frac{(i-k)^{2}+(j-l)^{2}}{2 \sigma_{d}^{2}}-\frac{\|f(i, j)-f(k, l)\|^{2}}{2 \sigma_{r}^{2}}\right)
$$

其中定义域滤波和值域滤波可以用下图形象表示:

代码实现如下:

void myBilateralFilter(const Mat &src, Mat &dst, int ksize, double space_sigma, double color_sigma)
{int channels = src.channels();CV_Assert(channels == 1 || channels == 3);double space_coeff = -0.5 / (space_sigma * space_sigma);double color_coeff = -0.5 / (color_sigma * color_sigma);int radius = ksize / 2;Mat temp;copyMakeBorder(src, temp, radius, radius, radius, radius, BorderTypes::BORDER_REFLECT);vector<double> _color_weight(channels * 256); // 存放差值的平方vector<double> _space_weight(ksize * ksize); // 空间模板系数vector<int> _space_ofs(ksize * ksize); // 模板窗口的坐标double *color_weight = &_color_weight[0];double *space_weight = &_space_weight[0];int    *space_ofs = &_space_ofs[0];for (int i = 0; i < channels * 256; i++)color_weight[i] = exp(i * i * color_coeff);// 生成空间模板int maxk = 0;for (int i = -radius; i <= radius; i++){for (int j = -radius; j <= radius; j++){double r = sqrt(i*i + j * j);if (r > radius)continue;space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系数space_ofs[maxk++] = i * temp.step + j * channels; // 存放模板的位置,和模板系数相对应
        }}// 滤波过程for (int i = 0; i < src.rows; i++){const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;uchar *dptr = dst.data + i * dst.step;if (channels == 1){for (int j = 0; j < src.cols; j++){double sum = 0, wsum = 0;int val0 = sptr[j]; // 模板中心位置的像素for (int k = 0; k < maxk; k++){int val = sptr[j + space_ofs[k]];double w = space_weight[k] * color_weight[abs(val - val0)]; // 模板系数 = 空间系数 * 灰度值系数sum += val * w;wsum += w;}dptr[j] = (uchar)cvRound(sum / wsum);}}else if (channels == 3){for (int j = 0; j < src.cols * 3; j+=3){double sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;int b0 = sptr[j];int g0 = sptr[j + 1];int r0 = sptr[j + 2];for (int k = 0; k < maxk; k++){const uchar *sptr_k = sptr + j + space_ofs[k];int b = sptr_k[0];int g = sptr_k[1];int r = sptr_k[2];double w = space_weight[k] * color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];sum_b += b * w;sum_g += g * w;sum_r += r * w;wsum += w;}wsum = 1.0f / wsum;b0 = cvRound(sum_b * wsum);g0 = cvRound(sum_g * wsum);r0 = cvRound(sum_r * wsum);dptr[j] = (uchar)b0;dptr[j + 1] = (uchar)g0;dptr[j + 2] = (uchar)r0;}}}
}

OpenCV提供了对应双边滤波的API接口

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

第一个参数,InputArray类型的src,输入图像,即源图像,需要为8为或者浮点型单通道、三通道的图像;
第二个参数,OutputArray类型的dst,即目标图像,需要和源图像有一样的尺寸和类型;
第三个参数,int类型的d,表示在过滤过程中每个像素邻域的直径。如果这个参数被设置为负值,那么OpenCV会从第五个参数sigmaSpace来计算出它;
第四个参数,double类型的sigmaColor,颜色空间滤波器的sigma值,这个参数的值越大,就表明该像素邻域内有越宽广的颜色会被混合到一起,产生较大的半相等颜色区域;
第五个参数,double类型的 sigmaSpace,坐标空间中的sigma值,坐标空间的标注方差。它的数值越大,意味着越远的像素会相互影响,从而使更大区域中足够相似的颜色获取相同的颜色,当d>0时,d制定了邻域大小且与sigmaSpace无关否则,d正比于sigmaSpace;
第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT。

示例:

//实现直方图的反向投影
#include "stdafx.h"
#include <math.h>
#include <random>
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>using namespace cv;
using namespace std;
// 添加椒盐噪声
void addSaltNoise(Mat &m, int num)
{// 随机数产生器std::random_device rd; //种子std::mt19937 gen(rd()); // 随机数引擎auto cols = m.cols * m.channels();for (int i = 0; i < num; i++){auto row = static_cast<int>(gen() % m.rows);auto col = static_cast<int>(gen() % cols);auto p = m.ptr<uchar>(row);p[col++] = 255;p[col++] = 255;p[col] = 255;}
}
void addGaussianNoise(Mat &m, int mu, int sigma)
{// 产生高斯分布随机数发生器std::random_device rd;std::mt19937 gen(rd());std::normal_distribution<> d(mu, sigma);auto rows = m.rows; // 行数auto cols = m.cols * m.channels(); // 列数for (int i = 0; i < rows; i++){auto p = m.ptr<uchar>(i); // 取得行首指针for (int j = 0; j < cols; j++){auto tmp = p[j] + d(gen);tmp = tmp > 255 ? 255 : tmp;tmp = tmp < 0 ? 0 : tmp;p[j] = tmp;}}
}
void myBilateralFilter(const Mat &src, Mat &dst, int ksize, double space_sigma, double color_sigma)
{int channels = src.channels();CV_Assert(channels == 1 || channels == 3);double space_coeff = -0.5 / (space_sigma * space_sigma);double color_coeff = -0.5 / (color_sigma * color_sigma);int radius = ksize / 2;Mat temp;copyMakeBorder(src, temp, radius, radius, radius, radius, BorderTypes::BORDER_REFLECT);vector<double> _color_weight(channels * 256); // 存放差值的平方vector<double> _space_weight(ksize * ksize); // 空间模板系数vector<int> _space_ofs(ksize * ksize); // 模板窗口的坐标double *color_weight = &_color_weight[0];double *space_weight = &_space_weight[0];int    *space_ofs = &_space_ofs[0];for (int i = 0; i < channels * 256; i++)color_weight[i] = exp(i * i * color_coeff);// 生成空间模板int maxk = 0;for (int i = -radius; i <= radius; i++){for (int j = -radius; j <= radius; j++){double r = sqrt(i*i + j * j);if (r > radius)continue;space_weight[maxk] = exp(r * r * space_coeff); // 存放模板系数space_ofs[maxk++] = i * temp.step + j * channels; // 存放模板的位置,和模板系数相对应}}// 滤波过程for (int i = 0; i < src.rows; i++){const uchar *sptr = temp.data + (i + radius) * temp.step + radius * channels;uchar *dptr = dst.data + i * dst.step;if (channels == 1){for (int j = 0; j < src.cols; j++){double sum = 0, wsum = 0;int val0 = sptr[j]; // 模板中心位置的像素for (int k = 0; k < maxk; k++){int val = sptr[j + space_ofs[k]];double w = space_weight[k] * color_weight[abs(val - val0)]; // 模板系数 = 空间系数 * 灰度值系数sum += val * w;wsum += w;}dptr[j] = (uchar)cvRound(sum / wsum);}}else if (channels == 3){for (int j = 0; j < src.cols * 3; j += 3){double sum_b = 0, sum_g = 0, sum_r = 0, wsum = 0;int b0 = sptr[j];int g0 = sptr[j + 1];int r0 = sptr[j + 2];for (int k = 0; k < maxk; k++){const uchar *sptr_k = sptr + j + space_ofs[k];int b = sptr_k[0];int g = sptr_k[1];int r = sptr_k[2];double w = space_weight[k] * color_weight[abs(b - b0) + abs(g - g0) + abs(r - r0)];sum_b += b * w;sum_g += g * w;sum_r += r * w;wsum += w;}wsum = 1.0f / wsum;b0 = cvRound(sum_b * wsum);g0 = cvRound(sum_g * wsum);r0 = cvRound(sum_r * wsum);dptr[j] = (uchar)b0;dptr[j + 1] = (uchar)g0;dptr[j + 2] = (uchar)r0;}}}
}
int main()
{Mat srcImage = imread("111.jpg");Mat src_salt, src_gaussian;src_salt = imread("111.jpg");src_gaussian = imread("111.jpg");if (!srcImage.data){cout << "图像打开失败!" << endl;return -1;}addSaltNoise(src_salt, 100);addGaussianNoise(src_gaussian,100,1);//平滑滤波Mat aft_bilateral_filter_salt, aft_bilateral_filter_gaussian, srcImage1;bilateralFilter(srcImage, srcImage1, 5, 100, 3);bilateralFilter(src_salt, aft_bilateral_filter_salt, 5, 100, 3);bilateralFilter(src_gaussian, aft_bilateral_filter_gaussian, 5, 100, 3);//增强图像对比度Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);filter2D(srcImage1, srcImage1, srcImage1.depth(), kernel, Point(-1, -1));imshow("原图", srcImage);imshow("双边滤波处理过后的原图", srcImage1);imshow("添加椒盐噪声后", src_salt);imshow("添加高斯噪声后", src_gaussian);imshow("经过平滑滤波后含椒盐噪声的的结果", aft_bilateral_filter_salt);imshow("经过平滑滤波后含高斯噪声的的结果", aft_bilateral_filter_gaussian);waitKey();return 0;
}

程序运行结果如下,双边滤波很好的对原图像进行了优化,但是对应椒盐噪声之类的高频噪声没有滤波效果。

参考资料:

图像处理基础(1):噪声的添加和过滤

图像处理基础(2):自适应中值滤波器(基于OpenCV实现)

图像处理基础(3):均值滤波器及其变种

图像处理基础(4):高斯滤波器详解

图像处理基础(5):双边滤波器

图像处理基础(6):锐化空间滤波器

OpenCV--Python 图像平滑之高斯平滑、均值平滑

【OpenCV图像处理】十五、图像空域滤波(上)

【OpenCV图像处理】十六、图像空域滤波(下)

【OpenCV】邻域滤波:方框、高斯、中值、双边滤波

【OpenCV学习笔记】之图像平滑(线性/非线性滤波器)

[Python图像处理] 四.图像平滑之均值滤波、方框滤波、高斯滤波及中值滤波

C++图像处理 -- 表面模糊

选择性模糊及其算法的实现。

 

 

 

转载于:https://www.cnblogs.com/noticeable/p/10875905.html

相关文章:

计算 LBP 特征

#include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/features2d/features2d.hpp> // 计算原始LBP特征 cv::Mat OLBP(cv::Mat& srcImage) {const int nRows …

三维重建【四】-------------------结构光 三维重建----论文调研

1. 动态目标实时三维重建-结构光方案 动态目标 三维重建 Stripe boundary codes for real-time structured-light range scanning of moving objects 我们提出了一种新的实时结构光扫描方法。在分析现有结构光技术的基本假设之后&#xff0c;我们基于编码投影条纹之间的边界&…

APP开发定制

app是什么意思? APP&#xff0c;application的简称&#xff0c;是智能手机的第三方应用程序&#xff0c;常见的有微信、手机qq、今日头条、手机支付宝、腾讯视频、微店等&#xff0c;随着智能手机和ipad等移动终端设备的普及&#xff0c;人们逐渐习惯了使用APP客户端上网的方式…

Haar 特征提取

double HaarExtract(double const ** image ,int type_, cv::Rect roi) {double value;double wh1, wh2;double bk1, bk2;int x roi.x;int y roi.y;int width roi.width;int height roi.height;switch (type_){// Haar水平边缘case 0: // HaarHEdgewh1 calcIntegral(image…

awk的基本⽤法

awk的基本⽤法 awk是报告⽣成器&#xff0c;格式化⽂本输出&#xff0c;有多种版本。centos中的是gawk即GNU awk版本。 awk⼯作原理&#xff1a;第⼀步&#xff1a;执⾏BEGIN{action;...}语句块中的语句。第⼆步&#xff1a;从⽂件或标准输⼊&#xff08;stdin&#xff09;读取…

视音频数据处理入门:RGB、YUV像素数据处理【转】

转自&#xff1a;http://blog.csdn.net/leixiaohua1020/article/details/50534150 视音频数据处理入门系列文章&#xff1a; 视音频数据处理入门&#xff1a;RGB、YUV像素数据处理 视音频数据处理入门&#xff1a;PCM音频采样数据处理 视音频数据处理入门&#xff1a;H.264视频…

SVO(SVO: fast semi-direct monocular visual odometry)

SVO&#xff08;SVO: fast semi-direct monocular visual odometry&#xff09;翻译 文章目录SVO&#xff08;SVO: fast semi-direct monocular visual odometry&#xff09;翻译1、介绍2、系统概述3、符号4、运动估计4.1、 基于稀疏模型的图像对齐4.2、 通过特征对齐松弛4.3、…

MSER 候选车牌区域检测

#include "opencv2/highgui/highgui.hpp" #include "opencv2/features2d/features2d.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <iostream> // Mser车牌目标检测 std::vector<cv::Rect> mserGetPlate(cv::Mat srcImage…

从HelloWorld看Knative Serving代码实现

为什么80%的码农都做不了架构师&#xff1f;>>> 摘要&#xff1a; Knative Serving以Kubernetes和Istio为基础&#xff0c;支持无服务器应用程序和函数的部署并提供服务。我们从部署一个HelloWorld示例入手来分析Knative Serving的代码细节。 概念先知 官方给出的这…

svo_note

SVO论文笔记1.frame overviews2. Motion Estimate Thread2.1 Sparse Model-based Image Alignment 基于稀疏点亮度的位姿预估2.2 Relaxation Through Feature Alignment 基于图块的特征点匹配2.3 Pose and Structure Refinement3 Mapping Thread3.1 depth-filter3.2 初始化参考…

Druid 配置 wallfilter

这个文档提供基于Spring的各种配置方式 使用缺省配置的WallFilter <bean id"dataSource" class"com.alibaba.druid.pool.DruidDataSource" init-method"init" destroy-method"close">...<property name"filters" v…

vue下的bootstrap table + jquery treegrid, treegrid无法渲染的问题

在mian.js导入的包如下&#xff1a;该bootstrap-table-treegrid.js需要去下载&#xff0c;在复制到jquery-treegrid/js/ 1 import $ from jquery 2 import bootstrap/dist/css/bootstrap.min.css 3 import bootstrap/dist/js/bootstrap.min 4 import bootstrap-table/dist/boot…

内存和缓存的区别

今天看书的时候又看到了内存和缓存&#xff0c;之所以说又&#xff0c;是因为之前遇到过查过资料&#xff0c;但是现在又忘了(图侵删)。 所以又复习一遍&#xff0c;记录一下&#xff0c;有所纰漏的地方&#xff0c;欢迎指正。 同志们&#xff0c;上图并不是内存和缓存中的任何…

【Boost】noncopyable:不可拷贝

【CSDN】&#xff1a;boost::noncopyable解析 【Effective C】&#xff1a;条款06_若不想使用编译器自动生成地函数&#xff0c;就该明确拒绝 1.example boost::noncopyable 为什么要boost::noncopyable 在c中定义一个类的时候&#xff0c;如果不明确定义拷贝构造函数和拷贝赋…

BigData NoSQL —— ApsaraDB HBase数据存储与分析平台概览

一、引言时间到了2019年&#xff0c;数据库也发展到了一个新的拐点&#xff0c;有三个明显的趋势&#xff1a; 越来越多的数据库会做云原生(CloudNative)&#xff0c;会不断利用新的硬件及云本身的优势打造CloudNative数据库&#xff0c;国内以阿里云的Cloud HBase、POLARDB为代…

ubuntu clion 创建桌面快捷方式

ubuntu clion 创建桌面快捷方式 首先在终端下输入 cd /usr/share/applications/进入applications目录下&#xff0c;建立一个clion.desktop文件 sudo touch clion.desktop然后在vim命令下编辑该文件 sudo vim clion.desktop进入vim后&#xff0c;按i插入开始编辑该文件&…

Flex 布局:语法篇

2019独角兽企业重金招聘Python工程师标准>>> 布局的传统解决方案&#xff0c;基于盒状模型&#xff0c;依赖 display 属性 position 属性 float 属性。它对于那些特殊布局非常不方便&#xff0c;比如&#xff0c;垂直居中就不容易实现。 2009年&#xff0c;W3C 提…

特征运动点估计

cv::Mat getRansacMat(const std::vector<cv::DMatch>& matches, const std::vector<cv::KeyPoint>& keypoints1, const std::vector<cv::KeyPoint>& keypoints2, std::vector<cv::DMatch>& outMatches) {// 转换特征点格式std::vecto…

Vue+Element-ui+二级联动封装组件

通过父子组件传值 父组件&#xff1a; 1 <template>2 <linkage :citysList"citysList" :holder"holder" saveId"saveId"></linkage>3 </template>4 <script>5 import linkage from ./common/linkage6 export de…

MOG2 成员函数参数设定

pMOG2->setDetectShadows(true); // 背景模型影响帧数 默认为500 pMOG2->setHistory(1000); // 模型匹配阈值 pMOG2->setVarThreshold(50); // 阴影阈值 pMOG2->setShadowThreshold(0.7);前景中模型参数&#xff0c;设置为0表示背景&#xff0c;255为前景&#xff…

webpack 大法好 ---- 基础概念与配置(1)

再一次见面&#xff01; Light 还是太太太懒了&#xff0c;距离上一篇没啥营养的文章已经过去好多天。今天为大家介绍介绍 webpack 最基本的概念&#xff0c;以及简单的配置&#xff0c;让你能快速得搭建一个可用的 webpack 开发环境。 webpack的安装 webpack 运行于 node 环境…

Zookeeper迁移(扩容/缩容)

zookeeper选举原理在迁移前有必要了解zookeeper的选举原理&#xff0c;以便更科学的迁移。快速选举FastLeaderElectionzookeeper默认使用快速选举&#xff0c;在此重点了解快速选举&#xff1a;向集群中的其他zookeeper建立连接&#xff0c;并且只有myid比对方大的连接才会被接…

SVO Without ROS环境搭建

Installation: Plain CMake (No ROS) 首先&#xff0c;建立工作目录&#xff1a;workspace&#xff0c;然后把下面的需要的都在该目录下进行. mkdir workspace cd workspace Boost - c Librairies (thread and system are needed) sudo apt-get install libboost-all-dev Eige…

BackgroundSubtractorGMG 背景建模

#include <opencv2/bgsegm.hpp> #include <opencv2/video.hpp> #include <opencv2/opencv.hpp> #include <iostream> #include <sstream> using namespace cv; using namespace std; using namespace bgsegm; // GMG目标建模检测 void detectBac…

启动webpack-dev-server只能本机访问的解决办法

修改package.json的dev启动设置&#xff0c;增加--host 0.0.0.0启动后localhost更换为本机IP即可访问

TCP/IP:IP选项处理

引言 IP输入函数要对IP 进行选项处理&#xff0c;。RFC791和1122规定了IP选项和处理规则。一个IP首部可以跟40个字节的选项。 选项格式 选项的格式&#xff0c;分为两种类型&#xff0c;单字节和多字节。 ip_dooptions函数 这个函数用于判断分组转发。用常量位移访问IP选项字段…

【SVO2.0 安装编译】Ubuntu 20.04 + Noetic

ways one 链接: https://pan.baidu.com/s/1ZAkeD64wjFsDHfpCm1CB1w 提取码: kxx2 (downloads and use idirectly) ways two: [SVO2-OPEN: https://github.com/uzh-rpg/rpg_svo_pro_open](https://github.com/DEARsunshine/rpg_svo_pro_open)git挂梯子 如果各位终端无法挂梯…

人眼目标检测初始化

// 初始化摄像头读取视频流cv::VideoCapture cap(0);// 宽高设置为320*256cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);cap.set(CV_CAP_PROP_FRAME_HEIGHT, 256);// 读取级联分类器// 文件存放在opencv\sources\data\haarcascades bool flagGlasses false;if(flagGlasses){face_ca…

Qt之界面换肤

简述 常用的软件基本都有换肤功能&#xff0c;例如&#xff1a;QQ、360、迅雷等。换肤其实很简单&#xff0c;并没有想象中那么难&#xff0c;利用前面分享过的QSS系列文章&#xff0c;沃我们完全可以实现各种样式的定制&#xff01; 简述实现原理效果新建QSS文件编写QSS代码加…

mongDB的常用操作总结

目录 常用查询:查询一条数据查询子元素集合:image.idgte: 大于等于,lte小于等于...查询字段不存在的数据not查询数量:常用更新更新第一条数据的一个字段:更新一条数据的多个字段:常用删除删除:常用查询: 查询一条数据 精确匹配is Query(Criteria.where("id").is(id))…