当前位置:   article > 正文

NIQE c++实现

NIQE c++实现

根据https://github.com/Shiyuuuu/image_metrics/blob/main/niqe.py代码改编,结果精度会不同,如果可以调整的很好的话,欢迎告知

测试代码:

多线程调用模式

  1. //多线程调用
  2. void test_speed()
  3. {
  4. string Path = R"(E:\2024Work\2\image_result\pathologic\0clear\0_0.jpg)";
  5. Mat input_mat = imread(Path);
  6. string file_path = R"(E:\2024Work\2\ILNIQE\test\Param.txt)";
  7. ScanQualityEvaluator sqe;
  8. sqe.start_calc(6, file_path); //启动6个线程,来计算
  9. int loop_counts = 200;
  10. auto st = CMyLog::getCurrentTime();
  11. for (int i = 0; i < loop_counts; i++)
  12. {
  13. // 图片添加到队列
  14. sqe.push_back(input_mat.data, input_mat.rows, input_mat.cols,1);
  15. }
  16. sqe.stop_calc(); //等待停止
  17. double total_times = CMyLog::getSpanTimes(st, CMyLog::getCurrentTime());
  18. cout << total_times / loop_counts << endl;
  19. }

②单测模式

  1. void test_1()
  2. {
  3. string Path = R"(E:\2024Work\2\image_result\pathologic\1blur\0_0.jpg)";
  4. Mat rgbImg = imread(Path);
  5. //模型文件
  6. string file_path = R"(E:\2024Work\2\ILNIQE\test\Param.txt)";
  7. ScanQualityEvaluator::_CalculateILNIQE_Ready(file_path);
  8. ScanQualityEvaluator::_CalculateILNIQE(cut_img);
  9. }

