什么是图像平滑
图像平滑是一种基本的图像处理技术,旨在减少图像中的噪声,使图像变得更加平滑。
原理
图像中的噪声通常表现为图像灰度值的随机变化,这些噪声可能来自图像采集设备(如相机传感器的电子噪声)或传输过程中的干扰。图像平滑通过对图像中每个像素及其邻域内的像素进行某种加权平均或滤波操作,来降低这种随机变化,从而达到去噪和平滑图像的目的。
线性滤波
均值滤波
原理:均值滤波是一种简单的线性滤波方法。它将图像中每个像素的值替换为其邻域(通常是一个正方形区域,如 3x3、5x5 等)内所有像素值的平均值。例如,对于一个 3x3 的均值滤波器,中心像素的新值为该 3x3 邻域内 9 个像素值的总和除以 9。
优点:简单快速,能够有效降低高斯噪声。
缺点:在去除噪声的同时,可能会模糊图像的边缘和细节,因为它对邻域内所有像素一视同仁,没有区分边缘像素和非边缘像素。
高斯滤波
原理:高斯滤波也是一种线性滤波方法,但它使用高斯函数作为加权系数。在高斯滤波器中,邻域内不同位置的像素对中心像素的贡献程度由高斯函数决定,距离中心像素越近的像素权重越大,越远的像素权重越小。这使得高斯滤波在平滑图像的同时,能相对较好地保留图像的边缘信息。高斯函数的二维表达式为:G(x,y,σ)=2πσ21e−2σ2x2+y2其中,(x,y)是像素在邻域内的坐标,σ是高斯分布的标准差,它控制着高斯函数的宽度,σ越大,高斯函数越宽,平滑效果越强,但图像也会越模糊。
优点:对于高斯噪声有很好的抑制效果,并且在平滑过程中能较好地保留图像的边缘和细节,是一种常用的图像平滑方法。
缺点:计算量相对均值滤波较大,因为需要根据高斯函数计算每个邻域像素的权重。
非线性滤波
中值滤波
原理:中值滤波是非线性滤波方法。它将图像中每个像素的值替换为其邻域内所有像素值的中值。例如,对于一个 3x3 的邻域,将这 9 个像素值从小到大排序,取中间值作为中心像素的新值。
优点:对于椒盐噪声(一种在图像中随机出现的黑白噪声点)有非常好的抑制效果,能够在去除噪声的同时,较好地保留图像的边缘和细节,不会像均值滤波那样过度模糊边缘。
缺点:对高斯噪声的抑制效果不如高斯滤波,并且计算复杂度相对较高,因为需要对邻域内的像素值进行排序。
双边滤波
越近的像素,其权重越大。例如,对于一个二维高斯函数 Gs(x,y,σs)=2πσs21e−2σs2x2+y2,其中 (x,y) 是邻域内像素相对于中心像素的坐标偏移,σs 控制空间域高斯函数的宽度。这部分权重保证了在平滑过程中,邻域内相近位置的像素对中心像素的影响更大。
值域权重:除了空间域权重,双边滤波还引入了值域权重。值域权重基于像素的灰度值(对于彩色图像则基于颜色值)差异来计算。它使用另一个高斯函数来衡量邻域像素与中心像素灰度值的相似程度。若邻域像素与中心像素的灰度值越接近,则其值域权重越大。设中心像素灰度值为 Ip,邻域像素灰度值为 Iq,值域权重的高斯函数可表示为 Gr(∣Ip−Iq∣,σr)=2πσr1e−2σr2∣Ip−Iq∣2,其中 σr 控制值域高斯函数的宽度。
综合权重与滤波:最终的双边滤波权重是空间域权重与值域权重的乘积。对于中心像素 p,其经过双边滤波后的新值 Ip′ 由邻域内所有像素 q 的加权和得到,公式为:
Ip′=∑q∈NpGs(p−q,σs)⋅Gr(∣Ip−Iq∣,σr)∑q∈NpGs(p−q,σs)⋅Gr(∣Ip−Iq∣,σr)⋅Iq
其中 Np 表示中心像素 p 的邻域。通过这种方式,双边滤波在平滑图像时,不仅考虑了像素的空间位置关系,还考虑了像素间的灰度值差异,从而在抑制噪声的同时保留图像的边缘。
实现方法
遍历图像像素:对于图像中的每一个像素,确定其邻域范围(通常是一个正方形区域,如 3×3、5×5 等)。
计算权重:针对邻域内的每个像素,分别计算其空间域权重和值域权重,并将两者相乘得到综合权重。
计算滤波结果:根据综合权重对邻域内像素的灰度值进行加权求和,并除以权重总和,得到中心像素经过双边滤波后的新灰度值。
优缺点
优点
边缘保持:双边滤波的核心优势在于能够在平滑图像的同时有效地保留边缘信息。由于值域权重的作用,在边缘处,邻域像素与中心像素灰度值差异较大,使得其综合权重降低,从而避免了边缘被过度平滑。
降噪效果好:对高斯噪声等常见噪声有较好的抑制作用,能够在一定程度上提高图像的质量。
缺点
计算复杂度高:相比于均值滤波和高斯滤波,双边滤波需要计算空间域和值域两个权重,并且在计算过程中涉及到指数运算,导致计算量大幅增加,处理速度相对较慢。
参数选择困难:双边滤波的效果对参数 σs 和 σr 非常敏感。不合适的参数可能导致图像过度平滑或边缘保留效果不佳。通常需要根据具体图像和应用场景进行多次试验来确定最佳参数。
代码实现
bash
#include <QCoreApplication>
#include <QDebug>
#include <iostream>
#include <opencv2/opencv.hpp>
int main() {
cv::Mat image = cv::imread("1.png");
if (image.empty()) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
// 均值滤波
cv::Mat meanFiltered;
cv::blur(image, meanFiltered, cv::Size(3, 3));
// 高斯滤波
cv::Mat gaussianFiltered;
cv::GaussianBlur(image, gaussianFiltered, cv::Size(5, 5), 1.5);
// 中值滤波
cv::Mat medianFiltered;
cv::medianBlur(image, medianFiltered, 3);
// 双边滤波
cv::Mat bilateralFiltered;
cv::bilateralFilter(image, bilateralFiltered, 9, 75, 75);
// 调整原始图像大小
cv::Mat resizedOriginal;
cv::resize(image, resizedOriginal, cv::Size(), 0.5, 0.5);
cv::imshow("Original Image", resizedOriginal);
// 调整均值滤波后图像大小
cv::Mat resizedMean;
cv::resize(meanFiltered, resizedMean, cv::Size(), 0.5, 0.5);
cv::imshow("Mean Filtered Image", resizedMean);
// 调整高斯滤波后图像大小
cv::Mat resizedGaussian;
cv::resize(gaussianFiltered, resizedGaussian, cv::Size(), 0.5, 0.5);
cv::imshow("Gaussian Filtered Image", resizedGaussian);
// 调整中值滤波后图像大小
cv::Mat resizedMedian;
cv::resize(medianFiltered, resizedMedian, cv::Size(), 0.5, 0.5);
cv::imshow("Median Filtered Image", resizedMedian);
// 调整双边滤波后图像大小
cv::Mat resizedBilateral;
cv::resize(bilateralFiltered, resizedBilateral, cv::Size(), 0.5, 0.5);
cv::imshow("Bilateral Filtered Image", resizedBilateral);
cv::waitKey(0);
return 0;
}

