Beispiel #1
0
  // Compute a modified version of the PSNR that takes into account only errors
  // that can be detected by the Human Visual System. Only one channel.
  // Color model must be YUV444 or YUV420.
  // return psnr * 1024 or INFINITE_PSNR (=0)
  public int computePSNR_HVS_M(
      int[] src, int[] dst, int x, int y, int w, int h, int channelIdx, ColorModelType type) {
    if ((type != ColorModelType.YUV420)
        && (type != ColorModelType.YUV422)
        && (type != ColorModelType.YUV444))
      throw new IllegalArgumentException("Invalid image type: must be YUV 420, 422 or 444");

    if ((channelIdx != 0) && (channelIdx != 1) && (channelIdx != 2))
      throw new IllegalArgumentException(
          "Invalid channel index: must be 0 for Y, 1 for U or 2 for V");

    if ((x < 0) || (y < 0))
      throw new IllegalArgumentException("Illegal argument: x and y must be positive or null");

    if ((w <= 0) || (h <= 0))
      throw new IllegalArgumentException("Illegal argument: w and h must be positive");

    if (src == dst) return 0;

    final int mse = computeCSFDeltaAvg(src, dst, x, y, w, h, channelIdx);

    if (mse <= 0) return Global.INFINITE_VALUE;

    // Formula:  double mse = (double) (sum) / size
    //           double psnr = 10 * Math.log10(255d*255d/mse);
    // Calculate PSNR << 10 with 1024 * 10 * (log10(65025L) = 49286
    return 49286 - Global.ten_log10(mse);
  }
Beispiel #2
0
  // return psnr * 1024 or INFINITE_PSNR (=0)
  public int computePSNR(
      int[] data1, int[] data2, int x, int y, int w, int h, ColorModelType type) {
    final long lsum = this.computeDeltaSum(data1, data2, x, y, w, h, type);

    // Rescale to avoid overflow
    final int isum = (int) ((lsum + 50) / 100);

    if (isum <= 0) return Global.INFINITE_VALUE;

    // Formula:  double mse = (double) (sum) / size
    //           double psnr = 10 * Math.log10(255d*255d/mse);
    // or        double psnr = 10 * (Math.log10(65025) + (Math.log10(size) - Math.log10(sum))
    // Calculate PSNR << 10 with 1024 * 10 * (log10(65025L) = 49286
    // 1024*10*log10(100) = 20480
    final int iterations = ((w - x) >> this.downSampling) * ((h - y) >> this.downSampling);
    return 49286 + (Global.ten_log10(iterations) - Global.ten_log10(isum)) - 20480;
  }
