赞
踩
根据https://github.com/Shiyuuuu/image_metrics/blob/main/niqe.py代码改编,结果精度会不同,如果可以调整的很好的话,欢迎告知
测试代码:
①多线程调用模式
- //多线程调用
- void test_speed()
- {
- string Path = R"(E:\2024Work\2\image_result\pathologic\0clear\0_0.jpg)";
- Mat input_mat = imread(Path);
- string file_path = R"(E:\2024Work\2\ILNIQE\test\Param.txt)";
- ScanQualityEvaluator sqe;
- sqe.start_calc(6, file_path); //启动6个线程,来计算
-
- int loop_counts = 200;
- auto st = CMyLog::getCurrentTime();
- for (int i = 0; i < loop_counts; i++)
- {
- // 图片添加到队列
- sqe.push_back(input_mat.data, input_mat.rows, input_mat.cols,1);
- }
- sqe.stop_calc(); //等待停止
- double total_times = CMyLog::getSpanTimes(st, CMyLog::getCurrentTime());
- cout << total_times / loop_counts << endl;
- }
②单测模式
-
- void test_1()
- {
- string Path = R"(E:\2024Work\2\image_result\pathologic\1blur\0_0.jpg)";
- Mat rgbImg = imread(Path);
- //模型文件
- string file_path = R"(E:\2024Work\2\ILNIQE\test\Param.txt)";
-
- ScanQualityEvaluator::_CalculateILNIQE_Ready(file_path);
-
- ScanQualityEvaluator::_CalculateILNIQE(cut_img);
- }
代码:
- #pragma once
- #include <opencv2/opencv.hpp>
- #include "../BaseCode/BnvQueue.hpp"
-
- //计算扫描质量的接口
- class ScanQualityEvaluator
- {
- public:
- //Path 是均值和方差文件
- static double _CalculateILNIQE(const cv::Mat img, int block_size_h = 96, int block_size_w = 96);
- static int _CalculateILNIQE_Ready(std::string path);
-
-
- //type=0 raw 1 rgb
- int push_back(unsigned char* data, int rows, int cols, int type = 0);
- //启动多少个线程来处理
- int start_calc(int thread_nums,std::string path);
- double stop_calc(); //要等待线程结束 返回计算的值
-
- private:
- static std::tuple<float, float, float> _estimate_aggd_param(const cv::Mat& block);
- static std::vector<float> _compute_feature(const cv::Mat& block);
-
- private:
- void _Start();
- double _Stop();
- int setStatus(int status);
- int config(int thread_nums, std::string path);
- private:
- int m_status; //0 是表示启动 1表示停止
- std::mutex m_img_q_vals_lock;
- std::vector<double> m_img_q_vals;
- BnvQueue<cv::Mat> m_rgb_img_queue;
- std::vector<std::thread> m_threads;
-
- //NIQE的均值,方差矩阵
- static cv::Mat mu_pris_param;
- static cv::Mat cov_pris_param;
- static cv::Mat gaussian_window;
- static std::vector<float> gam;
- static std::vector<float> r_gam;
- };
- #include "ScanQualityEvaluator.hpp"
- #include <vector>
- #include <numeric>
- #include "MyLog.h"
- #include <fstream>
- #include <Tool.h>
- using namespace cv;
- using namespace std;
-
- cv::Mat ScanQualityEvaluator::mu_pris_param;
- cv::Mat ScanQualityEvaluator::cov_pris_param;
- cv::Mat ScanQualityEvaluator::gaussian_window;
- std::vector<float> ScanQualityEvaluator::gam;
- std::vector<float> ScanQualityEvaluator::r_gam;
-
- std::tuple<float, float, float> ScanQualityEvaluator::_estimate_aggd_param(const Mat& block) {
- // 将图像块展平
- Mat block_flattened = block.clone().reshape(1, 1); // 展平为行向量
-
- //计算标准差
- float negative_sum = 0.0, positive_sum = 0.0;
- int negative_counts = 0, positive_counts = 0;
-
-
- float* p = block_flattened.ptr<float>(0);
- float val;
- for (int i = 0; i < block_flattened.cols; ++i)
- {
- val = *p++;
- if (val < 0)
- {
- negative_sum += val *val;
- ++negative_counts;
- }
- else
- {
- positive_sum += val * val;
- ++positive_counts;
- }
- }
-
-
- float left_std = sqrt(negative_sum / negative_counts);
- float right_std = sqrt(positive_sum/ positive_counts);
-
- float gammahat = left_std / right_std;
- float rhat = std::pow(mean(abs(block_flattened))[0], 2) / mean(block_flattened.mul(block_flattened))[0];
- float rhatnorm = (rhat * (std::pow(gammahat, 3) + 1) * (gammahat + 1)) / std::pow((std::pow(gammahat, 2) + 1), 2);
- size_t array_position = std::distance(r_gam.begin(), std::min_element(r_gam.begin(), r_gam.end(), [&](float a, float b) { return std::abs(a - rhatnorm) < std::abs(b - rhatnorm); }));
-
- float alpha = gam[array_position];
- float beta_l = left_std * std::sqrt(std::tgamma(1 / alpha) / std::tgamma(3 / alpha));
- float beta_r = right_std * std::sqrt(std::tgamma(1 / alpha) / std::tgamma(3 / alpha));
-
- return std::make_tuple(alpha, beta_l, beta_r);
- }
-
-
- //50ms
- std::vector<float> ScanQualityEvaluator::_compute_feature(const Mat& block) {
- std::vector<float> feat;
- // 计算 alpha、beta_l 和 beta_r 参数
- float alpha, beta_l, beta_r;
- std::tie(alpha, beta_l, beta_r) = _estimate_aggd_param(block);
-
- // 添加 alpha 和 (beta_l + beta_r) / 2 到特征向量中
- feat.push_back(alpha);
- feat.push_back((beta_l + beta_r) / 2);
-
- // 定义偏移量
- std::vector<std::vector<int>> shifts = { {0, 1}, {1, 0}, {1, 1}, {1, -1} };
-
- // 遍历每个偏移量
- // 滚动图像块
-
- for (size_t i = 0; i < shifts.size(); ++i) {
- Mat shifted_block = block.clone();
- Mat cut_block;
- int y_shift = shifts[i][0]; //y_shift 上下行移动
- int x_shift = shifts[i][1]; //x_shift 是行内移动
- if (y_shift != 0)
- {
- if (y_shift > 0)
- {
- cut_block = shifted_block(Rect(0, shifted_block.rows - y_shift, shifted_block.cols, y_shift));
- shifted_block = shifted_block(Rect(0, 0, shifted_block.cols, shifted_block.rows - y_shift));
- vconcat(cut_block, shifted_block, shifted_block);
- }
- else
- {
- cut_block = shifted_block(Rect(0, 0, shifted_block.cols, abs(y_shift)));
- shifted_block = shifted_block(Rect(0, abs(y_shift), shifted_block.cols, shifted_block.rows - abs(y_shift)));
- vconcat(shifted_block, cut_block, shifted_block);
- }
- }
- if (x_shift != 0)
- {
- if(x_shift > 0)
- {
- cut_block = shifted_block(Rect(shifted_block.cols - x_shift, 0, x_shift, shifted_block.rows));
- shifted_block = shifted_block(Rect(0, 0, shifted_block.cols - x_shift, shifted_block.rows));
- hconcat(cut_block, shifted_block, shifted_block);
- }
- else
- {
- cut_block = shifted_block(Rect(0, 0, abs(x_shift), shifted_block.rows));
- shifted_block = shifted_block(Rect(abs(x_shift), 0, shifted_block.cols - abs(x_shift), shifted_block.rows));
- hconcat(shifted_block, cut_block, shifted_block);
- }
- }
-
- // 计算 alpha、beta_l 和 beta_r 参数
- std::tie(alpha, beta_l, beta_r) = _estimate_aggd_param(block.mul(shifted_block));
-
- // Eq. 8
- float mean = (beta_r - beta_l) * (std::tgamma(2 / alpha) / std::tgamma(1 / alpha));
- feat.push_back(alpha);
- feat.push_back(mean);
- feat.push_back(beta_l);
- feat.push_back(beta_r);
- }
-
-
- return feat;
- }
-
-
- double ScanQualityEvaluator::_CalculateILNIQE(const cv::Mat img, int block_size_h, int block_size_w)
- {
- auto st = CMyLog::getCurrentTime();
- // 将图像转换为浮点型,并归一化到[0, 1]范围
- Mat floatImage;
- img.convertTo(floatImage, CV_32F, 1.0f / 255);
-
- // 使用 OpenCV 函数实现颜色转换和亮度调整
- Mat y_Mat;
- transform(floatImage, y_Mat, cv::Matx13f(65.481f, 128.553f, 24.966f));
- y_Mat += 16.0f;
-
-
- float* p;
- for (int y = 0; y < y_Mat.rows; ++y) {
- p = y_Mat.ptr<float>(y);
- for (int x = 0; x < y_Mat.cols; ++x) {
- *p = std::round(*p);
- ++p;
- }
- }
-
-
- int h = y_Mat.rows;
- int w = y_Mat.cols;
-
- int num_block_h = std::floor(h / block_size_h);
- int num_block_w = std::floor(w / block_size_w);
-
- int cut_width = block_size_w * num_block_w;
- int cut_height = block_size_h * num_block_h;
-
- Rect rect(0, 0, cut_width, cut_height);
- y_Mat = y_Mat(rect);
-
-
-
- vector<float> scales{ 1,2 };
- vector <Mat> distparam;
-
-
- for (auto scale : scales)
- {
- Mat mu;
- filter2D(y_Mat, mu, -1, gaussian_window, Point(-1, -1), 0, BORDER_REPLICATE);
-
- Mat mu_squared = mu.mul(mu);
-
- Mat img_squared = y_Mat.mul(y_Mat);
-
- Mat conv_result;
- filter2D(img_squared, conv_result, -1, gaussian_window, Point(-1, -1), 0, BORDER_REPLICATE);
-
- Mat sigma;
-
- sqrt(abs(conv_result - mu_squared), sigma);
-
- Mat img_nomalized = (y_Mat - mu) / (sigma + 1);
-
- vector <vector<float>> feat;
- vector <float> all_feat;
- for (int idx_w = 0; idx_w < num_block_w; ++idx_w)
- {
- for (int idx_h = 0; idx_h < num_block_h; ++idx_h)
- {
- Rect block_rect(idx_w * block_size_w / scale, idx_h * block_size_h / scale,
- block_size_w / scale, block_size_h / scale);
- Mat block = img_nomalized(block_rect);
-
- // 计算当前块的特征并存储到 feat 中
- auto vals = _compute_feature(block);
- feat.push_back(vals);
- all_feat.insert(all_feat.end(), vals.begin(), vals.end());
- }
- }
-
-
- if (scale == 1)
- {
- resize(y_Mat / 255., y_Mat, Size(), 0.5, 0.5, INTER_LINEAR);
- y_Mat = y_Mat * 255;
- }
-
- Mat featMat(feat.size(), feat[0].size(), CV_32FC1, all_feat.data());
- distparam.emplace_back(featMat.clone());
- }
-
-
- Mat distparam_mat = distparam[0];
- for (int i = 1; i < distparam.size(); ++i)
- {
- hconcat(distparam_mat, distparam[i], distparam_mat);
- }
-
- Mat mu_distparam;
- reduce(distparam_mat, mu_distparam, 0, REDUCE_AVG);
-
-
- Mat cov_distparam;
- calcCovarMatrix(distparam_mat, cov_distparam, mu_distparam, COVAR_NORMAL | COVAR_ROWS | COVAR_SCALE);
-
- cov_distparam.convertTo(cov_distparam, CV_32F);
- mu_distparam.convertTo(mu_distparam, CV_32F);
-
- // 计算 niqe quality
- Mat invcov_param = (cov_pris_param + cov_distparam) / 2;
- invert(invcov_param, invcov_param, DECOMP_SVD);
-
- Mat diff_mat = mu_pris_param - mu_distparam;
-
- Mat quality = diff_mat * invcov_param * diff_mat.t();
- double quality_value = std::sqrt(quality.at<float>(0, 0));
-
- std::cout << "Quality: " << quality_value << std::endl;
- 做个规约转到2-10转为100-0% 分数越小越好
- //quality_value = quality_value < 2 ? 2 : quality_value;
- //quality_value = quality_value >10 ? 10 : quality_value;
-
- //quality_value = 1 - (quality_value - 2) / 8;
-
-
- return quality_value;
-
- }
-
- int ScanQualityEvaluator::_CalculateILNIQE_Ready(std::string path)
- {
- if (mu_pris_param.empty() || cov_pris_param.empty())
- {
- ifstream in_file(path);
- vector<string> lines;
- string line;
- while (getline(in_file, line))
- {
- lines.push_back(line);
- }
- if (lines.size() < 37)
- {
- return -1;
- }
- in_file.close();
- vector<float> mu_pris_param_vec;
- Tool::split_f(lines[0], mu_pris_param_vec, ' ');
-
-
- vector<float> cov_pris_param_vec;
- for (int i = 1; i < 37; ++i)
- {
- vector<float> tmp;
- Tool::split_f(lines[i], tmp, ' ');
- cov_pris_param_vec.insert(cov_pris_param_vec.end(), tmp.begin(), tmp.end());
- }
-
- int param_counts = mu_pris_param_vec.size();
- mu_pris_param = Mat(1, param_counts, CV_32FC1, mu_pris_param_vec.data()).clone();
- cov_pris_param = Mat(param_counts, param_counts, CV_32FC1, cov_pris_param_vec.data()).clone();
- }
-
-
- if (gaussian_window.empty())
- {//生成高斯窗口
- gaussian_window = getGaussianKernel(7, 7.0 / 6, CV_32F);
-
- // 将高斯窗口变为二维矩阵
- gaussian_window = gaussian_window * gaussian_window.t();
-
- // 归一化高斯窗口
- gaussian_window /= sum(gaussian_window)[0];
- }
-
-
- // 计算参数
- for (float i = 0.2; i <= 10.001; i += 0.001) {
- gam.push_back(i);
- }
- std::vector<float> gam_reciprocal(gam.size());
- std::transform(gam.begin(), gam.end(), gam_reciprocal.begin(), [](float x) { return 1.0f / x; });
-
- r_gam.resize(gam.size());
- for (size_t i = 0; i < gam.size(); ++i) {
- float gamma_reciprocal_2 = std::tgamma(gam_reciprocal[i] * 2);
- float gamma_reciprocal_3 = std::tgamma(gam_reciprocal[i] * 3);
- r_gam[i] = std::pow(gamma_reciprocal_2, 2) / (std::tgamma(gam_reciprocal[i]) * gamma_reciprocal_3);
- }
-
- return 0;
- }
-
-
- int ScanQualityEvaluator::config(int thread_nums, std::string path)
- {
- if (_CalculateILNIQE_Ready(path) != 0)
- {
- return -1;
- }
- m_rgb_img_queue.init();
- m_threads.clear();
- m_img_q_vals.clear();
- setStatus(0);
- for (int i = 0; i < thread_nums; ++i)
- {
- m_threads.push_back(thread(&ScanQualityEvaluator::_Start, this));
- }
- return 0;
- }
-
- //raw 转 RGB
- int ScanQualityEvaluator::push_back(unsigned char* data, int rows, int cols, int type)
- {
- Mat rgbImg;
- if (type == 0)
- {
- const Mat srcImg(rows, cols, CV_8UC1, data);
- cvtColor(srcImg, rgbImg, COLOR_BayerRG2RGB);
- }
- else //rgb
- {
- rgbImg = Mat(rows, cols, CV_8UC3, data).clone();
- }
-
- //裁取到最长边1024 加速处理
- int max_size = max(rgbImg.rows, rgbImg.cols);
- double scale = 1024.0 / max_size;
- int new_rows = scale * rgbImg.rows;
- int new_cols = scale * rgbImg.cols;
-
- //在_img中心裁取new_rows*new_cols的图像
- int x = (rgbImg.cols - new_cols) / 2;
- int y = (rgbImg.rows - new_rows) / 2;
- Mat cut_img = rgbImg(Rect(x, y, new_cols, new_rows));
-
- //如果不要裁取就解开注释
- //Mat cut_img = rgbImg;
-
- m_rgb_img_queue.push_back(cut_img.clone());
- return 0;
- }
-
- int ScanQualityEvaluator::start_calc(int thread_nums, std::string path)
- {
- return config(thread_nums, path);
- }
-
- double ScanQualityEvaluator::stop_calc()
- {
- return _Stop();
- }
-
- int ScanQualityEvaluator::setStatus(int status)
- {
- m_status = status;
- return m_status;
- }
-
- void ScanQualityEvaluator::_Start()
- {
- Mat rgbImg;
- while (!(m_status == 1 && m_rgb_img_queue.empty()))
- {
- if (m_rgb_img_queue.pop(rgbImg))
- {
- double ilniqe = _CalculateILNIQE(rgbImg);
- {
- lock_guard<mutex> lk(m_img_q_vals_lock);
- m_img_q_vals.push_back(ilniqe);
- }
- }
- else
- {
- this_thread::sleep_for(10ms);
- }
- }
- }
-
- double ScanQualityEvaluator::_Stop()
- {
- setStatus(1);
- m_rgb_img_queue.finish();
- for (auto& th : m_threads)
- {
- th.join();
- }
- m_threads.clear();
- int offset = 0;
- sort(m_img_q_vals.begin(), m_img_q_vals.end());
-
- if (m_img_q_vals.size() > 100) //超过100个,前20%不参与运算
- {
- offset = m_img_q_vals.size() * 0.2;
- }
- double mean = std::accumulate(m_img_q_vals.begin() + offset, m_img_q_vals.end(), 0.0) / (m_img_q_vals.size() - offset);
-
- return mean;
- }
-
-
-
Param.txt文件的制作(Matlab来生成的)
- setDir = fullfile('E:\2024Work\2\ILNIQE\扫描质量评分\all');
- imds = imageDatastore(setDir,'FileExtensions',{'.jpg'});
-
- model = fitniqe(imds);
-
- mean = model.Mean;
- cov = model.Covariance;
-
-
- dlmwrite('Param.txt', mean, ' ');
- dlmwrite('Param.txt', cov,'delimiter', ' ', '-append');
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。