brisque_eval_tid2008.cpp 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #include <fstream>
  2. #include "opencv2/quality.hpp"
  3. #include "opencv2/imgproc.hpp"
  4. #include "opencv2/imgcodecs.hpp"
  5. #include "opencv2/ml.hpp"
  6. /*
  7. BRISQUE evaluator using TID2008
  8. TID2008:
  9. http://www.ponomarenko.info/tid2008.htm
  10. [1] N. Ponomarenko, V. Lukin, A. Zelensky, K. Egiazarian, M. Carli,
  11. F. Battisti, "TID2008 - A Database for Evaluation of Full-Reference
  12. Visual Quality Assessment Metrics", Advances of Modern
  13. Radioelectronics, Vol. 10, pp. 30-45, 2009.
  14. [2] N. Ponomarenko, F. Battisti, K. Egiazarian, J. Astola, V. Lukin
  15. "Metrics performance comparison for color image database", Fourth
  16. international workshop on video processing and quality metrics
  17. for consumer electronics, Scottsdale, Arizona, USA. Jan. 14-16, 2009, 6 p.
  18. */
  19. namespace {
  20. // get ordinal ranks of data, fractional ranks assigned for ties. O(n^2) time complexity
  21. // optional binary predicate used for rank ordering of data elements, equality evaluation
  22. template <typename T, typename PrEqual = std::equal_to<T>, typename PrLess = std::less<T>>
  23. std::vector<float> rank_ordinal(const T* data, std::size_t sz, PrEqual&& eq = {}, PrLess&& lt = {})
  24. {
  25. std::vector<float> result{};
  26. result.resize(sz, -1);// set all ranks to -1, indicating not yet done
  27. int rank = 0;
  28. while (rank < (int)sz)
  29. {
  30. std::vector<int> els = {};
  31. for (int i = 0; i < (int)sz; ++i)
  32. {
  33. if (result[i] < 0)//not yet done
  34. {
  35. if (!els.empty())// already found something
  36. {
  37. if (lt(data[i], data[els[0]]))//found a smaller item, replace existing
  38. {
  39. els.clear();
  40. els.emplace_back(i);
  41. }
  42. else if (eq(data[i], data[els[0]]))// found a tie, add to vector
  43. els.emplace_back(i);
  44. }
  45. else//els.empty==no current item, add it
  46. els.emplace_back(i);
  47. }
  48. }
  49. CV_Assert(!els.empty());
  50. // compute, assign arithmetic mean
  51. const auto assigned_rank = (double)rank + (double)(els.size() - 1) / 2.;
  52. for (auto el : els)
  53. result[el] = (float)assigned_rank;
  54. rank += (int)els.size();
  55. }
  56. return result;
  57. }
  58. template <typename T>
  59. double pearson(const T* x, const T* y, std::size_t sz)
  60. {
  61. // based on https://www.geeksforgeeks.org/program-spearmans-rank-correlation/
  62. double sigma_x = {}, sigma_y = {}, sigma_xy = {}, sigma_xsq = {}, sigma_ysq = {};
  63. for (unsigned i = 0; i < sz; ++i)
  64. {
  65. sigma_x += x[i];
  66. sigma_y += y[i];
  67. sigma_xy += x[i] * y[i];
  68. sigma_xsq += x[i] * x[i];
  69. sigma_ysq += y[i] * y[i];
  70. }
  71. const double
  72. num = (sz * sigma_xy - sigma_x * sigma_y)
  73. , den = std::sqrt(((double)sz*sigma_xsq - sigma_x * sigma_x) * ((double)sz*sigma_ysq - sigma_y * sigma_y))
  74. ;
  75. return num / den;
  76. }
  77. // https://en.wikipedia.org/wiki/Spearman%27s_rank_correlation_coefficient
  78. template <typename T>
  79. double spearman(const T* x, const T* y, std::size_t sz)
  80. {
  81. // convert x, y to ranked integral vectors
  82. const auto
  83. x_rank = rank_ordinal(x, sz)
  84. , y_rank = rank_ordinal(y, sz)
  85. ;
  86. return pearson(x_rank.data(), y_rank.data(), sz);
  87. }
  88. // returns cv::Mat of columns: { Distortion Type ID, MOS_Score, Brisque_Score }
  89. cv::Mat tid2008_eval(const std::string& root, cv::quality::QualityBRISQUE& alg)
  90. {
  91. const std::string
  92. mos_with_names_path = root + "mos_with_names.txt"
  93. , dist_imgs_root = root + "distorted_images/"
  94. ;
  95. cv::Mat result(0, 3, CV_32FC1);
  96. // distortion types we care about
  97. static const std::vector<int> distortion_types = {
  98. 10 // jpeg compression
  99. , 11 // jp2k compression
  100. , 1 // additive gaussian noise
  101. , 8 // gaussian blur
  102. };
  103. static const int
  104. num_images = 25 // [I01_ - I25_], file names
  105. , num_distortions = 4 // num distortions per image
  106. ;
  107. // load mos_with_names. format: { mos, fname }
  108. std::vector<std::pair<float, std::string>> mos_with_names = {};
  109. std::ifstream mos_file(mos_with_names_path, std::ios::in);
  110. while (true)
  111. {
  112. std::string line;
  113. std::getline(mos_file, line);
  114. if (!line.empty())
  115. {
  116. const auto space_pos = line.find(' ');
  117. CV_Assert(space_pos != line.npos);
  118. mos_with_names.emplace_back(std::make_pair(
  119. (float)std::atof(line.substr(0, space_pos).c_str())
  120. , line.substr(space_pos + 1)
  121. ));
  122. }
  123. if (mos_file.peek() == EOF)
  124. break;
  125. };
  126. // foreach image
  127. // foreach distortion type
  128. // foreach distortion level
  129. // distortion type id, mos value, brisque value
  130. for (int i = 0; i < num_images; ++i)
  131. {
  132. for (int ty = 0; ty < (int)distortion_types.size(); ++ty)
  133. {
  134. for (int dist = 1; dist <= num_distortions; ++dist)
  135. {
  136. float mos_val = 0.f;
  137. const std::string img_name = std::string("i")
  138. + (((i + 1) < 10) ? "0" : "")
  139. + std::to_string(i + 1)
  140. + "_"
  141. + ((distortion_types[ty] < 10) ? "0" : "")
  142. + std::to_string(distortion_types[ty])
  143. + "_"
  144. + std::to_string(dist)
  145. + ".bmp";
  146. // find mos
  147. bool found = false;
  148. for (const auto& val : mos_with_names)
  149. {
  150. if (val.second == img_name)
  151. {
  152. found = true;
  153. mos_val = val.first;
  154. break;
  155. }
  156. }
  157. CV_Assert(found);
  158. // do brisque
  159. auto img = cv::imread(dist_imgs_root + img_name);
  160. // typeid, mos, brisque
  161. cv::Mat row(1, 3, CV_32FC1);
  162. row.at<float>(0) = (float)distortion_types[ty];
  163. row.at<float>(1) = mos_val;
  164. row.at<float>(2) = (float)alg.compute(img)[0];
  165. result.push_back(row);
  166. }// dist
  167. }//ty
  168. }//i
  169. return result;
  170. }
  171. }
  172. inline void printHelp()
  173. {
  174. using namespace std;
  175. cout << " Demo of comparing BRISQUE quality assessment model against TID2008 database." << endl;
  176. cout << " A. Mittal, A. K. Moorthy and A. C. Bovik, 'No Reference Image Quality Assessment in the Spatial Domain'" << std::endl << std::endl;
  177. cout << " Usage: program <tid2008_path> <brisque_model_path> <brisque_range_path>" << endl << endl;
  178. }
  179. int main(int argc, const char * argv[])
  180. {
  181. using namespace cv::ml;
  182. if (argc != 4)
  183. {
  184. printHelp();
  185. exit(1);
  186. }
  187. std::cout << "Evaluating database at " << argv[1] << "..." << std::endl;
  188. const auto ptr = cv::quality::QualityBRISQUE::create(argv[2], argv[3]);
  189. const auto data = tid2008_eval( std::string( argv[1] ) + "/", *ptr );
  190. // create contiguous mats
  191. const auto mos = data.col(1).clone();
  192. const auto brisque = data.col(2).clone();
  193. // calc srocc
  194. const auto cc = spearman((const float*)mos.data, (const float*)brisque.data, data.rows);
  195. std::cout << "SROCC: " << cc << std::endl;
  196. return 0;
  197. }