一、基于图像梯度(空域)
正焦的清晰图像比模糊的离焦图像边缘更加锐利清晰,边缘像素灰度值变化大,因而有更大的梯度值。在进行图像处理时,将图像看作二维离散矩阵,利用梯度函数获取图像灰度信息,以此来评判图像清晰度。在离散信号中梯度表现为差分形式。常用的梯度函数有:能量梯度函数EOG、Roberts函数、Tenengrad函数、Brenner函数、方差Variance函数、拉普拉斯Laplace函数等等。下面分别对其进行介绍。
1、Tenengrad 梯度函数
Tenengrad梯度函数是一种基于图像的边缘信息,通过计算图像中边缘的梯度变化来评估图像的清晰度。边缘信息通常与图像中的高频成分相关,因此,边缘的强度和数量可以作为图像清晰度的指标。
在Tenengrad算法中,首先会对图像应用Sobel等边缘检测算子来检测边缘。然后,计算检测到的边缘的梯度幅值,并对这些幅值进行统计,得到一个表示图像清晰度的度量。通常,边缘梯度幅值的总和或平均值可以作为清晰度评价指标,数值越大说明图像越清晰。
方法存在的缺点:
对噪声敏感:由于Tenengrad梯度函数主要依赖图像的边缘信息,因此它对噪声非常敏感。图像中的噪声可能会导致边缘检测的不准确,进而影响清晰度评价的准确性。
计算复杂度高:为了计算梯度幅值,Tenengrad方法需要对每个像素点应用边缘检测算子,并进行相应的数学运算。因此,在处理大规模图像或实时应用时,计算复杂度可能会成为瓶颈,影响处理速度。
参数依赖性:Tenengrad方法的性能可能受到所选参数的影响,如边缘检测算子的类型和大小。选择合适的参数对于获得准确的清晰度评价至关重要,但这也增加了算法的复杂性和调参的难度。
边缘信息局限性:虽然边缘信息对于评估图像清晰度很重要,但它并不能完全反映图像的所有特征。有些图像可能具有丰富的纹理信息而不是明显的边缘,这种情况下,Tenengrad方法可能无法准确评估图像的清晰度。
c++程序实现:
#include <opencv2/opencv.hpp>
#include <cmath>
#include <iostream>
double calculateTenengrad(const cv::Mat& img) {
cv::Mat grayImg;
if (img.channels() == 3) {
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
} else {
grayImg = img;
}
cv::Mat grad_x, grad_y;
// 使用Sobel算子计算x和y方向的梯度
cv::Sobel(grayImg, grad_x, CV_32F, 1, 0, 3);
cv::Sobel(grayImg, grad_y, CV_32F, 0, 1, 3);
// 计算梯度幅值
cv::Mat grad_mag;
cv::cartToPolar(grad_x, grad_y, grad_mag, cv::Mat(), true);
// 计算梯度幅值的总和作为清晰度评价
double sum = cv::sum(grad_mag)[0];
return sum;
}
int main() {
cv::Mat img = cv::imread("path_to_your_image.jpg", cv::IMREAD_COLOR);
if (img.empty()) {
std::cerr << "Error: Could not read the image." << std::endl;
return -1;
}
double tenengrad = calculateTenengrad(img);
std::cout << "Tenengrad Score: " << tenengrad << std::endl;
return 0;
}
2、 Laplacian梯度函数
Laplacian 梯度函数与Tenengrad梯度函数基本一致,用Laplacian算子替代Sobel算子即可。Laplacian 梯度函数基于图像的二阶导数信息来评估图像的清晰度。对图像中的边缘和突变点非常敏感,因此可以用来衡量图像的细节丰富程度和清晰度。
Laplacian梯度函数通过计算图像的Laplacian算子得到图像的梯度信息。在理想情况下,清晰图像的Laplacian响应(即梯度值的绝对值之和或平方和)应该比模糊图像更大。数值越大图像越清晰。
方法存在的缺点:
噪声敏感性:Laplacian算子对噪声非常敏感,因为噪声通常表现为高频成分,这可能导致清晰度评价的准确性降低。
边缘信息损失:Laplacian算子在检测边缘时,可能会丢失一些边缘信息,尤其是在边缘较宽或梯度变化平缓的情况下。
局部性质:Laplacian算子只关注图像的局部变化,对于全局性的清晰度评价可能不够准确。
阈值选择:在实际应用中,需要设定一个阈值来判断图像的清晰度,而这个阈值的选择可能需要根据具体的应用场景进行调整。
c++程序实现:
#include <opencv2/opencv.hpp>
#include <iostream>
double calculateLaplacianGradient(const cv::Mat& img) {
cv::Mat grayImg;
if (img.channels() == 3) {
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
} else {
grayImg = img;
}
cv::Mat laplacianImg;
cv::Laplacian(grayImg, laplacianImg, CV_64F);
// 计算Laplacian响应的绝对值之和作为清晰度评价
double sumAbs = cv::sum(cv::abs(laplacianImg))[0];
return sumAbs;
}
int main() {
cv::Mat img = cv::imread("path_to_your_image.jpg", cv::IMREAD_COLOR);
if (img.empty()) {
std::cerr << "Error: Could not read the image." << std::endl;
return -1;
}
double laplacianScore = calculateLaplacianGradient(img);
std::cout << "Laplacian Gradient Score: " << laplacianScore << std::endl;
return 0;
}
3、SMD(灰度差分绝对值之和)函数
SMD是用于计算两幅图像或同一图像不同区域之间差异的方法。其基本原理是逐像素地计算两幅图像的灰度值差异,并将所有差异的绝对值求和。这种方法简单直观,计算效率高,适用于需要快速比较图像相似性的场合,数值越大图像越清晰。公式如下:
方法存在的缺点:
对噪声敏感:由于SMD是基于像素级的差异计算,因此它对图像中的噪声非常敏感。即使是很小的噪声也可能导致较大的SMD值,从而影响对图像差异的判断。
忽略空间信息:SMD方法只考虑了像素值之间的差异,而没有考虑像素之间的空间关系。这意味着即使两幅图像的内容完全不同,但只要它们的像素值分布相似,SMD值也可能很小。
对缩放、旋转等变换不鲁棒:如果两幅图像之间存在缩放、旋转等几何变换,那么即使它们的内容相似,SMD值也可能很大。这是因为这些变换会改变像素的空间位置,导致对应像素的灰度值差异增大。
计算效率受图像大小影响:虽然SMD方法的计算效率相对较高,但当处理大尺寸图像时,由于需要遍历图像中的每一个像素,因此计算时间可能会显著增加。
c++程序实现:
#include <iostream>
#include <vector>
#include <cmath>
using namespace std;
// 假设我们有一个简单的灰度图像表示,使用二维vector存储像素值
typedef vector<vector<unsigned char>> GrayImage;
// 计算两幅灰度图像的SMD值
int calculateSMD(const GrayImage& image1, const GrayImage& image2) {
int width = image1.size();
int height = image1[0].size();
int smd = 0;