opencv(C++) 图像滤波

发布于:2025-05-28 ⋅ 阅读:(20) ⋅ 点赞:(0)

介绍

图像是由不同的灰度(或颜色)模式组成的。图像之间的区别在于它们的灰度分布不同。然而,还可以从另一个角度来分析图像——观察图像中灰度值的变化

一些图像包含大面积几乎恒定的亮度区域(例如蓝天),而另一些图像中的灰度值变化非常迅速(例如拥挤的小物体场景)。因此,观察图像中这些变化的频率是表征图像的另一种方式。这种视角被称为频率域(frequency domain),而通过观察灰度分布来描述图像的方式称为空间域(spatial domain)

频率域分析将图像分解为从最低到最高的频率成分。图像中亮度变化缓慢的区域仅包含低频成分,而高频成分则来源于亮度的快速变化。

如傅里叶变换或余弦变换,可以明确显示图像的频率内容。由于图像是二维的,它同时包含垂直方向和水平方向的频率。
在频率域分析框架下,滤波是一种增强(或保留)某些频率带、同时抑制其他频率带的操作。
低通滤波器(low-pass filter)会消除图像的高频成分;相反,高通滤波器(high-pass filter)会消除低频成分。

使用低通滤波器对图像进行滤波

低通滤波器是一种线性滤波器,其核心思想是用一个卷积核(kernel) 对图像中的每个像素进行加权平均操作。
低通滤波器的主要作用是对图像进行模糊和平滑。因为这种滤波器会抑制高频成分,而高频成分正是图像边缘处快速变化所对应的特征
OpenCV 提供了多种低通滤波方法,用于平滑图像、去除噪声、模糊细节。主要函数包括:

  • cv::blur():均值滤波
  • cv::GaussianBlur():高斯滤波

工作原理

均值滤波器(Mean Filter / Box Filter)

每个像素被替换为它周围区域像素的平均值;
卷积核中所有权重相同(例如 3x3 均值滤波核如下):
在这里插入图片描述
函数:cv::blur() 或 cv::boxFilter();
效果:模糊图像、去噪,但边缘会变得模糊不清

高斯滤波器(Gaussian Filter)

使用一个服从高斯分布的卷积核;
中心像素权重最大,远离中心的像素权重逐渐减小;
更好地保留边缘信息;
函数:cv::GaussianBlur();
示例核大小:5x5,σ=1.5;
可分离计算:先水平方向,再垂直方向,提高效率;

像素的权重与其到中心像素的距离成反比。一维高斯函数的形式如下:
在这里插入图片描述
归一化系数 A 的选取使得高斯曲线下的面积等于 1。σ(sigma)值控制了高斯函数的宽度。σ 越大,函数越平坦。
例如,在 σ = 0.5 时,计算区间 [-4, 0, 4] 上的一维高斯滤波器系数,得到如下结果:

[0.0  0.0  0.00026  0.10645  0.78657  0.10645  0.00026  0.0  0.0]

当 σ = 1.5 时,系数变为:

[0.0076  0.03608  0.1096  0.2135  0.2667  0.2135  0.1096  0.0361  0.0076]

这些系数可以通过调用 OpenCV 函数 cv::getGaussianKernel 得到:

cv::Mat gauss = cv::getGaussianKernel(9, sigma, CV_32F);

调用 cv::GaussianBlur() 时:
可指定核大小和 σ 值;
若将 σ 设为 0,则 OpenCV 自动根据核大小选择合适的 σ;
若将核大小设为 cv::Size(0, 0),则自动根据 σ 计算核大小。

两种 σ 值对应的高斯曲线如图所示。高斯函数对称的“钟形”形状使其成为滤波的理想选择。
在这里插入图片描述
距离中心越远的像素权重越低,从而使得像素之间的过渡更加平滑。这与均值滤波中远离中心的像素可能引起当前平均值突变的情况形成对比。从频率角度看,这意味着均值滤波并不能完全去除所有高频成分。

