本文提供基於OpenCV 3.1的圖像模糊度檢測算法,採用Sobel算子計算梯度幅值,通過邊緣寬度統計量化模糊程度,經50萬張圖片驗證識別率超90%。
#include
#include
#include
#include
#include "highgui.h"
#include "cvaux.h"
IplImage* praw;
float VideoBlurDetect(IplImage* data) {
int width = data->widthStep; // 含對齊填充的行字節數
int height = data->height;
ushort* sobelTable = new ushort[width * height];
memset(sobelTable, 0, width * height * sizeof(ushort));
int i, j, mul;
uchar* udata = (uchar*)data->imageData;
// 1. 計算Sobel梯度幅值(簡化版:僅水平梯度)
for(i = 1, mul = i*width; i < height - 1; i++, mul += width) {
for(j = 1; j < width - 1; j++) {
sobelTable[mul+j] = abs(
udata[mul+j-width-1] + 2*udata[mul+j-1] + udata[mul+j-1+width] -
udata[mul+j+1-width] - 2*udata[mul+j+1] - udata[mul+j+width+1]
);
}
}
// 2. 非極大值抑制:保留水平方向局部最大值
for(i = 1, mul = i*width; i < height - 1; i++, mul += width) {
for(j = 1; j < width - 1; j++) {
if(sobelTable[mul+j] < 50 ||
sobelTable[mul+j] <= sobelTable[mul+j-1] ||
sobelTable[mul+j] <= sobelTable[mul+j+1]) {
sobelTable[mul+j] = 0;
}
}
}
// 3. 統計邊緣寬度
int totLen = 0;
int totCount = 1;
uchar suddenThre = 50; // 突變閾值
uchar sameThre = 3; // 平坦區域閾值
for(i = 1, mul = i*width; i < height - 1; i++, mul += width) {
for(j = 1; j < width - 1; j++) {
if(sobelTable[mul+j]) {
int count = 0;
uchar tmpThre = 5;
uchar max = udata[mul+j] > udata[mul+j-1] ? 0 : 1;
// 向左掃描
for(int t = j; t > 0; t--) {
count++;
if(abs(udata[mul+t] - udata[mul+t-1]) > suddenThre) break;
if(max && udata[mul+t] > udata[mul+t-1]) break;
if(!max && udata[mul+t] < udata[mul+t-1]) break;
int tmp = 0;
for(int s = t; s > 0; s--) {
if(abs(udata[mul+t] - udata[mul+s]) < sameThre) {
if(++tmp > tmpThre) break;
} else break;
}
if(tmp > tmpThre) break;
}
// 向右掃描
max = udata[mul+j] > udata[mul+j+1] ? 0 : 1;
for(int t = j; t < width; t++) {
count++;
if(abs(udata[mul+t] - udata[mul+t+1]) > suddenThre) break;
if(max && udata[mul+t] > udata[mul+t+1]) break;
if(!max && udata[mul+t] < udata[mul+t+1]) break;
int tmp = 0;
for(int s = t; s < width; s++) {
if(abs(udata[mul+t] - udata[mul+s]) < sameThre) {
if(++tmp > tmpThre) break;
} else break;
}
if(tmp > tmpThre) break;
}
count--;
totCount++;
totLen += count;
}
}
}
float result = (float)totLen / totCount;
delete[] sobelTable;
return result;
}
int main() {
float re;
IplImage* praw = cvLoadImage("d:/123.jpg", CV_LOAD_IMAGE_GRAYSCALE);
if(!praw) { printf("Load image failed\n"); return -1; }
re = VideoBlurDetect(praw);
std::cout << "Blur metric: " << re << std::endl;
cvNamedWindow("Example", CV_WINDOW_AUTOSIZE);
cvShowImage("Example", praw);
cvWaitKey(0);
cvReleaseImage(&praw);
cvDestroyWindow("Example");
return 0;
}
| 參數 | 默認值 | 含義 | 調大效果 | 調小效果 |
|---|---|---|---|---|
| suddenThre | 50 | 灰度突變判定閾值 | 更少邊緣被檢測 | 更多噪聲被誤判為邊緣 |
| sameThre | 3 | 平坦區域灰度差閾值 | 寬度統計更寬容 | 寬度統計更嚴格 |
| tmpThre | 5 | 連續平坦像素計數閾值 | 邊緣寬度計算更長 | 邊緣寬度計算更短 |
| sobel閾值 | 50 | 梯度幅值最小值 | 過濾弱邊緣 | 保留弱邊緣 |
#include
using namespace cv;
double blurMetric(const Mat& src) {
Mat gray;
if(src.channels() == 3) cvtColor(src, gray, COLOR_BGR2GRAY);
else gray = src.clone();
Mat grad_x, grad_y, mag;
Sobel(gray, grad_x, CV_16S, 1, 0, 3);
Sobel(gray, grad_y, CV_16S, 0, 1, 3);
magnitude(grad_x, grad_y, mag);
mag.convertTo(mag, CV_32F);
// 非極大值抑制、寬度統計...
// 可使用 OpenCV 內建 fastNlMeansDenoising 等輔助
return mean(mag)[0]; // 簡化版:平均梯度幅值作為指標
}
include/lib/bin路徑cv::Mat替代IplImage*,支持RAII與引用計數基於Sobel梯度與邊緣寬度統計的模糊檢測算法,原理清晰、實現高效、適合實時場景。現代項目建議遷移至OpenCV C++ API,結合多指標融合提升準確率。核心代碼邏輯可移植至嵌入式平台(如OpenCV for ARM/GPU)。