Beispiel #3
0
  private int computeCSFDeltaAvg(
      int[] src, int[] dst, int x0, int y0, int w, int h, int channelIdx) {
    final int[] csf;
    final int[] mask_csf;

    if (channelIdx == 0) {
      csf = CSF_Y_1024;
      mask_csf = MASK_CSF_Y_1024;
    } else if (channelIdx == 1) {
      csf = CSF_Cb_1024;
      mask_csf = MASK_CSF_Cb_1024;
    } else {
      csf = CSF_Cr_1024;
      mask_csf = MASK_CSF_Cr_1024;
    }

    long lsum = 0;
    final int[] dct_s = new int[64];
    final int[] dct_d = new int[64];
    int pixels = 0;
    final DCT8 dct = new DCT8();
    IndexedIntArray iia_s = new IndexedIntArray(dct_s, 0);
    IndexedIntArray iia_d = new IndexedIntArray(dct_d, 0);
    int[] s_means_64 = new int[4];
    int[] d_means_64 = new int[4];
    int[] s_vars_1024 = new int[4];
    int[] d_vars_1024 = new int[4];
    final int st = this.stride << this.downSampling;
    final int inc = 1 << this.downSampling;
    final int inc7 = 7 * inc;

    for (int y = y0; y < h - 7; y += inc7) {
      for (int x = x0; x < w - 7; x += inc7) {
        s_means_64[0] = s_means_64[1] = s_means_64[2] = s_means_64[3] = 0;
        d_means_64[0] = d_means_64[1] = d_means_64[2] = d_means_64[3] = 0;
        s_vars_1024[0] = s_vars_1024[1] = s_vars_1024[2] = s_vars_1024[3] = 0;
        d_vars_1024[0] = d_vars_1024[1] = d_vars_1024[2] = d_vars_1024[3] = 0;
        int s_gmean64 = 0;
        int d_gmean64 = 0;
        int s_gvar64 = 0;
        int d_gvar64 = 0;

        // Populate DCT arrays
        for (int i = 0; i < 8; i++) {
          final int i8 = i << 3;
          final int offs = (y + i) * st + (x * inc);

          for (int j = 0; j < 8; j++) {
            final int idx1 = i8 + j;
            final int idx2 = offs + (j * inc);
            final int sub = ((i & 12) >> 2) + ((j & 12) >> 1);
            dct_s[idx1] = src[idx2];
            dct_d[idx1] = dst[idx2];
            s_gmean64 += dct_s[idx1];
            d_gmean64 += dct_d[idx1];
            s_means_64[sub] += (dct_s[idx1] << 2);
            d_means_64[sub] += (dct_d[idx1] << 2);
          }
        }

        // Compute variance
        for (int i = 0; i < 8; i++) {
          final int i8 = i << 3;

          for (int j = 0; j < 8; j++) {
            final int s = dct_s[i8 + j] << 6;
            final int d = dct_d[i8 + j] << 6;
            final int sub = ((i & 12) >> 2) + ((j & 12) >> 1);
            s_gvar64 += (s - s_gmean64) * (s - s_gmean64);
            d_gvar64 += (d - d_gmean64) * (d - d_gmean64);
            s_vars_1024[sub] += (s - s_means_64[sub]) * (s - s_means_64[sub]);
            d_vars_1024[sub] += (d - d_means_64[sub]) * (d - d_means_64[sub]);
          }
        }

        // Replace s_gvar64 /= (63*64) and s_vars_1024[i] *= 16/15 with 63*64*16/15 = 275251/64
        // Since s_vars_1024[i] is scaled by 4 (s_means_1024 scaled by 64*64 instead of 1024),
        // use rescaling factor 275251/256.
        if (s_gvar64 > 0) {
          long sum = (long) (s_vars_1024[0] + s_vars_1024[1] + s_vars_1024[2] + s_vars_1024[3]);
          s_gvar64 = (int) (sum / 256 * 275251 / s_gvar64);
        }

        if (d_gvar64 > 0) {
          long sum = (long) (d_vars_1024[0] + d_vars_1024[1] + d_vars_1024[2] + d_vars_1024[3]);
          d_gvar64 = (int) (sum / 256 * 275251 / d_gvar64);
        }

        // Perform forward DCT (gain is 1<<5)
        iia_s.index = 0;
        dct.forward(iia_s, iia_s);
        iia_d.index = 0;
        dct.forward(iia_d, iia_d);

        // Offset DCT gain
        dct_s[0] >>= 5;
        dct_d[0] >>= 5;

        long s_mask_1024 = 0;
        long d_mask_1024 = 0;

        // Compute masks
        for (int i = 0; i < 8; i++) {
          final int i8 = i << 3;
          final int j0 = (i == 0) ? 1 : 0;

          for (int j = j0; j < 8; j++) {
            final int idx = i8 + j;

            // Offset DCT gain
            dct_s[idx] >>= 5;
            dct_d[idx] >>= 5;
            s_mask_1024 += (dct_s[idx] * dct_s[idx] * mask_csf[idx]);
            d_mask_1024 += (dct_d[idx] * dct_d[idx] * mask_csf[idx]);
          }
        }

        if (d_mask_1024 * d_gvar64 > s_mask_1024 * s_gvar64)
          s_mask_1024 =
              (long) (Global.sqrt((int) d_mask_1024) >> 8) * (long) (Global.sqrt(d_gvar64) >> 7);
        else
          s_mask_1024 =
              (long) (Global.sqrt((int) s_mask_1024) >> 8) * (long) (Global.sqrt(s_gvar64) >> 7);

        // Calculate error
        for (int i = 0; i < 8; i++) {
          final int i8 = i << 3;

          for (int j = 0; j < 8; j++) {
            final int idx = i8 + j;
            long err1024 = ((long) Math.abs(dct_s[idx] - dct_d[idx])) << 10;

            if ((i != 0) || (j != 0))
              err1024 =
                  (err1024 * mask_csf[idx] < s_mask_1024)
                      ? 0
                      : err1024 - (s_mask_1024 / mask_csf[idx]);

            final long val1024 = (err1024 * csf[idx] + 512) >> 10;
            lsum += ((val1024 * val1024) >> 10);
            pixels++;
          }
        }
      }
    }

    return (pixels == 0) ? 0 : (int) (((lsum + 512) >> 10) / pixels);
  }