感觉看不出什么变化!
几何变换
平移、选择、缩放、翻转、剪切

bash
#include <QCoreApplication>
#include <QDebug>
#include <opencv2/opencv.hpp>
#include <iostream>
// 图像平移
// 功能:将输入图像按照指定的水平和垂直偏移量进行平移
// 参数:image为输入图像,xOffset为水平方向的偏移量,yOffset为垂直方向的偏移量
// 返回值:平移后的图像
cv::Mat translateImage(const cv::Mat& image, int xOffset, int yOffset) {
// 创建一个2x3的平移矩阵
// 该矩阵形式为:[1 0 xOffset]
// [0 1 yOffset]
// 它定义了图像在二维平面上的平移变换
cv::Mat translationMatrix = (cv::Mat_<double>(2, 3) << 1, 0, xOffset, 0, 1, yOffset);
cv::Mat translatedImage;
// 使用仿射变换函数warpAffine对图像进行平移
// 第一个参数为输入图像,第二个参数为输出图像,第三个参数为变换矩阵
// 第四个参数为输出图像的大小,这里保持与输入图像大小一致
cv::warpAffine(image, translatedImage, translationMatrix, image.size());
return translatedImage;
}
// 图像旋转
// 功能:将输入图像围绕图像中心按照指定角度进行旋转
// 参数:image为输入图像,angle为旋转角度(单位为度)
// 返回值:旋转后的图像
cv::Mat rotateImage(const cv::Mat& image, double angle) {
// 计算图像的中心坐标
cv::Point2f center(image.cols / 2.0, image.rows / 2.0);
// 获取旋转矩阵
// 第一个参数为旋转中心,第二个参数为旋转角度(单位为度),第三个参数为缩放因子(这里为1.0表示不缩放)
cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, 1.0);
cv::Mat rotatedImage;
// 使用仿射变换函数warpAffine对图像进行旋转
// 第一个参数为输入图像,第二个参数为输出图像,第三个参数为变换矩阵
// 第四个参数为输出图像的大小,这里保持与输入图像大小一致
cv::warpAffine(image, rotatedImage, rotationMatrix, image.size());
return rotatedImage;
}
// 图像缩放
// 功能:将输入图像按照指定的缩放因子进行缩放
// 参数:image为输入图像,scaleFactor为缩放因子
// 返回值:缩放后的图像
cv::Mat scaleImage(const cv::Mat& image, double scaleFactor) {
cv::Mat scaledImage;
// 使用resize函数对图像进行缩放
// 第一个参数为输入图像,第二个参数为输出图像
// 第三个参数为输出图像的大小,这里使用cv::Size()表示根据缩放因子自动计算
// 第四个和第五个参数分别为水平和垂直方向的缩放因子,这里都使用scaleFactor
cv::resize(image, scaledImage, cv::Size(), scaleFactor, scaleFactor);
return scaledImage;
}
// 图像翻转
// 功能:将输入图像按照指定的翻转方式进行翻转
// 参数:image为输入图像,flipCode决定翻转方式
// flipCode = 0 垂直翻转
// flipCode > 0 水平翻转
// flipCode < 0 水平和垂直同时翻转
// 返回值:翻转后的图像
cv::Mat flipImage(const cv::Mat& image, int flipCode) {
cv::Mat flippedImage;
// 使用flip函数对图像进行翻转
// 第一个参数为输入图像,第二个参数为输出图像,第三个参数为翻转方式
cv::flip(image, flippedImage, flipCode);
return flippedImage;
}
// 图像剪切
// 功能:将输入图像按照指定的水平和垂直剪切因子进行剪切
// 参数:image为输入图像,shearFactorX为水平方向的剪切因子,shearFactorY为垂直方向的剪切因子
// 返回值:剪切后的图像
cv::Mat shearImage(const cv::Mat& image, double shearFactorX, double shearFactorY) {
// 创建一个2x3的剪切矩阵
// 该矩阵形式为:[1 shearFactorX 0]
// [shearFactorY 1 0]
// 它定义了图像在二维平面上的剪切变换
cv::Mat shearMatrix = (cv::Mat_<double>(2, 3) << 1, shearFactorX, 0, shearFactorY, 1, 0);
cv::Mat shearedImage;
// 使用仿射变换函数warpAffine对图像进行剪切
// 第一个参数为输入图像,第二个参数为输出图像,第三个参数为变换矩阵
// 第四个参数为输出图像的大小,这里保持与输入图像大小一致
cv::warpAffine(image, shearedImage, shearMatrix, image.size());
return shearedImage;
}
int main() {
// 读取图像
cv::Mat image = cv::imread("1.png");
// 如果图像读取失败,输出错误信息并返回
if (image.empty()) {
std::cout << "Could not open or find the image" << std::endl;
return -1;
}
// 平移图像
cv::Mat translatedImage = translateImage(image, 50, 30);
// 显示平移后的图像
cv::imshow("Translated Image", translatedImage);
// 旋转图像(45度)
cv::Mat rotatedImage = rotateImage(image, 45);
// 显示旋转后的图像
cv::imshow("Rotated Image", rotatedImage);
// 缩放图像(0.2倍)
cv::Mat scaledImage = scaleImage(image, 0.2);
// 显示缩放后的图像
cv::imshow("Scaled Image", scaledImage);
// 水平翻转图像
cv::Mat flippedHorizontallyImage = flipImage(image, 1);
// 显示水平翻转后的图像
cv::imshow("Flipped Horizontally Image", flippedHorizontallyImage);
// 垂直翻转图像
cv::Mat flippedVerticallyImage = flipImage(image, 0);
// 显示垂直翻转后的图像
cv::imshow("Flipped Vertically Image", flippedVerticallyImage);
// 剪切图像
cv::Mat shearedImage = shearImage(image, 0.2, 0.2);
// 显示剪切后的图像
cv::imshow("Sheared Image", shearedImage);
// 等待按键按下,0表示无限等待
cv::waitKey(0);
return 0;
}