要对图像应用二维高斯滤波器,可以先对图像的每一行应用一维高斯滤波器(用于滤除水平方向的高频),然后再对图像的每一列应用一维高斯滤波器(用于滤除垂直方向的高频)。因为高斯滤波器是可分离滤波器(即二维核可以分解为两个一维核)。OpenCV 中提供了 cv::sepFilter2D 函数用于实现一般的可分离滤波。也可以直接使用 cv::filter2D 应用一个二维核。一般来说,可分离滤波器由于所需乘法运算更少,因此计算效率更高。

在 OpenCV 中使用 cv::GaussianBlur 函数时,可以同时指定核的大小(第三个参数,必须为奇数)和 σ 值(第四个参数)。你也可以只设置 σ 值,让 OpenCV 自动决定合适的核大小(此时核大小设为 0);或者反之,输入核大小并设置 σ 为 0,OpenCV 将根据核大小自动确定最合适的 σ 值。

案例实现

#include <opencv2/opencv.hpp>

int main() {
   
    // 读取图像(灰度图)
    cv::Mat image = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
   
        std::cerr << "无法加载图像!" << std::endl;
        return -1;
    }

    cv::Mat blurResult, gaussianResult;

    // 均值滤波(Box Filter)
    cv::blur(image, blurResult, cv::Size(5, 5));

    // 高斯滤波
    cv::GaussianBlur(image, gaussianResult, cv::Size(5, 5), 1.5);

    // 显示结果
    cv::imshow("Original", image);
    cv::imshow("Blur Result", blurResult);
    cv::imshow("Gaussian Result", gaussianResult);

    cv::waitKey(0);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

#include <opencv2/opencv.hpp>
#include <iostream>

int main() {
   
    // 1. 读取图像(灰度图)
    cv::Mat image = cv::imread(IMAGE_1, cv::IMREAD_GRAYSCALE);
    if (image.empty()) {
   
        std::cerr << "无法加载图像,请确认路径是否正确!" << std::endl;
        return -1;
    }

    // 2. 定义高斯滤波参数
    int kernelSize = 5;  // 核大小(必须为奇数)
    double sigma = 1.5;  // 高斯标准差

    // 3. 应用高斯滤波
    cv::Mat filteredImage;
    cv::GaussianBlur(image, filteredImage, cv::Size(kernelSize, kernelSize), sigma);

    // 4. 可选:获取并显示一维高斯核(用于理解权重分布)
    cv::Mat gaussianKernel = cv::getGaussianKernel(kernelSize, sigma, CV_32F);
    std::cout << "一维高斯核(kernel size = " << kernelSize << ", sigma = " << sigma << "):\n" << gaussianKernel << std::endl;

    // 5. 显示结果
    cv::imshow("Original Image", image);
    cv::imshow("Gaussian Filtered Image", filteredImage);

    // 等待按键关闭窗口
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

在这里插入图片描述

在这里插入图片描述

通过滤波实现图像的下采样

图像经常需要调整大小(重采样)。将图像尺寸缩小的过程通常称为下采样,而放大则称为上采样。执行这些操作面临的挑战是尽可能保留图像的视觉质量。为了实现这一目标,通常使用低通滤波器来去除高频成分,防止出现混叠(aliasing)现象。

图像中包含的高频信息(如细节、边缘)在缩小后无法被小尺寸图像正确表示,从而产生空间混叠。
因此,在下采样前应先应用低通滤波器。

工作原理

  • 图像缩小前必须进行低通滤波,以去除不能在小尺寸图像中正确表示的高频成分;
  • 理论依据是 奈奎斯特-香农采样定理:若将图像缩小 N 倍,则可表示的频率范围也减少 N 倍;
  • OpenCV 提供了封装函数 cv::pyrDown() 和 cv::resize() 来简化操作。
cv::Mat reduced;
cv::pyrDown(image, reduced); // 缩小为原来的一半

cv::pyrDown() 内部使用了一个 5x5 的高斯核进行滤波后再下采样。

cv::Mat resized;
cv::resize(image, resized, cv::Size(image.cols / 4

网站公告

今日签到

点亮在社区的每一天
去签到