过滤图像和视频
图像滤波是一种邻域运算,其中输出图像中任何给定像素的值是通过对相应输入像素附近的像素值应用某种算法来确定的。该技术通常用于平滑、锐化和检测图像和视频的边缘。
让我们了解在讨论图像过滤技术、内核和卷积时使用的一些术语的含义。
内核
内核是一个具有奇数高度和奇数宽度的矩阵。它也被称为卷积矩阵、掩码或滤波器。内核根据其值分布命名。内核用于在图像过滤操作中定义像素的邻域。一些流行的内核是规范化盒过滤器,高斯内核,拉普拉斯内核,边缘检测内核等。内核可以定义不同的大小。但是大内核会导致较长的处理时间。
这是一个 3 x 3 规范化框筛选器。此内核用于均匀平滑(模糊)。
这是一个 5 x 5 规范化框筛选器。该内核也用于均匀平滑。同样,您可以创建具有任何大小的归一化框过滤器,以用于均匀平滑。
这是一个 3 x 3 高斯核,用于高斯平滑(模糊)。
这是一个 5 x 5 高斯核,用于高斯平滑(模糊)。同样,您可以创建任何大小的高斯核。核的值分布应使用 2-D 高斯函数计算。
x, y 是原点位于中心的核的坐标。(即内核中心元素处的 x = 0 和 y = 0。**σ**是高斯分布的标准差。对于较大的标准差,需要更大的核才能准确执行高斯平滑。以下 5 x 5 高斯核的标准差为 1。
卷积
卷积是对图像执行的数学运算,方法是在整个图像上滑动内核,并根据内核的值和原始图像的重叠像素值计算每个像素的新值。
C22 = (K11 x A11 + K12 x A12 + K13 x A13) + (K21 x A21 + K22 x A22 + K23 x A23) + (K31 x A31 + K32 x A32 + K33 x A33) C23 = (K11 x A12 + K12 x A13 + K13 x A14) + (K21 x A22 + K22 x A23 + K23 x A24) + (K31 x A32 + K32 x A33 + K33 x A34) C24 = (K11 x A13 + K12 x A14 + K13 x A15) + (K21 x A23 + K22 x A24 + K23 x A25) + (K31 x A33 + K32 x A34 + K33 x A35) C32 = (K11 x A21 + K12 x A22 + K13 x A23) + (K21 x A31 + K22 x A32 + K23 x A33) + (K31 x A41 + K32 x A42 + K33 x A43) C33 = (K11 x A22 + K12 x A23 + K13 x A24) + (K21 x A32 + K22 x A33 + K23 x A34) + (K31 x A42 + K32 x A43 + K33 x A44)
以同样的方式C34,可以在卷积图像中计算。但是卷积图像边界中的其他值(例如 C11、C12 等)不能以相同的方式计算,因为内核在边界处与原始图像部分重叠。因此,应计算非重叠的不存在的像素值,以确定卷积图像边界处的像素值。
均匀模糊
均匀模糊是平滑图像的最简单方法。它也被称为均匀平滑、均匀过滤和框模糊。在此技术中,每个像素值计算为内核定义的像素邻域的平均值。
均匀模糊中使用的内核称为归一化框滤波器。您可以根据需要为此内核定义任何大小。但最好定义宽度和高度为奇数大小的方形核。在下图中,我显示了 3 x 3 和 5 x 5 规范化框筛选器。
3x3 规范化框筛选器 |
![]() |
---|
5 x 5 标准化框筛选器 |
您必须选择正确大小的内核来定义每个像素的邻域。如果太大,图像的小特征可能会消失,图像看起来会模糊。如果它太小,则无法消除图像中的噪点。
使用OpenCV的图像上的均匀模糊
#include <QCoreApplication> #include "opencv2/opencv.hpp" #include "opencv/highgui.h" using namespace std; using namespace cv; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 读取图像 Mat image = imread("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/lean_guitar.jpg"); // 创建3X3和5X5的均匀滤波核并对读取图像进行均匀滤波处理 Mat blur_3X3_img; Mat blur_5X5_img; blur(image, blur_3X3_img, Size(3, 3)); blur(image, blur_5X5_img, Size(5, 5)); string orignWd = "原始图像"; string blurWd_3X3 = "3X3处理的图像"; string blurWd_5X5 = "5X5处理的图像"; namedWindow(orignWd); namedWindow(blurWd_3X3); namedWindow(blurWd_5X5); // 显示出来图像到窗口 imshow(orignWd, image); imshow(blurWd_3X3, blur_3X3_img); imshow(blurWd_5X5, blur_5X5_img); waitKey(0); destroyAllWindows(); return a.exec(); }
![]() |
---|
原始图像 |
![]() |
---|
使用 3 x 3 归一化框滤镜模糊图像![]() |
使用 5 x 5 归一化框滤镜模糊图像 |
代码解释
//Blur the image with 3x3 kernel Mat image_blurred_with_3x3_kernel; blur(image, image_blurred_with_3x3_kernel, Size(3, 3));
上述函数在原始图像上使用 3 x 3 归一化框滤镜执行均匀平滑/模糊操作,并将平滑的图像存储在 image_blurred_with_3x3_kernel Mat 对象中。原始图像中的每个通道都是独立处理的。
//Blur the image with 5x5 kernel Mat image_blurred_with_5x5_kernel; blur(image, image_blurred_with_5x5_kernel, Size(5, 5));
上述函数使用5 x 5归一化框滤镜对原始图像执行均匀平滑/模糊操作,并将平滑的图像存储在*image_blurred_with_5x5_kernel* Mat对象中。原始图像中的每个通道都是独立处理的。
//Define names of the window String window_name = "The Guitar"; String window_name_blurred_with_3x3_kernel = "The Guitar Blurred with 3 x 3 Kernel"; String window_name_blurred_with_5x5_kernel = "The Guitar Blurred with 5 x 5 Kernel"; // Create a window with above names namedWindow(window_name); namedWindow(window_name_blurred_with_3x3_kernel); namedWindow(window_name_blurred_with_5x5_kernel); // Show our images inside the created windows. imshow(window_name, image); imshow(window_name_blurred_with_3x3_kernel, image_blurred_with_3x3_kernel); imshow(window_name_blurred_with_5x5_kernel, image_blurred_with_5x5_kernel);
上面的代码段创建窗口并在其中显示图像。
视频上的均匀模糊
#include <opencv2/opencv.hpp> #include <iostream> #include <QDebug> using namespace cv; using namespace std; int main(int argc, char* argv[]) { //open the video file for reading VideoCapture cap("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/点云应用.mp4"); // if not success, exit program if (cap.isOpened() == false) { qDebug() << "打开不读取的视频文件" << endl; cin.get(); //wait for any key press return -1; } //Define names of the window String window_name_of_original_video = "Original Video"; String window_name_of_video_blurred_with_5x5_kernel = "Video Blurred with 5 x 5 Kernel"; // Create a window with above names namedWindow(window_name_of_original_video, WINDOW_NORMAL); namedWindow(window_name_of_video_blurred_with_5x5_kernel, WINDOW_NORMAL); while (true) { Mat frame; bool bSuccess = cap.read(frame); // read a new frame from video if (bSuccess == false) { qDebug() << "Found the end of the video" << endl; break; } //Blur the frame with 5x5 kernel Mat frame_blurred_with_5x5_kernel; blur(frame, frame_blurred_with_5x5_kernel, Size(5, 5)); //show the frames in the created windows imshow(window_name_of_original_video, frame); imshow(window_name_of_video_blurred_with_5x5_kernel, frame_blurred_with_5x5_kernel); //wait for for 10 ms until any key is pressed. //If the 'Esc' key is pressed, break the while loop. //If the any other key is pressed, continue the loop //If any key is not pressed withing 10 ms, continue the loop if (waitKey(10) == 27) { qDebug() << "按下ESC键,停止视频处理" << endl; break; } } return 0; }
高斯模糊
所谓模糊,可以理解成每一个像素都取周边像素的平均值。
高斯模糊/平滑是最常用的平滑技术,用于消除图像和视频中的噪点。在这种技术中,图像应该与高斯核卷积以产生平滑的图像。
您可以根据需要定义内核的大小。但是,在X和Y方向上高斯分布的标准差应仔细选择,考虑核的大小,以使核的边缘接近于零。在这里,我展示了 3 x 3 和 5 x 5 高斯核。
![]() |
---|
3 x 3 高斯核 |
![]() |
---|
5 x 5 高斯核 |
您必须选择正确大小的内核来定义每个像素的邻域。如果太大,图像的小特征可能会消失,图像看起来会模糊。如果它太小,则无法消除图像中的噪点。
3X3高斯模型过程
定义高斯函数: G(x, y) = (1 / (2 * π * σ^2)) * e^(-(x^2 + y^2) / (2 * σ^2)) 其中,x和y是偏移量,σ是高斯分布的标准差。
计算元素的值: 对于一个3x3的高斯核,中心元素位于(0, 0),即(行偏移量, 列偏移量) = (0, 0)。 将(x, y)代入高斯函数的公式中,可得到每个元素的值。
G(-1, -1) = (1 / (2 * π * σ^2)) * e^(-((-1)^2 + (-1)^2) / (2 * σ^2))
G(-1, 0) = (1 / (2 * π * σ^2)) * e^(-((-1)^2 + 0^2) / (2 * σ^2))
G(-1, 1) = (1 / (2 * π * σ^2)) * e^(-((-1)^2 + 1^2) / (2 * σ^2))
G(0, -1) = (1 / (2 * π * σ^2)) * e^(-(0^2 + (-1)^2) / (2 * σ^2))
G(0, 0) = (1 / (2 * π * σ^2)) * e^(-(0^2 + 0^2) / (2 * σ^2))
G(0, 1) = (1 / (2 * π * σ^2)) * e^(-(0^2 + 1^2) / (2 * σ^2))
G(1, -1) = (1 / (2 * π * σ^2)) * e^(-(1^2 + (-1)^2) / (2 * σ^2))
G(1, 0) = (1 / (2 * π * σ^2)) * e^(-(1^2 + 0^2) / (2 * σ^2))
G(1, 1) = (1 / (2 * π * σ^2)) * e^(-(1^2 + 1^2) / (2 * σ^2))
- 归一化: 将所有元素值相加并除以总和:G(-1, -1) + G(-1, 0) + G(-1, 1) + G(0, -1) + G(0, 0) + G(0, 1) + G(1, -1) + G(1, 0) + G(1, 1)
请注意,归一化是将所有元素的和调整为1,而不是单个元素的值。对于标准的高斯核,σ的值通常取1。代入σ=1进行计算,我们可以得到一个准确的3x3高斯核:
1/16 2/16 1/16
2/16 4/16 2/16
1/16 2/16 1/16
这是一个经过归一化处理的3x3高斯核,它满足所有元素之和为1。
对图像进行高斯模糊
#include <QCoreApplication> #include "opencv2/opencv.hpp" using namespace std; using namespace cv; int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 读取图像 Mat image = imread("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/lean_guitar.jpg"); // 创建3X3和5X5的高斯滤波核并对读取图像进行高斯滤波处理 Mat blur_3X3_img; Mat blur_5X5_img; GaussianBlur(image, blur_3X3_img, Size(3, 3), 0); GaussianBlur(image, blur_5X5_img, Size(9, 9), 0); string orignWd = "原始图像"; string blurWd_3X3 = "3X3处理的图像"; string blurWd_5X5 = "5X5处理的图像"; namedWindow(orignWd); namedWindow(blurWd_3X3); namedWindow(blurWd_5X5); // 显示出来图像到窗口 imshow(orignWd, image); imshow(blurWd_3X3, blur_3X3_img); imshow(blurWd_5X5, blur_5X5_img); waitKey(0); destroyAllWindows(); return a.exec(); }
![]() |
---|
原始图像 |
![]() |
---|
使用 3 x 3 高斯核模糊图像![]() |
使用 5 x 5 高斯核模糊图像 |
代码解释
/Blur the image with 3x3 Gaussian kernel Mat image_blurred_with_3x3_kernel; GaussianBlur(image, image_blurred_with_3x3_kernel, Size(3, 3), 0);
上述函数在原始图像上使用 3 x 3 高斯滤波器执行高斯模糊/平滑操作,并将平滑的图像存储在 image_blurred_with_3x3_kernel Mat 对象中。原始图像中的每个通道都是独立处理的。内核的宽度和高度应该是奇数。高斯分布的 X 方向和 Y 方向的标准偏差将根据核的大小计算。
//Blur the image with 5x5 Gaussian kernel Mat image_blurred_with_5x5_kernel; GaussianBlur(image, image_blurred_with_5x5_kernel, Size(5, 5), 0);
上述函数使用5 x 5高斯滤波器对原始图像执行高斯模糊/平滑操作,并将平滑的图像存储在image_blurred_with_5x5_kernel Mat对象中。原始图像中的每个通道都是独立处理的。内核的宽度和高度应该是奇数。高斯分布的 X 方向和 Y 方向的标准偏差将根据核的大小计算。
//Define names of the windows String window_name = "Lotus"; String window_name_blurred_with_3x3_kernel = "Lotus Blurred with 3 x 3 Gaussian Kernel"; String window_name_blurred_with_5x5_kernel = "Lotus Blurred with 5 x 5 Gaussian Kernel"; // Create windows with above names namedWindow(window_name); namedWindow(window_name_blurred_with_3x3_kernel); namedWindow(window_name_blurred_with_5x5_kernel); // Show our images inside the created windows. imshow(window_name, image); imshow(window_name_blurred_with_3x3_kernel, image_blurred_with_3x3_kernel); imshow(window_name_blurred_with_5x5_kernel, image_blurred_with_5x5_kernel);
上面的代码段创建窗口并在其中显示图像。
视频上的高斯模糊
#include <opencv2/opencv.hpp> #include <iostream> using namespace cv; using namespace std; int main(int argc, char* argv[]) { //读取视频文件内容 VideoCapture cap("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/点云应用.mp4"); //定义窗口名称 String window_name_of_original_video = "原始视频"; String window_name_of_video_blurred_with_5x5_kernel = "5X5高斯核处理的图像"; // 根据前面的窗口标题创建窗口对象 namedWindow(window_name_of_original_video, WINDOW_NORMAL); namedWindow(window_name_of_video_blurred_with_5x5_kernel, WINDOW_NORMAL); while (true) { Mat frame; // 从视频中读取一帧 bool bSuccess = cap.read(frame); if (bSuccess == false) { cout << "视频已经读取完成" << endl; break; } //创建当前帧的5X5高斯核来平滑图像 Mat frame_blurred_with_5x5_kernel; GaussianBlur(frame, frame_blurred_with_5x5_kernel, Size(5, 5), 0); //把当前帧平滑处理的图像显示到对应的窗口上 imshow(window_name_of_original_video, frame); imshow(window_name_of_video_blurred_with_5x5_kernel, frame_blurred_with_5x5_kernel); // 等待10秒按下ESC键退出程序 if (waitKey(10) == 27) { cout << "按下ESC键,停止视频直播" << endl; break; } } return 0; }
反转图像
反转图像就像拍摄图像的负片。
#include <iostream> #include <opencv2/opencv.hpp> #include <QDebug> using namespace cv; using namespace std; int main() { //定义原图像 Mat color; //定义灰度图像,灰度图像反转后的图像 Mat gray, grayDst; // 定义图像的高度和宽度 int height, width; int i, j; //载入图片 color = imread("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/Car.jpg"); if (color.empty()) { qDebug() << "读取错误" << endl; return -1; } //获取图像信息 height = color.rows; //列项要乘通道数 width = color.cols * color.channels(); qDebug() << "彩色图片宽:" << color.cols << ", 高:" << color.rows << ",通道数:" << color.channels() << endl; qDebug() << "实际宽:" << width << endl << endl << endl; // 定义窗口名称 string originColor = "原彩色原图"; string reverseColor = "反转彩色图像"; //创建窗口 namedWindow(originColor, WINDOW_AUTOSIZE); namedWindow(reverseColor, WINDOW_AUTOSIZE); //显示原图 imshow(originColor, color); //彩色图像反转 //算法:反转后的像素a = 255 - a for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { color.at<uchar>(i, j) = 255 - color.at<uchar>(i, j); //对每一个像素反转 } } //保存反色后的图片 cv::imwrite("outputColor.jpg", color); //显示反相图片 imshow(reverseColor, color); //---------------------灰度图像反转------------------ //灰度化,由原图获得灰度图像 cvtColor(color, gray, COLOR_BGR2GRAY); //保存灰度图片 cv::imwrite("gray.jpg", gray); // 获取灰度图的高 height = gray.rows; //列项乘通道数 // 获取灰度图的宽度 width = gray.cols * gray.channels(); qDebug() << "灰度图片宽:" << gray.cols << ", 高:" << gray.rows << ",通道数:" << gray.channels() << endl; qDebug() << "实际宽:" << width << endl << endl << endl; string grayTitle = "原灰度窗口"; string grayReverseTitle = "反转灰度窗口"; namedWindow(grayTitle, WINDOW_AUTOSIZE); namedWindow(grayReverseTitle, WINDOW_AUTOSIZE); //显示灰度图像 imshow(grayTitle, gray); //灰度图像反转 //算法:反转后的像素a = 255 - a grayDst = gray.clone(); for (int i = 0; i < gray.rows; i++) { for (int j = 0; j < gray.cols; j++) { //灰度反转 grayDst.at<uchar>(i, j) = 255 - gray.at<uchar>(i, j); } } //显示反转图像 imshow(grayReverseTitle, grayDst); //保存反色后的图片 imwrite("outputGray.jpg", grayDst); //暂停,保持图像显示 waitKey(0); return 0; }
基于opencv的五种滤波方法
方框滤波–> boxFilter函数来实现 –>线性滤波
均值滤波(邻域平均滤波)–> blur函数 –>线性滤波
高斯滤波–>GaussianBlur函数 –>线性滤波
中值滤波–>medianBlur函数 –>非线性滤波
双边滤波–>bilateralFilter函数 –>非线性滤波
方框滤波
boxFilter( InputArray src, OutputArray dst, int ddepth, Size ksize, Point anchor = Point(-1,-1), bool normalize = true, int borderType = BORDER_DEFAULT )
- src:输入图像
- dst:输出图像
- ddepth:输入图像的深度,-1 代表使用原图深度
- ksize: 滤波内核的大小。一般这样写Size(w, h)来表示内核的大小,Size(10, 10)就表示 10x10 的核大小
- anchor = Point(-1,-1) :表示锚点(即被平滑的那个点),注意他有默认值Point(-1,-1) 如果这个点坐标是负值的话,就表示取核的中心为锚点,所以默认值Point(-1,-1)表示这个锚点在核的中心。
- normalize = true:默认值为true,一个标识符,表示内核是否被其区域归一化(normalized)了
- borderType = BORDER_DEFAULT:用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。
均值滤波
blur( InputArray src,OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT)
- src:输入图像 。
- dst:输出图像 。
- ksize:内核大小 ,一般用 Size(w,h),w 为宽度,h 为深度。
- anchor:被平滑的点,表示取 内核中心 ,默认值 Point(-1,-1)。
- boderType:推断图像外部像素的某种边界模式。默认值 BORDER_DEFAULT
高斯滤波
1 2 3
GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT )
- src:输入图像 。
- dst:输出图像 。
- ksize:ksize.width 和 ksize.height 可以不同,但他们都必须为正数和奇数,或者为0,可由 sigma 计算而来
- sigmaX:高斯核函数在 X 方向的的标准差
- sigmaY:高斯核函数在 Y 方向的的标准差
若 sigmaY 为零,就将它设为 sigmaX;若 sigmaX 和 sigmaY 都是0,那么就由 ksize.width 和 ksize.height 计算出来
中值滤波
medianBlur(InputArray src,OutputArray dst,int ksize)
- src:输入图像 。
- dst:输出图像 。
- ksize:孔径的线性尺寸,这个参数必须是大于1 的奇数
双边滤波
bilateralFilter(InputArray src,OutputArray dst, int d, double sigmaColor,double sigmaSpace, int borderType=BORDER_DEFAULT)
- src: 输入图像,可以是Mat类型,图像必须是8位或浮点型单通道、三通道的图像。
- dst: 输出图像,和原图像有相同的尺寸和类型。
- d: 表示在过滤过程中每个像素邻域的直径范围。如果这个值是非正数,则函数会从第五个参数sigmaSpace计算该值。
- sigmaColor: 颜色空间过滤器的sigma值,这个参数的值月大,表明该像素邻域内有月宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
- sigmaSpace: 坐标空间中滤波器的sigma值,如果该值较大,则意味着颜色相近的较远的像素将相互影响,从而使更大的区域中足够相似的颜色获取相同的颜色。当d>0时,d指定了邻域大小且与sigmaSpace五官,否则d正比于sigmaSpace.
- borderType=BORDER_DEFAULT: 用于推断图像外部像素的某种边界模式,有默认值BORDER_DEFAULT.
上面所有滤波的综合案例
#include <iostream> #include <opencv2/opencv.hpp> #include <QDebug> using namespace cv; using namespace std; int main() { // 读取图像 Mat srcImg = imread("D:/Gerry/project/opencvproj/singleandslot/OpenCV-2/ImageAndVideoHandle/resources/lean_guitar.jpg"); // 定义六个窗口的名称 string srcTitle = "原始图像"; string boxFilterTitle = "方框滤波"; string blurTitle = "均值滤波"; string gaussianTitle = "高斯滤波"; string medianTitle = "中值滤波"; string bilateralTitle = "双边滤波"; // 创建6个窗口 namedWindow(srcTitle, WINDOW_NORMAL); namedWindow(boxFilterTitle, WINDOW_NORMAL); namedWindow(blurTitle, WINDOW_NORMAL); namedWindow(gaussianTitle, WINDOW_NORMAL); namedWindow(medianTitle, WINDOW_NORMAL); namedWindow(bilateralTitle, WINDOW_NORMAL); // 显示原图 imshow(srcTitle, srcImg); // 方框滤波处理 Mat boxFilterImg; // 首先进行方框滤波处理 boxFilter(srcImg, boxFilterImg, srcImg.depth(), Size(5,5)); // 显示到对应窗口上 imshow(boxFilterTitle, boxFilterImg); // 均值滤波处理 Mat blurImg; // 首先进行均值滤波处理 blur(srcImg, blurImg, Size(5,5)); // 显示到对应窗口上 imshow(blurTitle, blurImg); // 高斯率处理 Mat gaussianImg; // 首先进行均值滤波处理 GaussianBlur(srcImg, gaussianImg, Size(5,5), 0); // 显示到对应窗口上 imshow(gaussianTitle, gaussianImg); // 中值滤波处理 Mat medianImg; // 首先进行均值滤波处理 medianBlur(srcImg, medianImg, 9); // 显示到对应窗口上 imshow(medianTitle, medianImg); // 双边滤波处理 Mat bilateralImg; // 首先进行均值滤波处理 bilateralFilter(srcImg, bilateralImg, 11, 21, 19); // 显示到对应窗口上 imshow(bilateralTitle, bilateralImg); waitKey(0); return 0; }