emor.cpp 4.16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
// Copyright (c) 2012-2017 VideoStitch SAS
// Copyright (c) 2018 stitchEm

#include "libvideostitch/emor.hpp"
#include "emor_coefs.hpp"
#include "emor_inv_coefs.hpp"

#include <algorithm>
#include <cassert>

namespace VideoStitch {
namespace Core {

ResponseCurve::ResponseCurve() : responseCurve(new float[totalLutSize()]), monotonyError(-1) {
  for (int i = 0; i < totalLutSize(); ++i) {
    responseCurve[i] = 0.0f;
  }
}

ResponseCurve::~ResponseCurve() { delete[] responseCurve; }

const float* ResponseCurve::getResponseCurve() const { return responseCurve; }

const float* ResponseCurve::getInverseResponseCurve() const { return responseCurve + lutSize(); }

int ResponseCurve::getMonotonyError() const { return monotonyError; }

void ResponseCurve::invert() {
  for (int i = 0; i < lutSize(); ++i) {
    std::swap(*(responseCurve + i), *(responseCurve + i + lutSize()));
  }
}

void ResponseCurve::calculateMonotonyError() {
  monotonyError = 0;
  int lastVal = 0;
  for (int i = 1; i < lutSize() - 1; ++i) {
    if (i % 4 == 0) {
      int val = (int)(responseCurve[i] * 1024);
      if (val <= lastVal) {
        monotonyError++;
      }
      lastVal = std::max(val, lastVal);
    }
  }
}

void ResponseCurve::makeMonotonous() {
  calculateMonotonyError();

  float maxVal = responseCurve[lutSize() - 1];
  for (int i = 0; i < lutSize() - 1; ++i) {
    if (responseCurve[i + 1] > maxVal) {
      responseCurve[i + 1] = maxVal;
    } else if (responseCurve[i + 1] < responseCurve[i]) {
      responseCurve[i + 1] = responseCurve[i];
    }
  }
}

void ResponseCurve::computeInverseResponse() {
  makeMonotonous();
  float* inverseResponseCurve = responseCurve + lutSize();
  for (int i = 0; i < lutSize() - 1; ++i) {
    const float y0 = lutSize() * responseCurve[i];
    const float y1 = lutSize() * responseCurve[i + 1];
    for (int j = (int)y0; j <= (int)y1 && j < lutSize(); ++j) {
      if (y1 == y0) {
        inverseResponseCurve[j] = y0;
      } else {
        const float x = ((float)j - y0) / (y1 - y0);
        inverseResponseCurve[j] = ((float)i + x) / lutSize();
      }
    }
  }
}

ValueResponseCurve::ValueResponseCurve(const std::array<uint16_t, 256>& values) {
#if (!_MSC_VER || _MSC_VER >= 1900)
  static_assert(4 * std::remove_reference<decltype(values)>::type().size() == lutSize(),
                "Expecting to fill curve by interpolating from 256 to 1024 values");
#endif

  for (size_t index10 = 0; index10 < lutSize(); index10++) {
    const float index8f = (float)index10 / 1023.f * 255.f;
    assert(index8f >= 0.f && index8f < (float)values.size());
    const int lowerIndex8i = std::max(0, std::min((int)index8f, (int)values.size() - 1));
    const int upperIndex8i = std::min(lowerIndex8i + 1, (int)values.size() - 1);
    const float x = index8f - (float)lowerIndex8i;
    float val = (1.0f - x) * values[lowerIndex8i] + x * values[upperIndex8i];
    responseCurve[index10] = val / 1023.f;
  }
  computeInverseResponse();
}

EmorResponseCurve::EmorResponseCurve(double emor1, double emor2, double emor3, double emor4, double emor5)
    : ResponseCurve() {
  for (int i = 0; i < lutSize(); ++i) {
    responseCurve[i] = f0[i];
    responseCurve[i] += (float)emor1 * h1[i];
    responseCurve[i] += (float)emor2 * h2[i];
    responseCurve[i] += (float)emor3 * h3[i];
    responseCurve[i] += (float)emor4 * h4[i];
    responseCurve[i] += (float)emor5 * h5[i];
    if (responseCurve[i] < 0.0f) {
      responseCurve[i] = 0.0f;
    } else if (responseCurve[i] > 1.0f) {
      responseCurve[i] = 1.0f;
    }
  }
  computeInverseResponse();
}

InvEmorResponseCurve::InvEmorResponseCurve(double emor1, double emor2, double emor3, double emor4, double emor5)
    : ResponseCurve() {
  for (int i = 0; i < lutSize(); ++i) {
    responseCurve[i] = fInv0[i];
    responseCurve[i] += (float)emor1 * hInv1[i];
    responseCurve[i] += (float)emor2 * hInv2[i];
    responseCurve[i] += (float)emor3 * hInv3[i];
    responseCurve[i] += (float)emor4 * hInv4[i];
    responseCurve[i] += (float)emor5 * hInv5[i];
    if (responseCurve[i] < 0.0f) {
      responseCurve[i] = 0.0f;
    } else if (responseCurve[i] > 1.0f) {
      responseCurve[i] = 1.0f;
    }
  }
  computeInverseResponse();
}
}  // namespace Core
}  // namespace VideoStitch