代码:

  1. #pragma once
  2. #include <opencv2/opencv.hpp>
  3. #include "../BaseCode/BnvQueue.hpp"
  4. //计算扫描质量的接口
  5. class ScanQualityEvaluator
  6. {
  7. public:
  8. //Path 是均值和方差文件
  9. static double _CalculateILNIQE(const cv::Mat img, int block_size_h = 96, int block_size_w = 96);
  10. static int _CalculateILNIQE_Ready(std::string path);
  11. //type=0 raw 1 rgb
  12. int push_back(unsigned char* data, int rows, int cols, int type = 0);
  13. //启动多少个线程来处理
  14. int start_calc(int thread_nums,std::string path);
  15. double stop_calc(); //要等待线程结束 返回计算的值
  16. private:
  17. static std::tuple<float, float, float> _estimate_aggd_param(const cv::Mat& block);
  18. static std::vector<float> _compute_feature(const cv::Mat& block);
  19. private:
  20. void _Start();
  21. double _Stop();
  22. int setStatus(int status);
  23. int config(int thread_nums, std::string path);
  24. private:
  25. int m_status; //0 是表示启动 1表示停止
  26. std::mutex m_img_q_vals_lock;
  27. std::vector<double> m_img_q_vals;
  28. BnvQueue<cv::Mat> m_rgb_img_queue;
  29. std::vector<std::thread> m_threads;
  30. //NIQE的均值,方差矩阵
  31. static cv::Mat mu_pris_param;
  32. static cv::Mat cov_pris_param;
  33. static cv::Mat gaussian_window;
  34. static std::vector<float> gam;
  35. static std::vector<float> r_gam;
  36. };
  1. #include "ScanQualityEvaluator.hpp"
  2. #include <vector>
  3. #include <numeric>
  4. #include "MyLog.h"
  5. #include <fstream>
  6. #include <Tool.h>
  7. using namespace cv;
  8. using namespace std;
  9. cv::Mat ScanQualityEvaluator::mu_pris_param;
  10. cv::Mat ScanQualityEvaluator::cov_pris_param;
  11. cv::Mat ScanQualityEvaluator::gaussian_window;
  12. std::vector<float> ScanQualityEvaluator::gam;
  13. std::vector<float> ScanQualityEvaluator::r_gam;
  14. std::tuple<float, float, float> ScanQualityEvaluator::_estimate_aggd_param(const Mat& block) {
  15. // 将图像块展平
  16. Mat block_flattened = block.clone().reshape(1, 1); // 展平为行向量
  17. //计算标准差
  18. float negative_sum = 0.0, positive_sum = 0.0;
  19. int negative_counts = 0, positive_counts = 0;
  20. float* p = block_flattened.ptr<float>(0);
  21. float val;
  22. for (int i = 0; i < block_flattened.cols; ++i)
  23. {
  24. val = *p++;
  25. if (val < 0)
  26. {
  27. negative_sum += val *val;
  28. ++negative_counts;
  29. }
  30. else
  31. {
  32. positive_sum += val * val;
  33. ++positive_counts;
  34. }
  35. }
  36. float left_std = sqrt(negative_sum / negative_counts);
  37. float right_std = sqrt(positive_sum/ positive_counts);
  38. float gammahat = left_std / right_std;
  39. float rhat = std::pow(mean(abs(block_flattened))[0], 2) / mean(block_flattened.mul(block_flattened))[0];
  40. float rhatnorm = (rhat * (std::pow(gammahat, 3) + 1) * (gammahat + 1)) / std::pow((std::pow(gammahat, 2) + 1), 2);
  41. 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); }));
  42. float alpha = gam[array_position];
  43. float beta_l = left_std * std::sqrt(std::tgamma(1 / alpha) / std::tgamma(3 / alpha));
  44. float beta_r = right_std * std::sqrt(std::tgamma(1 / alpha) / std::tgamma(3 / alpha));
  45. return std::make_tuple(alpha, beta_l, beta_r);
  46. }
  47. //50ms
  48. std::vector<float> ScanQualityEvaluator::_compute_feature(const Mat& block) {
  49. std::vector<float> feat;
  50. // 计算 alpha、beta_l 和 beta_r 参数
  51. float alpha, beta_l, beta_r;
  52. std::tie(alpha, beta_l, beta_r) = _estimate_aggd_param(block);
  53. // 添加 alpha 和 (beta_l + beta_r) / 2 到特征向量中
  54. feat.push_back(alpha);
  55. feat.push_back((beta_l + beta_r) / 2);
  56. // 定义偏移量
  57. std::vector<std::vector<int>> shifts = { {0, 1}, {1, 0}, {1, 1}, {1, -1} };
  58. // 遍历每个偏移量
  59. // 滚动图像块
  60. for (size_t i = 0; i < shifts.size(); ++i) {
  61. Mat shifted_block = block.clone();
  62. Mat cut_block;
  63. int y_shift = shifts[i][0]; //y_shift 上下行移动
  64. int x_shift = shifts[i][1]; //x_shift 是行内移动
  65. if (y_shift != 0)
  66. {
  67. if (y_shift > 0)
  68. {
  69. cut_block = shifted_block(Rect(0, shifted_block.rows - y_shift, shifted_block.cols, y_shift));
  70. shifted_block = shifted_block(Rect(0, 0, shifted_block.cols, shifted_block.rows - y_shift));
  71. vconcat(cut_block, shifted_block, shifted_block);
  72. }
  73. else
  74. {
  75. cut_block = shifted_block(Rect(0, 0, shifted_block.cols, abs(y_shift)));
  76. shifted_block = shifted_block(Rect(0, abs(y_shift), shifted_block.cols, shifted_block.rows - abs(y_shift)));
  77. vconcat(shifted_block, cut_block, shifted_block);
  78. }
  79. }
  80. if (x_shift != 0)
  81. {
  82. if(x_shift > 0)
  83. {
  84. cut_block = shifted_block(Rect(shifted_block.cols - x_shift, 0, x_shift, shifted_block.rows));
  85. shifted_block = shifted_block(Rect(0, 0, shifted_block.cols - x_shift, shifted_block.rows));
  86. hconcat(cut_block, shifted_block, shifted_block);
  87. }
  88. else
  89. {
  90. cut_block = shifted_block(Rect(0, 0, abs(x_shift), shifted_block.rows));
  91. shifted_block = shifted_block(Rect(abs(x_shift), 0, shifted_block.cols - abs(x_shift), shifted_block.rows));
  92. hconcat(shifted_block, cut_block, shifted_block);
  93. }
  94. }
  95. // 计算 alpha、beta_l 和 beta_r 参数
  96. std::tie(alpha, beta_l, beta_r) = _estimate_aggd_param(block.mul(shifted_block));
  97. // Eq. 8
  98. float mean = (beta_r - beta_l) * (std::tgamma(2 / alpha) / std::tgamma(1 / alpha));
  99. feat.push_back(alpha);
  100. feat.push_back(mean);
  101. feat.push_back(beta_l);
  102. feat.push_back(beta_r);
  103. }
  104. return feat;
  105. }
  106. double ScanQualityEvaluator::_CalculateILNIQE(const cv::Mat img, int block_size_h, int block_size_w)
  107. {
  108. auto st = CMyLog::getCurrentTime();
  109. // 将图像转换为浮点型,并归一化到[0, 1]范围
  110. Mat floatImage;
  111. img.convertTo(floatImage, CV_32F, 1.0f / 255);
  112. // 使用 OpenCV 函数实现颜色转换和亮度调整
  113. Mat y_Mat;
  114. transform(floatImage, y_Mat, cv::Matx13f(65.481f, 128.553f, 24.966f));
  115. y_Mat += 16.0f;
  116. float* p;
  117. for (int y = 0; y < y_Mat.rows; ++y) {
  118. p = y_Mat.ptr<float>(y);
  119. for (int x = 0; x < y_Mat.cols; ++x) {
  120. *p = std::round(*p);
  121. ++p;
  122. }
  123. }
  124. int h = y_Mat.rows;
  125. int w = y_Mat.cols;
  126. int num_block_h = std::floor(h / block_size_h);
  127. int num_block_w = std::floor(w / block_size_w);
  128. int cut_width = block_size_w * num_block_w;
  129. int cut_height = block_size_h * num_block_h;
  130. Rect rect(0, 0, cut_width, cut_height);
  131. y_Mat = y_Mat(rect);
  132. vector<float> scales{ 1,2 };
  133. vector <Mat> distparam;
  134. for (auto scale : scales)
  135. {
  136. Mat mu;
  137. filter2D(y_Mat, mu, -1, gaussian_window, Point(-1, -1), 0, BORDER_REPLICATE);
  138. Mat mu_squared = mu.mul(mu);
  139. Mat img_squared = y_Mat.mul(y_Mat);
  140. Mat conv_result;
  141. filter2D(img_squared, conv_result, -1, gaussian_window, Point(-1, -1), 0, BORDER_REPLICATE);
  142. Mat sigma;
  143. sqrt(abs(conv_result - mu_squared), sigma);
  144. Mat img_nomalized = (y_Mat - mu) / (sigma + 1);
  145. vector <vector<float>> feat;
  146. vector <float> all_feat;
  147. for (int idx_w = 0; idx_w < num_block_w; ++idx_w)
  148. {
  149. for (int idx_h = 0; idx_h < num_block_h; ++idx_h)
  150. {
  151. Rect block_rect(idx_w * block_size_w / scale, idx_h * block_size_h / scale,
  152. block_size_w / scale, block_size_h / scale);
  153. Mat block = img_nomalized(block_rect);
  154. // 计算当前块的特征并存储到 feat 中
  155. auto vals = _compute_feature(block);
  156. feat.push_back(vals);
  157. all_feat.insert(all_feat.end(), vals.begin(), vals.end());
  158. }
  159. }
  160. if (scale == 1)
  161. {
  162. resize(y_Mat / 255., y_Mat, Size(), 0.5, 0.5, INTER_LINEAR);
  163. y_Mat = y_Mat * 255;
  164. }
  165. Mat featMat(feat.size(), feat[0].size(), CV_32FC1, all_feat.data());
  166. distparam.emplace_back(featMat.clone());
  167. }
  168. Mat distparam_mat = distparam[0];
  169. for (int i = 1; i < distparam.size(); ++i)
  170. {
  171. hconcat(distparam_mat, distparam[i], distparam_mat);
  172. }
  173. Mat mu_distparam;
  174. reduce(distparam_mat, mu_distparam, 0, REDUCE_AVG);
  175. Mat cov_distparam;
  176. calcCovarMatrix(distparam_mat, cov_distparam, mu_distparam, COVAR_NORMAL | COVAR_ROWS | COVAR_SCALE);
  177. cov_distparam.convertTo(cov_distparam, CV_32F);
  178. mu_distparam.convertTo(mu_distparam, CV_32F);
  179. // 计算 niqe quality
  180. Mat invcov_param = (cov_pris_param + cov_distparam) / 2;
  181. invert(invcov_param, invcov_param, DECOMP_SVD);
  182. Mat diff_mat = mu_pris_param - mu_distparam;
  183. Mat quality = diff_mat * invcov_param * diff_mat.t();
  184. double quality_value = std::sqrt(quality.at<float>(0, 0));
  185. std::cout << "Quality: " << quality_value << std::endl;
  186. 做个规约转到2-10转为100-0% 分数越小越好
  187. //quality_value = quality_value < 2 ? 2 : quality_value;
  188. //quality_value = quality_value >10 ? 10 : quality_value;
  189. //quality_value = 1 - (quality_value - 2) / 8;
  190. return quality_value;
  191. }
  192. int ScanQualityEvaluator::_CalculateILNIQE_Ready(std::string path)
  193. {
  194. if (mu_pris_param.empty() || cov_pris_param.empty())
  195. {
  196. ifstream in_file(path);
  197. vector<string> lines;
  198. string line;
  199. while (getline(in_file, line))
  200. {
  201. lines.push_back(line);
  202. }
  203. if (lines.size() < 37)
  204. {
  205. return -1;
  206. }
  207. in_file.close();
  208. vector<float> mu_pris_param_vec;
  209. Tool::split_f(lines[0], mu_pris_param_vec, ' ');
  210. vector<float> cov_pris_param_vec;
  211. for (int i = 1; i < 37; ++i)
  212. {
  213. vector<float> tmp;
  214. Tool::split_f(lines[i], tmp, ' ');
  215. cov_pris_param_vec.insert(cov_pris_param_vec.end(), tmp.begin(), tmp.end());
  216. }
  217. int param_counts = mu_pris_param_vec.size();
  218. mu_pris_param = Mat(1, param_counts, CV_32FC1, mu_pris_param_vec.data()).clone();
  219. cov_pris_param = Mat(param_counts, param_counts, CV_32FC1, cov_pris_param_vec.data()).clone();
  220. }
  221. if (gaussian_window.empty())
  222. {//生成高斯窗口
  223. gaussian_window = getGaussianKernel(7, 7.0 / 6, CV_32F);
  224. // 将高斯窗口变为二维矩阵
  225. gaussian_window = gaussian_window * gaussian_window.t();
  226. // 归一化高斯窗口
  227. gaussian_window /= sum(gaussian_window)[0];
  228. }
  229. // 计算参数
  230. for (float i = 0.2; i <= 10.001; i += 0.001) {
  231. gam.push_back(i);
  232. }
  233. std::vector<float> gam_reciprocal(gam.size());
  234. std::transform(gam.begin(), gam.end(), gam_reciprocal.begin(), [](float x) { return 1.0f / x; });
  235. r_gam.resize(gam.size());
  236. for (size_t i = 0; i < gam.size(); ++i) {
  237. float gamma_reciprocal_2 = std::tgamma(gam_reciprocal[i] * 2);
  238. float gamma_reciprocal_3 = std::tgamma(gam_reciprocal[i] * 3);
  239. r_gam[i] = std::pow(gamma_reciprocal_2, 2) / (std::tgamma(gam_reciprocal[i]) * gamma_reciprocal_3);
  240. }
  241. return 0;
  242. }
  243. int ScanQualityEvaluator::config(int thread_nums, std::string path)
  244. {
  245. if (_CalculateILNIQE_Ready(path) != 0)
  246. {
  247. return -1;
  248. }
  249. m_rgb_img_queue.init();
  250. m_threads.clear();
  251. m_img_q_vals.clear();
  252. setStatus(0);
  253. for (int i = 0; i < thread_nums; ++i)
  254. {
  255. m_threads.push_back(thread(&ScanQualityEvaluator::_Start, this));
  256. }
  257. return 0;
  258. }
  259. //raw 转 RGB
  260. int ScanQualityEvaluator::push_back(unsigned char* data, int rows, int cols, int type)
  261. {
  262. Mat rgbImg;
  263. if (type == 0)
  264. {
  265. const Mat srcImg(rows, cols, CV_8UC1, data);
  266. cvtColor(srcImg, rgbImg, COLOR_BayerRG2RGB);
  267. }
  268. else //rgb
  269. {
  270. rgbImg = Mat(rows, cols, CV_8UC3, data).clone();
  271. }
  272. //裁取到最长边1024 加速处理
  273. int max_size = max(rgbImg.rows, rgbImg.cols);
  274. double scale = 1024.0 / max_size;
  275. int new_rows = scale * rgbImg.rows;
  276. int new_cols = scale * rgbImg.cols;
  277. //在_img中心裁取new_rows*new_cols的图像
  278. int x = (rgbImg.cols - new_cols) / 2;
  279. int y = (rgbImg.rows - new_rows) / 2;
  280. Mat cut_img = rgbImg(Rect(x, y, new_cols, new_rows));
  281. //如果不要裁取就解开注释
  282. //Mat cut_img = rgbImg;
  283. m_rgb_img_queue.push_back(cut_img.clone());
  284. return 0;
  285. }
  286. int ScanQualityEvaluator::start_calc(int thread_nums, std::string path)
  287. {
  288. return config(thread_nums, path);
  289. }
  290. double ScanQualityEvaluator::stop_calc()
  291. {
  292. return _Stop();
  293. }
  294. int ScanQualityEvaluator::setStatus(int status)
  295. {
  296. m_status = status;
  297. return m_status;
  298. }
  299. void ScanQualityEvaluator::_Start()
  300. {
  301. Mat rgbImg;
  302. while (!(m_status == 1 && m_rgb_img_queue.empty()))
  303. {
  304. if (m_rgb_img_queue.pop(rgbImg))
  305. {
  306. double ilniqe = _CalculateILNIQE(rgbImg);
  307. {
  308. lock_guard<mutex> lk(m_img_q_vals_lock);
  309. m_img_q_vals.push_back(ilniqe);
  310. }
  311. }
  312. else
  313. {
  314. this_thread::sleep_for(10ms);
  315. }
  316. }
  317. }
  318. double ScanQualityEvaluator::_Stop()
  319. {
  320. setStatus(1);
  321. m_rgb_img_queue.finish();
  322. for (auto& th : m_threads)
  323. {
  324. th.join();
  325. }
  326. m_threads.clear();
  327. int offset = 0;
  328. sort(m_img_q_vals.begin(), m_img_q_vals.end());
  329. if (m_img_q_vals.size() > 100) //超过100个,前20%不参与运算
  330. {
  331. offset = m_img_q_vals.size() * 0.2;
  332. }
  333. double mean = std::accumulate(m_img_q_vals.begin() + offset, m_img_q_vals.end(), 0.0) / (m_img_q_vals.size() - offset);
  334. return mean;
  335. }

Param.txt文件的制作(Matlab来生成的)

  1. setDir = fullfile('E:\2024Work\2\ILNIQE\扫描质量评分\all');
  2. imds = imageDatastore(setDir,'FileExtensions',{'.jpg'});
  3. model = fitniqe(imds);
  4. mean = model.Mean;
  5. cov = model.Covariance;
  6. dlmwrite('Param.txt', mean, ' ');
  7. dlmwrite('Param.txt', cov,'delimiter', ' ', '-append');

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/200198
推荐阅读
相关标签
  

闽ICP备14008679号