C++基于3西格玛准则和拉普拉斯变换实现图片质量检测(模糊检测、遮挡检测)
目录
程序简介
程序调用C++的opencv模块,根据拉普拉斯变换计算像素方差来作为图片的模糊程度和遮挡程度的指标值,然后根据参考值构建正态分布,根据3西格玛准则,判断图片是否异常,最终实现了模糊检测和遮挡检测功能。但是本程序使用的数据集为从VOC2007随机选择的图片,对于固定场景,最好选择对应场景的图片集,以及调整正确的参数。
拉普拉斯变换是工程数学中常用的一种积分变换,也可以用作边缘检测。
3西格玛准则又称为拉依达准则,它是先假设一组检测数据只含有随机误差,对其进行计算处理得到标准偏差,按一定概率确定一个区间,认为凡超过这个区间的误差,就不属于随机误差而是粗大误差,含有该误差的数据应予以剔除。
程序/数据集下载
代码分析
依赖库
#include <opencv2/opencv.hpp> #include <iostream>
构建函数,计算均值、方差、标准差
/* * @函数说明: 计算一维向量平均值 * @param std::vector<double> distValues 一维向量 * * @return double 平均值 */ double calAvg(std::vector<double> distValues) { double sum = 0; for (int i = 0; i < distValues.size(); i++) sum += distValues[i]; return sum / distValues.size(); } /* * @函数说明: 计算一维向量方差 * @param std::vector<double> distValues 一维向量 * * @return double 方差 */ double calVar(std::vector<double> distValues) { double avg = calAvg(distValues); double sum = 0; for (int i = 0; i < distValues.size(); i++) sum += pow(distValues[i] - avg, 2); return sum / distValues.size(); } /* * @函数说明: 计算一维向量标准差 * @param std::vector<double> distValues 一维向量 * * @return double 标准差 */ double calStd(std::vector<double> distValues) { double var = calVar(distValues); double sigma = sqrt(var); return sigma; }
构建calLaplacianVar函数,对图片进行拉普拉斯变换,返回方差作为图片清晰度指标
/* * @函数说明:计算拉普拉斯方差,图片越模糊,其边缘就越少 * 所以该方法可用于模糊检测、遮挡检测,衡量图片信息量 * @param cv::Mat img 图像矩阵 * @param cv::Size size 重定图像尺寸 * * @return */ double calLaplacianVar(cv::Mat img, cv::Size size) { //若图片为彩图,转灰度图 cv::Mat grayImg; if (img.channels() == 3) { cv::cvtColor(img, grayImg, CV_BGR2GRAY); } //图片转到固定尺寸 cv::resize(grayImg, grayImg, size); //拉普拉斯变换 cv::Mat sobelImg; cv::Laplacian(grayImg, sobelImg, CV_64FC1); //标准差 cv::Mat mu, sigma; cv::meanStdDev(sobelImg, mu, sigma); double sigmaValue = sigma.at<double>(0, 0); //方差 double variance = pow(sigmaValue, 2); return variance; }
构建类,成员函数initDist用calLaplacianVar函数来读取一个文件夹下的图片的指标值来建立分布
成员函数judgeOutlier用来判断一张新图是否异常
//下面ViewEvaluate::judgeOutlier返回的结果 struct Judgement { bool isOutlier;//是否异常 double value;//指标值 std::string status;//状态字符串 }; //图片质量评估的类 class ViewEvaluate { public: std::vector<double> distValues;//图片指标分布值 cv::Size fixedSize;//重定尺寸 double distAvg;//图片指标分布均值 double distStd;//图片指标分布标准差 double distArea[2];//图片指标分布正常区间 /* * @函数说明: 计算出指定文件夹下所有图片指标,构建分布 * @param std::string imgDir 图片文件夹 * @param cv::Size size 重定尺寸 * * @return */ void initDist(std::string imgDir, cv::Size size) { this->fixedSize = size; //获取文件夹下文件名路径 std::vector<cv::String> imgPaths; cv::glob(imgDir, imgPaths); std::cout << "正在读取" << imgPaths.size() << "张图片并形成高斯分布:"; for (int i = 0; i < imgPaths.size(); i++) { //打印进度 double process = (i + 1) * 100 / imgPaths.size(); std::cout.width(3); std::cout << process << "%"; std::cout << "\b\b\b\b";//回删三个字符,使数字在原地变化 cv::Mat img = cv::imread(imgPaths[i]); double value = calLaplacianVar(img, size); this->distValues.push_back(value); } std::cout << std::endl; //分布均值 this->distAvg = calAvg(this->distValues); //分布标准差 this->distStd = calStd(this->distValues); std::cout << "均值:" << this->distAvg << " 标准差:" << this->distStd << std::endl; } /* * @函数说明:判断指定图片是否为异常图片 * @param cv::Mat img 图片矩阵 * @param int stdTimes 标准差倍数,用来指定正常区间 * * @return */ Judgement judgeOutlier(cv::Mat img, int stdTimes) { //正常值区间 this->distArea[0] = this->distAvg - this->distStd*stdTimes; this->distArea[1] = this->distAvg + this->distStd*stdTimes; //当图片指标不在正常范围内,被判断为异常 Judgement judgement; judgement.value = calLaplacianVar(img, this->fixedSize); if (judgement.value < this->distArea[0] || judgement.value > this->distArea[1]) { judgement.isOutlier = true; judgement.status = "bad"; } else { judgement.isOutlier = false; judgement.status = "normal"; } std::cout << "该图指标值:" << judgement.value << " status:" << judgement.status; std::cout << " 正常区间:[" << this->distArea[0] << " , " << this->distArea[1] << "]" << std::endl; return judgement; } };
对上面的接口进行测试模糊测试和遮挡测试,测试结果如下
int main() { //建立分布的图片文件夹 std::string imgDir = "D:/Desktop/ViewEvaluate_CPP/img"; //重定尺寸 cv::Size size(500, 500); //标准差倍数 double stdTimes = 1; //初始化类对象 初始化分布 ViewEvaluate viewer; viewer.initDist(imgDir, size); //对原图进行测试 cv::Mat img = cv::imread("D:/Desktop/ViewEvaluate_CPP/img/003713.jpg"); cv::resize(img, img, size); Judgement originJudge; originJudge = viewer.judgeOutlier(img, stdTimes); std::stringstream text; text << "status:" << originJudge.status << " value:" << originJudge.value; cv::putText(img, text.str(), cv::Point(0, 50), cv::FONT_HERSHEY_TRIPLEX, 1.0, cv::Scalar(0, 0, 255), 1); cv::imwrite("原图测试.jpg", img); //遮挡测试 cv::Mat coverImg = img.clone(); cv::rectangle(coverImg,cv::Rect(0,0,500,450),cv::Scalar(0,0,0),-1); Judgement coverJudge; coverJudge = viewer.judgeOutlier(coverImg, stdTimes); std::stringstream text2; text2 << "status:" << coverJudge.status << " value:" << coverJudge.value; cv::putText(coverImg, text2.str(), cv::Point(0, 50), cv::FONT_HERSHEY_TRIPLEX, 1.0, cv::Scalar(0, 0, 255), 1); cv::imwrite("遮挡测试.jpg", coverImg); //模糊测试 cv::Mat blurImg = img.clone(); cv::medianBlur(blurImg, blurImg,9); Judgement blurJudge; blurJudge = viewer.judgeOutlier(blurImg, stdTimes); std::stringstream text3; text3 << "status:" << blurJudge.status << " value:" << blurJudge.value; cv::putText(blurImg, text3.str(), cv::Point(0, 50), cv::FONT_HERSHEY_TRIPLEX, 1.0, cv::Scalar(0, 0, 255), 1); cv::imwrite("模糊测试.jpg", blurImg); return 0; }