PageRenderTime 63ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/common/src/DistortionMetricVQM.cpp

https://gitlab.com/standards/HDRTools
C++ | 881 lines | 627 code | 153 blank | 101 comment | 114 complexity | 166c75d45a6dd4f511cc17c6043e3ee5 MD5 | raw file
  1. /* The copyright in this software is being made available under the BSD
  2. * License, included below. This software may be subject to other third party
  3. * and contributor rights, including patent rights, and no such rights are
  4. * granted under this license.
  5. *
  6. * <OWNER> = Samsung Electronics
  7. * <ORGANIZATION> = Samsung Electronics
  8. * <YEAR> = 2015
  9. *
  10. * Copyright (c) 2015, Samsung Electronics
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions are met:
  15. *
  16. * * Redistributions of source code must retain the above copyright notice,
  17. * this list of conditions and the following disclaimer.
  18. * * Redistributions in binary form must reproduce the above copyright notice,
  19. * this list of conditions and the following disclaimer in the documentation
  20. * and/or other materials provided with the distribution.
  21. * * Neither the name of the <ORGANIZATION> nor the names of its contributors may
  22. * be used to endorse or promote products derived from this software without
  23. * specific prior written permission.
  24. *
  25. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  26. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  27. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  28. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
  29. * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  30. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  31. * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  32. * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  33. * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
  35. * THE POSSIBILITY OF SUCH DAMAGE.
  36. */
  37. /*
  38. *************************************************************************************
  39. * \file DistortionMetricVQM.cpp
  40. *
  41. * \brief
  42. * Computes HDRVQM score for input video sequences
  43. *
  44. * The code is based on the following work:
  45. * M. Narwaria, M. P. Da Silva, and P. Le. Callet,
  46. * “HDR-VQM: An Objective Quality Measure for High Dynamic Range Video",
  47. * Signal Processing: Image Communication, 2015.
  48. * https://sites.google.com/site/narwariam/HDR-VQM_SPIC.pdf?attredirects=0&d=1
  49. * https://sites.google.com/site/narwariam/hdr-vqm/
  50. *
  51. * There are small differences in this code versus the original Matlab implementation
  52. * a) A different FFT implementation is used that may result in different results.
  53. * b) The current implementation only supports FFTs that have power of 2 dimensions.
  54. * In the original implementation a size of 896x512 (width x height).
  55. * c) This implementation computes the "true" luminance given the colour primaries
  56. * of the signal.
  57. *
  58. * \author
  59. * - Sarvesh Sahota <sa.sahota@samsung.com>
  60. * - Kulbhushan Pachauri <kb.pachauri@samsung.com>
  61. *************************************************************************************
  62. */
  63. #include "DistortionMetricVQM.H"
  64. #include "ColorTransformGeneric.H"
  65. #include "PUEncode.H"
  66. #include "Frame.H"
  67. #include <algorithm>
  68. #include <functional>
  69. #include <iostream>
  70. #include <cmath>
  71. #include <cassert>
  72. #include <stdio.h>
  73. #include <string.h>
  74. #if defined(ENABLE_SSE_OPT) || defined(ENABLE_AVX_OPT)
  75. #if defined(WIN32) || defined(WIN64)
  76. #include <emmintrin.h>
  77. #else
  78. #include <x86intrin.h>
  79. #include <mmintrin.h>
  80. #endif
  81. #if defined(__SSE3__)
  82. #include <pmmintrin.h>
  83. #endif
  84. #endif
  85. namespace hdrtoolslib {
  86. //-----------------------------------------------------------------------------
  87. // Constructor/destructor
  88. //-----------------------------------------------------------------------------
  89. DistortionMetricVQM::DistortionMetricVQM(const FrameFormat *format, VQMParams *vqmParams)
  90. : DistortionMetric()
  91. {
  92. m_resizeWidth = vqmParams->m_columnsFFT;
  93. m_resizeHeight = vqmParams->m_rowsFFT;
  94. // Currently only powers of two are supported. Resize width/height to the closest power of 2 or fhe original width/height
  95. m_resizeWidth = setClosest(m_resizeWidth, clp2(m_resizeWidth), flp2(m_resizeWidth));
  96. m_resizeHeight = setClosest(m_resizeHeight, clp2(m_resizeHeight), flp2(m_resizeHeight));
  97. m_resizeSize = m_resizeWidth * m_resizeHeight;
  98. m_vqmScore = 0.0;
  99. m_allFrames = false;
  100. m_maxVideo[0] = 0;
  101. m_maxVideo[1] = 0;
  102. m_poolFrameCnt = 0;
  103. m_stTubeCnt = 0;
  104. m_minLuma = vqmParams->m_minDisplay;
  105. m_maxLuma = vqmParams->m_maxDisplay;
  106. m_sortPerc = vqmParams->m_poolingPerc;
  107. //TBD
  108. m_numberOfFrames = vqmParams->m_numberOfFrames;
  109. m_numberOfOrient = vqmParams->m_numberOfOrient;
  110. m_numberOfScale = vqmParams->m_numberOfScale;
  111. // Always limit m_numberOfFramesFixate by m_numberOfFrames so we can check short clips
  112. if (vqmParams->m_frameRate == 0.0f)
  113. m_numberOfFramesFixate = iMin( m_numberOfFrames, (int)ceil(vqmParams->m_fixationTime));
  114. else
  115. m_numberOfFramesFixate = iMin( m_numberOfFrames, (int)ceil(vqmParams->m_frameRate * vqmParams->m_fixationTime));
  116. m_displayAdapt = vqmParams->m_displayAdapt;
  117. float bSizeTemp = (float) ((tan(PI_VQM / 90.0 ) * (double) vqmParams->m_viewingDistance * sqrt(((double) vqmParams->m_rowsDisplay * (double) vqmParams->m_colsDisplay)/ (double) vqmParams->m_displayArea)) / 2.0);
  118. int minDiff = INT_MAX, ind = 0, diff;
  119. for(int i = 2; i < 11; i++) {
  120. diff = (int) (dAbs((double) bSizeTemp - pow(2.0,(double) i)));
  121. if(minDiff > diff) {
  122. minDiff = diff;
  123. ind = i;
  124. }
  125. }
  126. m_bSize = 1 << ind; // pow(2.0, ind);
  127. //For pooling
  128. m_subBandError.resize(m_numberOfFramesFixate);
  129. for(int i = 0; i < m_numberOfFramesFixate; i++) {
  130. m_subBandError[i].resize(m_resizeSize);
  131. }
  132. int tPoolSize = (int) (ceil((double) m_resizeHeight / m_bSize) * ceil((double)m_resizeWidth / m_bSize) * (m_numberOfFrames / m_numberOfFramesFixate));
  133. m_poolError.resize(tPoolSize);
  134. m_rszIn0.resize(m_resizeSize);
  135. m_rszIn1.resize(m_resizeSize);
  136. m_filters.resize(m_numberOfScale * m_numberOfOrient);
  137. for(int i = 0; i< m_numberOfOrient * m_numberOfScale; i++){
  138. m_filters[i].resize(m_resizeSize);
  139. }
  140. //For gabor
  141. m_fftIn0.resize(m_resizeSize);
  142. m_fftIn1.resize(m_resizeSize);
  143. m_fftOut0.resize(m_resizeSize);
  144. m_fftOut1.resize(m_resizeSize);
  145. initLogGabor();
  146. }
  147. DistortionMetricVQM::~DistortionMetricVQM()
  148. {
  149. }
  150. //-----------------------------------------------------------------------------
  151. // Private methods
  152. //-----------------------------------------------------------------------------
  153. int DistortionMetricVQM::vqmRound(double value)
  154. {
  155. #if defined _MSC_VER && defined _M_IX86
  156. int t;
  157. __asm
  158. {
  159. fld value;
  160. fistp t;
  161. }
  162. return t;
  163. #else
  164. return (int)(value + (value >= 0 ? 0.5 : -0.5));
  165. #endif
  166. }
  167. void DistortionMetricVQM::fftShift(float * in, int height, int width)
  168. {
  169. int half_h = (height + 1) >> 1;
  170. int half_w = (width + 1) >> 1;
  171. for (int i = 0; i < height; i++) {
  172. for (int j = 0; j < half_w; j++) {
  173. if((width & 0x01) == 0 && (height & 0x01) == 0) {
  174. if(i< half_h) {
  175. swap(&in[i * width + j], &in[(i + half_h) * width + (j + half_w)]);
  176. }
  177. else {
  178. swap(&in[i * width + j], &in[(i - half_h) * width + (j + half_w)]);
  179. }
  180. }
  181. }
  182. }
  183. }
  184. void DistortionMetricVQM::adaptDisplay(Frame * in, float percMax, int idx)
  185. {
  186. double tMean = 0;
  187. for (int i = 0; i < 3; i++) {
  188. float *Comp = in->m_floatComp[i];
  189. for(int x = 0; x < in->m_compSize[i]; x++) {
  190. if(*Comp < 0.0f)
  191. *Comp = 0.0f;
  192. Comp++;
  193. }
  194. }
  195. std::sort(in->m_floatComp[0], in->m_floatComp[0] + 3 * in->m_compSize[R_COMP], std::less<float>());
  196. int tCount = (int) (3.0 * (double) in->m_compSize[R_COMP] - (double) percMax * 3.0 * (double) in->m_compSize[R_COMP] - 1.0);
  197. for( ; tCount < 3 * in->m_compSize[R_COMP]; tCount++) {
  198. tMean += (double) (*(in->m_floatComp))[tCount];
  199. }
  200. tCount = (int) ((double) percMax * 3.0 * (double) in->m_compSize[R_COMP] + 1.0);
  201. tMean /= (double) (tCount);
  202. if((double) m_maxVideo[idx] < tMean)
  203. m_maxVideo[idx] = (float) tMean;
  204. return;
  205. }
  206. void DistortionMetricVQM::setColorConversion(int colorPrimaries, const double **transform) {
  207. int mode = CTF_IDENTITY;
  208. if (colorPrimaries == CP_709) {
  209. mode = CTF_RGB709_2_YUV709;
  210. }
  211. else if (colorPrimaries == CP_2020) {
  212. mode = CTF_RGB2020_2_YUV2020;
  213. }
  214. else if (colorPrimaries == CP_P3D65) {
  215. mode = CTF_RGBP3D65_2_YUVP3D65;
  216. }
  217. else if (colorPrimaries == CP_P3D65N) {
  218. mode = CTF_RGBP3D65_2_YUV709;
  219. }
  220. else if (colorPrimaries == CP_601) {
  221. mode = CTF_RGB601_2_YUV601;
  222. }
  223. *transform = FWD_TRANSFORM[mode][Y_COMP];
  224. }
  225. void DistortionMetricVQM::clipLuminance(Frame * in)
  226. {
  227. float *Comp0 = in->m_floatComp[0];
  228. float *Comp1 = in->m_floatComp[1];
  229. float *Comp2 = in->m_floatComp[2];
  230. const double *transform = NULL;
  231. setColorConversion(in->m_colorPrimaries, &transform);
  232. for(int x = 0; x < in->m_compSize[R_COMP]; x++) {
  233. double rOrig = dClip(*Comp0, 0.0, 65504.0);
  234. double gOrig = dClip(*Comp1, 0.0, 65504.0);
  235. double bOrig = dClip(*Comp2, 0.0, 65504.0);
  236. double temp = rOrig * transform[R_COMP] + gOrig * transform[G_COMP] + bOrig * transform[B_COMP];
  237. double tempClipped = dClip(temp, m_minLuma, m_maxLuma);
  238. if(temp == 0.0) {
  239. *Comp0 = 0.0f;
  240. *Comp1 = 0.0f;
  241. *Comp2 = 0.0f;
  242. }
  243. else {
  244. *Comp0 = (float) ((double) *Comp0 * tempClipped / temp);
  245. *Comp1 = (float) ((double) *Comp1 * tempClipped / temp);
  246. *Comp2 = (float) ((double) *Comp2 * tempClipped / temp);
  247. }
  248. *Comp0 = (float) ((double) (*Comp0) * transform[R_COMP] + (double) (*Comp1) * transform[G_COMP] + (double) (*Comp2) * transform[B_COMP]);
  249. Comp0++;
  250. Comp1++;
  251. Comp2++;
  252. }
  253. }
  254. int DistortionMetricVQM::binarySearch(const double * in, double val, int start, int end)
  255. {
  256. if((end - start) <= 1)
  257. return end;
  258. int tIdx = (start + end)/2;
  259. int rVal;
  260. if(in[tIdx] < val) {
  261. rVal = binarySearch(in, val, tIdx, end);
  262. }
  263. else if(in[tIdx] > val) {
  264. rVal = binarySearch(in, val, start, tIdx);
  265. }
  266. else
  267. return tIdx;
  268. return rVal;
  269. }
  270. double DistortionMetricVQM::interp1 (const double * in, const double * out, double val)
  271. {
  272. int i = binarySearch(in, val, 0, SIZE_PU_ENC);
  273. double t1 = out[i + 1] - out[i];
  274. double t2 = in [i + 1] - in [i];
  275. return (out[i] + (t1 * (val - in[i])) / t2);
  276. }
  277. void DistortionMetricVQM::puEncode (Frame * in)
  278. {
  279. double t0, t1;
  280. float *Comp0 = in->m_floatComp[Y_COMP];
  281. const double ln10 = log(10.0);
  282. for(int x = 0; x < in->m_compSize[Y_COMP]; x++) {
  283. t0 = log10((double) *Comp0);
  284. t1 = interp1(PUIn, PUOut, t0);
  285. *Comp0++ = (float) exp(t1 * ln10);
  286. }
  287. }
  288. void DistortionMetricVQM::biCubic (float * in, int inWidth, int inHeight, float * out, int outWidth, int outHeight)
  289. {
  290. if(inWidth == outWidth && inHeight == outHeight) {
  291. memcpy(out, in, inHeight * inWidth * sizeof(float));
  292. }
  293. else {
  294. ResizeBiCubic::compute(in, inWidth, inHeight, out, outWidth, outHeight, 1);
  295. }
  296. }
  297. void DistortionMetricVQM::calcLogGabor()
  298. {
  299. #if defined(ENABLE_SSE_OPT) && defined(__SSE3__)
  300. float arr[4], sim[2];
  301. __m128 r1, r2, r3, r4;
  302. float * ptr_i0, * ptr_i1, * ptr_o0, * ptr_o1, * ptr_mul;
  303. int i, o, s;
  304. int tRows = m_resizeHeight;
  305. int tCols = m_resizeWidth;
  306. int tSize = tRows * tCols;
  307. float * t_similarityError = &m_subBandError[m_poolFrameCnt % m_numberOfFramesFixate][0];
  308. for(i=0; i<tRows*tCols; i++) {
  309. m_fftIn0[i].real = m_rszIn0[i];
  310. m_fftIn0[i].imag = 0.0f;
  311. m_fftIn1[i].real = m_rszIn1[i];
  312. m_fftIn1[i].imag = 0.0f;
  313. }
  314. FFT::compute2D(&m_fftIn0[0], tRows, tCols, 1, tCols);
  315. FFT::compute2D(&m_fftIn1[0], tRows, tCols, 1, tCols);
  316. m_metric[0] = 0.0;
  317. /* For scale */
  318. for(s = 0; s < m_numberOfScale; s++) {
  319. /* For orientation */
  320. for(o = 0; o < m_numberOfOrient; o++) {
  321. float *filters = &m_filters[o * m_numberOfScale + s][0];
  322. ptr_i0 = &(m_fftIn0[0].real);
  323. ptr_i1 = &(m_fftIn1[0].real);
  324. ptr_o0 = &(m_fftOut0[0].real);
  325. ptr_o1 = &(m_fftOut1[0].real);
  326. ptr_mul = filters;
  327. for(i=0; i<tSize; i+=2)
  328. {
  329. r1 = _mm_loadu_ps(ptr_i0);
  330. r2 = _mm_loadu_ps(ptr_i1);
  331. r3 = _mm_set_ps(ptr_mul[i+1], ptr_mul[i+1], ptr_mul[i], ptr_mul[i]);
  332. r1 = _mm_mul_ps(r1, r3);
  333. r2 = _mm_mul_ps(r2, r3);
  334. _mm_storeu_ps(ptr_o0, r1);
  335. _mm_storeu_ps(ptr_o1, r2);
  336. ptr_i0 += 4;
  337. ptr_i1 += 4;
  338. ptr_o0 += 4;
  339. ptr_o1 += 4;
  340. }
  341. FFT::compute2D(&m_fftOut0[0], tRows, tCols, -1, tCols);
  342. FFT::compute2D(&m_fftOut1[0], tRows, tCols, -1, tCols);
  343. r3 = _mm_set_ps1((float) tSize);
  344. for(i = 0; i < tSize; i += 2) {
  345. r1 = _mm_loadu_ps((const float *) &m_fftOut0[i]);
  346. r2 = _mm_loadu_ps((const float *) &m_fftOut1[i]);
  347. r1 = _mm_div_ps(r1, r3);
  348. r2 = _mm_div_ps(r2, r3);
  349. r1 = _mm_mul_ps(r1, r1);
  350. r2 = _mm_mul_ps(r2, r2);
  351. r4 = _mm_hadd_ps(r1, r2);
  352. r4 = _mm_sqrt_ps(r4);
  353. _mm_storeu_ps(arr, r4);
  354. sim[0] = (float) ((2.0 * arr[0] * arr[2] + 0.2) / ( arr[0]*arr[0] + arr[2]*arr[2] + 0.2));
  355. sim[1] = (float) ((2.0 * arr[1] * arr[3] + 0.2) / ( arr[1]*arr[1] + arr[3]*arr[3] + 0.2));
  356. t_similarityError[i ] += sim[0];
  357. t_similarityError[i+1] += sim[1];
  358. m_metric[0] += t_similarityError[i];
  359. m_metric[0] += t_similarityError[i + 1];
  360. }
  361. m_metric[0] /= (double) tSize;
  362. }
  363. }
  364. //#elif defined(ENABLE_AVX_OPT)
  365. #else
  366. float t0, t1, t2;
  367. double tSim;
  368. double t3 = 0.0, t4 = 0.0;
  369. int i, o, s;
  370. int tRows = m_resizeHeight;
  371. int tCols = m_resizeWidth;
  372. int tSize = tRows * tCols;
  373. float * t_similarityError = &m_subBandError[m_poolFrameCnt % m_numberOfFramesFixate][0];
  374. for(i=0; i<tRows*tCols; i++) {
  375. m_fftIn0[i].real = m_rszIn0[i];
  376. m_fftIn0[i].imag = 0.0f;
  377. m_fftIn1[i].real = m_rszIn1[i];
  378. m_fftIn1[i].imag = 0.0f;
  379. }
  380. FFT::compute2D(&m_fftIn0[0], tRows, tCols, 1, tCols);
  381. FFT::compute2D(&m_fftIn1[0], tRows, tCols, 1, tCols);
  382. m_metric[0] = 0.0;
  383. /* For scale */
  384. for(s = 0; s < m_numberOfScale; s++) {
  385. /* For orientation */
  386. for(o = 0; o < m_numberOfOrient; o++) {
  387. float *filters = &m_filters[o * m_numberOfScale + s][0];
  388. for(i=0; i<tSize; i++) {
  389. double vFilter = (double) filters[i];
  390. m_fftOut0[i].real = (float) ((double) m_fftIn0[i].real * vFilter);
  391. m_fftOut0[i].imag = (float) ((double) m_fftIn0[i].imag * vFilter);
  392. m_fftOut1[i].real = (float) ((double) m_fftIn1[i].real * vFilter);
  393. m_fftOut1[i].imag = (float) ((double) m_fftIn1[i].imag * vFilter);
  394. }
  395. FFT::compute2D(&m_fftOut0[0], tRows, tCols, -1, tCols);
  396. FFT::compute2D(&m_fftOut1[0], tRows, tCols, -1, tCols);
  397. for(i = 0; i < tSize; i++) {
  398. t0 = (float) ((double) m_fftOut0[i].real / (double) tSize);
  399. t2 = (float) ((double) m_fftOut0[i].imag / (double) tSize);
  400. t3 = ((double) t0 * (double) t0 + (double) t2 * (double) t2);
  401. t3 = sqrt(t3);
  402. t1 = (float) ((double) m_fftOut1[i].real / (double) (tSize));
  403. t2 = (float) ((double) m_fftOut1[i].imag / (double) (tSize));
  404. t4 = ((double) t1 * (double) t1 + (double) t2 * (double) t2);
  405. t4 = sqrt(t4);
  406. //calculating similarity
  407. tSim = (2.0 * t3 * t4 + 0.2)/(t3 * t3 + t4 * t4 + 0.2);
  408. //taking mod of complex number and saving in frame val
  409. //from now, we have the error image in the 0th frame.
  410. //No need of second frame now!
  411. t_similarityError[i] = (float) ((double) t_similarityError[i] + tSim);
  412. m_metric[0] += (double) t_similarityError[i];
  413. }
  414. m_metric[0] /= (double) tSize;
  415. }
  416. }
  417. #endif
  418. return;
  419. }
  420. void DistortionMetricVQM::spatioTemporalPooling()
  421. {
  422. int totalSamples = m_bSize * m_bSize * m_numberOfFramesFixate;
  423. std::vector<float> tStdArr(totalSamples);
  424. float * frameSubBandError = NULL;
  425. double tMean = 0.0, tStdSum = 0.0;
  426. int tidx, i = 0;
  427. int stTubeIdx = (int)(m_stTubeCnt * ceil((double) m_resizeHeight / m_bSize) * m_resizeWidth / m_bSize);
  428. for(int row = 0; row < m_resizeHeight; row += m_bSize) {
  429. for(int col = 0; col < m_resizeWidth; col += m_bSize) {
  430. for(int frame = 0; frame < m_numberOfFramesFixate; frame++) {
  431. frameSubBandError = &m_subBandError[frame][0];
  432. for(int stRow = row; (stRow < row + m_bSize) && (stRow < m_resizeHeight); stRow++) {
  433. for(int stCol = col; (stCol < col + m_bSize) && (stCol < m_resizeWidth); stCol++) {
  434. tidx = stRow * m_resizeWidth + stCol;
  435. tStdArr[i] = frameSubBandError[tidx];
  436. frameSubBandError[tidx] = 0.0f;
  437. tMean += (double) tStdArr[i++];
  438. }
  439. }
  440. }
  441. //here we have all elements of one st-tube
  442. //below is the code for short term spatio-temporal pooling
  443. tMean /= (double) totalSamples;
  444. for(int j = 0; j < totalSamples; j++) {
  445. tStdSum += ((double) tStdArr[j] - tMean) * ((double) tStdArr[j] - tMean);
  446. tStdArr[j] = 0.0f;
  447. }
  448. //all the values of st-tubes stored contiguously in Frame.
  449. //m_poolError[(int)(m_stTubeCnt * ceil((double) m_resizeHeight / m_bSize) * m_resizeWidth / m_bSize) + ((row * m_resizeWidth) / (m_bSize * m_bSize)) + col / m_bSize] = (float) sqrt(tStdSum / (double) totalSamples);
  450. m_poolError[stTubeIdx + ((row * m_resizeWidth) / (m_bSize * m_bSize)) + col / m_bSize] = (float) sqrt(tStdSum / (double) totalSamples);
  451. tMean = 0.0;
  452. tStdSum = 0.0;
  453. i = 0;
  454. }
  455. }
  456. }
  457. void DistortionMetricVQM::longTermPooling()
  458. {
  459. int tRows = ceilDivide(m_resizeHeight, m_bSize); // (int)ceil((double)m_resizeHeight / (double) m_bSize);
  460. int tCols = ceilDivide(m_resizeWidth, m_bSize); // (int)ceil((double)m_resizeWidth / (double) m_bSize);
  461. int tMax = m_numberOfFrames/m_numberOfFramesFixate;
  462. int tSize = tRows * tCols;
  463. int tSize1 = vqmRound((tSize-1) * m_sortPerc) + 1;
  464. // Add one to avoid problem with the subsequent tMax computation
  465. std::vector<float> tArr(tMax + 1);
  466. memset(&tArr[0], 0, sizeof(float)*(tMax + 1));
  467. for(int i = 0; i < tMax; i++) {
  468. std::sort(&m_poolError[i*tSize], &m_poolError[i*tSize+tSize], std::less<float>());
  469. for( int kkk = 0; kkk < tSize1; kkk++) {
  470. tArr[i] = (float) ((double) tArr[i] + (double) m_poolError[i*tSize+kkk]);
  471. }
  472. tArr[i] = (float) ((double) tArr[i] / (double) tSize1);
  473. }
  474. std::sort(&tArr[0], &tArr[tMax], std::less<float>());
  475. tMax = vqmRound((double) (tMax - 1) * (double) m_sortPerc) + 1;
  476. for(int i = 0; i < tMax; i++) {
  477. m_vqmScore = (float) ((double) m_vqmScore + (double) tArr[i]);
  478. }
  479. m_vqmScore = (float) ((double) m_vqmScore / (double) tMax);
  480. //printf("longTermPooling %f\n", m_vqmScore);
  481. //if(m_vqmScore != 0)
  482. // m_vqmScore = 1.0f/m_vqmScore;
  483. //else
  484. // m_vqmScore = 999.0f;
  485. //printf("longTermPooling %f\n", m_vqmScore);
  486. }
  487. void DistortionMetricVQM::initLogGabor()
  488. {
  489. float x_x;
  490. double logRfo;
  491. double angl, cosAngl, sinAngl;
  492. double aTan2;
  493. const double dThetaOnSigma = 1.5;
  494. const double sigmaOnF = 0.55;
  495. const double logSigmaScale = (2.0 * log(sigmaOnF) * log(sigmaOnF));
  496. const double minWaveLength = 3.0;
  497. const double mult = 3.0;
  498. const double logMult = log(mult);
  499. const double thetaSigma = (PI_VQM /((double) m_numberOfOrient * dThetaOnSigma));
  500. const double thetaSigmaSqr = 2.0 * thetaSigma * thetaSigma;
  501. const double lnMinWaveLength = log(2.0) - log(minWaveLength);
  502. int i, j, o, s;
  503. std::vector<float> x (m_resizeSize);
  504. std::vector<float> y (m_resizeSize);
  505. std::vector<float> logRadius(m_resizeSize);
  506. std::vector<float> sinTheta (m_resizeSize);
  507. std::vector<float> cosTheta (m_resizeSize);
  508. std::vector<float> spread (m_resizeSize);
  509. std::vector<float> logGabor (m_resizeSize);
  510. std::vector<float> y_y (m_resizeWidth);
  511. std::vector<float> logWaveLength(m_numberOfScale);
  512. double ds;
  513. double dc;
  514. for(int i=0; i<m_numberOfScale; i++) {
  515. logWaveLength[i] = (float) (lnMinWaveLength - (double) i * logMult);
  516. }
  517. for (i = 0; i < m_resizeHeight; i++) {
  518. x_x = (float)(((double) (2 * i - m_resizeHeight ) / (double) m_resizeHeight ));
  519. for ( j = 0; j < m_resizeWidth; j++) {
  520. y[i * m_resizeWidth + j] = x_x;
  521. }
  522. }
  523. for (i = 0; i < m_resizeWidth; i++) {
  524. y_y[i] = (float)(((double) (2 * i - m_resizeWidth) / (double) m_resizeWidth ));
  525. }
  526. for (j = 0; j < m_resizeSize; j += m_resizeWidth)
  527. memcpy(&x[j],&y_y[0], sizeof(float) * m_resizeWidth);
  528. for (i = 0; i < m_resizeSize; i++) {
  529. logRadius[i] = (float) log((sqrt(((double) x[i] * (double) x[i]) + ((double) y[i] * (double) y[i]))));
  530. aTan2 = atan2((double) y[i], (double) x[i]);
  531. sinTheta [i] = (float) (sin(aTan2));
  532. cosTheta [i] = (float) (cos(aTan2));
  533. }
  534. logRadius[(m_resizeSize + m_resizeHeight) >> 1] = 0.0f;
  535. for (o = 0; o < m_numberOfOrient; o++) {
  536. angl = ((double) o * PI_VQM / (double) m_numberOfOrient);
  537. cosAngl = cos(angl);
  538. sinAngl = sin(angl);
  539. for(i = 0; i < m_resizeSize; i++) {
  540. ds = ((double) sinTheta[i] * cosAngl - (double) cosTheta[i] * sinAngl);
  541. dc = ((double) cosTheta[i] * cosAngl + (double) sinTheta[i] * sinAngl);
  542. aTan2 = atan2(ds, dc);
  543. spread[i] = (float) (exp(-(aTan2 * aTan2) / thetaSigmaSqr));
  544. }
  545. /* For scale */
  546. for (s = 0; s < m_numberOfScale; s++) {
  547. float *filters = &m_filters[o * m_numberOfScale + s][0];
  548. logRfo = (double) logWaveLength[s];
  549. for(i = 0; i < m_resizeSize; i++) {
  550. double curLogRadius = (double) logRadius[i] - logRfo;
  551. logGabor[i] = (float) (exp(-(curLogRadius * curLogRadius) / logSigmaScale));
  552. }
  553. logGabor[(m_resizeSize + m_resizeHeight) >> 1 ] = 0.0f;
  554. for(i = 0; i < m_resizeSize; i++) {
  555. filters[i] = (float) ((double) spread[i] * (double) logGabor[i]);
  556. }
  557. //fftshift
  558. fftShift(filters, m_resizeHeight, m_resizeWidth);
  559. }
  560. }
  561. }
  562. void DistortionMetricVQM::normalizeIntensity(Frame *inp) {
  563. for (int i = 0; i < 3; i++ ) {
  564. float *Comp = inp->m_floatComp[i];
  565. for(int xx = 0; xx < inp->m_compSize[i]; xx++) {
  566. if(*Comp < 0.0f)
  567. *Comp = 0.0f;
  568. else
  569. *Comp = (float) ((double) *Comp * (double) m_maxLuma / (double) m_maxVideo[0]);
  570. Comp++;
  571. }
  572. }
  573. }
  574. #if(VQM_DUMP_STATS == 1)
  575. void dumpFileDouble(char * f_name, double * var, int rows, int cols)
  576. {
  577. FILE *f = fopen(f_name, "w");
  578. for(int jh = 0; jh < rows * cols; jh++) {
  579. fprintf(f, "%lf\n", var[jh]);
  580. }
  581. fclose(f);
  582. }
  583. void dumpFileFloat(char * f_name, float * var, int rows, int cols)
  584. {
  585. FILE *f = fopen(f_name, "w");
  586. for(int jh = 0; jh < rows * cols; jh++) {
  587. fprintf(f, "%f\n", var[jh]);
  588. }
  589. fclose(f);
  590. }
  591. void dumpFileFloatIntA(char * f_name, float var, int count)
  592. {
  593. FILE *f = fopen(f_name, "a");
  594. fprintf(f, "%f\n", var);
  595. fprintf(f, "%d\n", count);
  596. fclose(f);
  597. }
  598. void dumpFileFloat2(char * f_name, Complex * var, int rows, int cols, int i)
  599. {
  600. char temp[256];
  601. sprintf(temp,"%d",i);
  602. strcat(temp, f_name);
  603. std::cout<<"****** "<< temp<<std::endl;
  604. FILE *f = fopen(temp, "w");
  605. for(int jh = 0; jh < rows * cols; jh++) {
  606. fprintf(f, "%f\n", var[jh].real);
  607. }
  608. fclose(f);
  609. }
  610. void dumpFileComplex(char * f_name, char * f_name2, Complex * var, int rows, int cols)
  611. {
  612. FILE *f = fopen(f_name, "a");
  613. for(int jh = 0; jh < rows * cols; jh++) {
  614. fprintf(f, "%f\n", var[jh].real);
  615. }
  616. fclose(f);
  617. }
  618. void dumpFileFloat3(char * f_name, float * var, int rows, int cols, int i)
  619. {
  620. char temp[256];
  621. sprintf(temp,"%d",i);
  622. strcat(temp, f_name);
  623. std::cout<<"****** "<< temp<<std::endl;
  624. FILE *f = fopen(temp, "w");
  625. for(int jh = 0; jh < rows * cols; jh++) {
  626. fprintf(f, "%f\n", var[jh]);
  627. }
  628. fclose(f);
  629. }
  630. void dumpFileDouble3(char * f_name, double * var, int rows, int cols, int i)
  631. {
  632. char temp[256];
  633. sprintf(temp,"%d",i);
  634. strcat(temp, f_name);
  635. std::cout<<"****** "<< temp<<std::endl;
  636. FILE *f = fopen(temp, "w");
  637. for(int jh = 0; jh < rows * cols; jh++) {
  638. fprintf(f, "%lf\n", var[jh]);
  639. }
  640. fclose(f);
  641. }
  642. #endif
  643. //-----------------------------------------------------------------------------
  644. // Public methods
  645. //-----------------------------------------------------------------------------
  646. void DistortionMetricVQM::computeMetric(Frame* inp0, Frame* inp1)
  647. {
  648. if (inp0->m_colorSpace != CM_RGB || inp1->m_colorSpace != CM_RGB) {
  649. printf("Only RGB data currently supported.\n");
  650. return;
  651. }
  652. if (inp0->m_isFloat != TRUE || inp1->m_isFloat != TRUE) {
  653. printf("Only floating point data currently supported.\n");
  654. return;
  655. }
  656. if(!m_allFrames && m_displayAdapt) {
  657. adaptDisplay(inp0, 0.05f, 0);
  658. adaptDisplay(inp1, 0.05f, 1);
  659. }
  660. else {
  661. if(m_displayAdapt) {
  662. normalizeIntensity(inp0);
  663. normalizeIntensity(inp1);
  664. }
  665. clipLuminance(inp0);
  666. clipLuminance(inp1);
  667. puEncode(inp0);
  668. puEncode(inp1);
  669. biCubic(inp0->m_floatComp[0], inp0->m_width[0], inp0->m_height[0], &m_rszIn0[0], m_resizeWidth, m_resizeHeight);
  670. biCubic(inp1->m_floatComp[0], inp1->m_width[0], inp1->m_height[0], &m_rszIn1[0], m_resizeWidth, m_resizeHeight);
  671. calcLogGabor();
  672. if((m_poolFrameCnt % m_numberOfFramesFixate < m_numberOfFramesFixate - 1) && (m_poolFrameCnt < m_numberOfFrames - 1)) {
  673. m_poolFrameCnt++;
  674. }
  675. else {
  676. if(m_poolFrameCnt == m_numberOfFrames - 1) {
  677. if(m_numberOfFrames%m_numberOfFramesFixate == 0) {
  678. spatioTemporalPooling();
  679. }
  680. longTermPooling();
  681. #if(VQM_DUMP_STATS==1)
  682. dumpFileFloat("VQM.txt", &m_vqmScore, 1,1);
  683. #endif
  684. }
  685. else {
  686. spatioTemporalPooling();
  687. }
  688. m_poolFrameCnt++;
  689. m_stTubeCnt++;
  690. }
  691. }
  692. }
  693. // Compute metric for only one component
  694. void DistortionMetricVQM::computeMetric (Frame* inp0, Frame* inp1, int component)
  695. {
  696. return;
  697. }
  698. // report frame level results
  699. void DistortionMetricVQM::reportMetric ()
  700. {
  701. printf(" %10.6f ", m_metric[0]);
  702. return;
  703. }
  704. // report summary results
  705. void DistortionMetricVQM::reportSummary ()
  706. {
  707. printf("%f ", m_vqmScore);
  708. FILE * pFile = fopen("VQM_Scores.txt", "at");
  709. fprintf(pFile, "%f\n", m_vqmScore);
  710. fclose(pFile);
  711. }
  712. void DistortionMetricVQM::reportMinimum ()
  713. {
  714. printf("%f ", m_vqmScore);
  715. }
  716. void DistortionMetricVQM::reportMaximum ()
  717. {
  718. printf("%f ", m_vqmScore);
  719. }
  720. void DistortionMetricVQM::printHeader ()
  721. {
  722. printf(" HDR-VQM ");
  723. }
  724. void DistortionMetricVQM::printSeparator()
  725. {
  726. printf("------------");
  727. }
  728. } // namespace hdrtoolslib