18 #ifndef PIC_ALGORITHMS_CAMERA_RESPONSE_FUNCTION_HPP 19 #define PIC_ALGORITHMS_CAMERA_RESPONSE_FUNCTION_HPP 23 #include "../image.hpp" 24 #include "../point_samplers/sampler_random.hpp" 25 #include "../histogram.hpp" 26 #include "../filtering/filter_mean.hpp" 27 #include "../util/polynomial.hpp" 29 #include "../algorithms/sub_sample_stack.hpp" 30 #include "../algorithms/weight_function.hpp" 31 #include "../algorithms/mitsunaga_nayar_crf.hpp" 33 #ifndef PIC_DISABLE_EIGEN 34 #ifndef PIC_EIGEN_NOT_BUNDLED 35 #include "../externals/Eigen/SVD" 55 float *
gsolve(
int *samples, std::vector< float > &log_exposure,
float lambda,
int nSamples)
57 #ifndef PIC_DISABLE_EIGEN 59 int nExposure = int(log_exposure.size());
62 int rows = nSamples * nExposure + n + 1;
63 int cols = n + nSamples;
66 printf(
"Matrix size: (%d, %d)\n", rows, cols);
69 Eigen::MatrixXf A = Eigen::MatrixXf::Zero(rows, cols);
70 Eigen::VectorXf b = Eigen::VectorXf::Zero(rows);
74 for(
int i = 0; i < nSamples; i++) {
75 for(
int j = 0; j < nExposure; j++) {
76 int tmp = samples[i * nExposure + j];
80 A.coeffRef(k, tmp) = w_ij;
81 A.coeffRef(k, n + i) = -w_ij;
83 b[k] = w_ij * log_exposure[j];
89 A.coeffRef(k, 128) = 1.0f;
93 for(
int i = 0; i < (n - 2); i++) {
94 float w_l = lambda *
w[i + 1];
95 A.coeffRef(k, i) = w_l;
96 A.coeffRef(k, i + 1) = -2.0f * w_l;
97 A.coeffRef(k, i + 2) = w_l;
102 Eigen::JacobiSVD< Eigen::MatrixXf > svd(A, Eigen::ComputeThinU | Eigen::ComputeThinV);
104 Eigen::VectorXf x = svd.solve(b);
106 float *ret =
new float[n];
108 for(
int i = 0; i < n; i++) {
126 for(
unsigned int i = 0; i <
icrf.size(); i++) {
127 if(
icrf[i] != NULL) {
133 for(
unsigned int i = 0; i <
crf.size(); i++) {
154 for(
unsigned int i = 0; i <
icrf.size(); i++) {
155 if(
icrf[i] != NULL) {
162 for(
unsigned int i = 0; i <
poly.size(); i++) {
163 float *tmp =
new float[256];
165 for(
int j = 0; j < 256; j++) {
166 float x = float(j) / 255.0f;
168 tmp[j] =
poly[i].eval(x);
184 std::vector< Polynomial >
poly;
205 inline float remove(
float x,
int channel)
214 int index =
CLAMP(
int(round(x * 255.0f)), 256);
215 return icrf.at(channel)[index];
220 return powf(x, 2.2f);
225 return poly[channel].eval(x);
242 inline float apply(
float x,
int channel)
251 float *ptr = std::lower_bound(&
icrf[channel][0], &
icrf[channel][255], x);
252 int offset =
CLAMPi((
int)(ptr -
icrf[channel]), 0, 255);
254 return float(offset) / 255.0f;
260 float inv_gamma = 1.0f / 2.2f;
262 constexpr
float inv_gamma = 1.0f / 2.2f;
265 return powf(x, inv_gamma);
270 float *ptr = std::lower_bound(&
icrf[channel][0], &
icrf[channel][255], x);
271 int offset =
CLAMPi((
int)(ptr -
icrf[channel]), 0, 255);
273 return float(offset) / 255.0f;
308 if((img_raw == NULL) || (img_jpg == NULL))
316 int width = img_raw->
width;
317 int height = img_raw->
height;
320 int crf_size = 256 * 256 * channels;
321 unsigned int *
crf =
new unsigned int[crf_size];
323 for(
int i=0;i<crf_size;i++) {
327 for(
int i=0; i<height; i++) {
328 for(
int j=0; j<width; j++) {
330 float *data_raw = (*img_raw)(j, i);
331 float *data_jpg = (*img_jpg)(j, i);
333 for(
int k=0;k<channels;k++) {
334 int i_raw =
CLAMPi(
int(255.0f * data_raw[k]), 0, 255);
335 int i_jpg =
CLAMPi(
int(255.0f * data_jpg[k]), 0, 255);
337 int addr = (i_raw * 256 + i_jpg ) * channels;
345 std::vector< int > coords;
347 for(
int k=0;k<channels;k++) {
349 float *ret_c =
new float[256];
351 for(
int j=0;j<256;j++) {
354 for(
int i=0;i<256;i++) {
356 int addr = (i * 256 + j ) * channels + k;
364 if(!coords.empty()) {
365 std::sort (coords.begin(), coords.end());
366 ret_c[j] = float(coords[coords.size() >> 1]) / 255.0f;
370 if(filteringSize > 0) {
371 Image toBeFiltered(1, 256, 1, 1, ret_c);
378 icrf.push_back(ret_c);
413 int channels = stack[0]->channels;
416 for(
int i = 0; i < 256; i++) {
420 int nExposure = int(stack.size());
423 std::vector< float > log_exposures;
427 printf(
"nSamples: %d\n", nSamples);
430 int stride = nSamples * nExposure;
431 for(
int i = 0; i < channels; i++) {
432 float *icrf_channel =
gsolve(&samples[i * stride], log_exposures, lambda, nSamples);
434 icrf.push_back(icrf_channel);
452 const float alpha = 0.04f,
const bool computeRatios =
false,
const float eps = 0.0001f,
453 const std::size_t max_iterations = 100)
480 int channels = stack[0]->channels;
482 std::size_t nExposures = stack.size();
484 std::vector< float > exposures;
487 int stride = nSamples * int(nExposures);
489 float error = std::numeric_limits<float>::infinity();
490 std::vector<float> R(nExposures - 1);
491 std::vector<std::vector<float>> RR(nExposures - 1, std::vector<float>(nExposures - 1));
493 poly.resize(channels);
495 if (polynomial_degree > 0) {
497 for (
int i = 0; i < channels; ++i) {
498 poly[i].coeff.assign(polynomial_degree + 1, 0.f);
500 error +=
MitsunagaNayarFull(&samples[i * stride], nSamples, exposures,
poly[i].coeff, computeRatios, RR, eps, max_iterations);
502 error +=
MitsunagaNayarClassic(&samples[i * stride], nSamples, exposures,
poly[i].coeff, computeRatios, R, eps, max_iterations);
505 }
else if (polynomial_degree < 0) {
506 error = std::numeric_limits<float>::infinity();
507 std::vector<Polynomial> tmpCoefficients(channels);
508 for (
int degree = 1; degree <= -polynomial_degree; ++degree) {
509 float tmpError = 0.f;
510 for (
int i = 0; i < channels; ++i) {
511 tmpCoefficients[i].coeff.resize(degree + 1);
513 tmpError +=
MitsunagaNayarFull(&samples[i * stride], nSamples, exposures, tmpCoefficients[i].coeff, computeRatios, RR, eps, max_iterations);
515 tmpError +=
MitsunagaNayarClassic(&samples[i * stride], nSamples, exposures, tmpCoefficients[i].coeff, computeRatios, R, eps, max_iterations);
519 if (tmpError < error) {
521 poly = std::move(tmpCoefficients);
522 tmpCoefficients.resize(channels);
527 bool bOk = error < std::numeric_limits<float>::infinity();
552 const int channels = stack[0]->channels;
553 const int pixelcount = stack[0]->nPixels();
556 for (
size_t i=0; i<256; i++) {
563 for (
int m = 0; m < 256; m++) {
564 if (this->w[m] > 0) {
570 for (
int m=255; m>=0; m--) {
571 if (this->w[m] > 0) {
578 int *lower =
new int [stack.size()];
579 int *higher =
new int[stack.size()];
581 for (
size_t i=0; i<stack.size(); i++) {
584 float t = stack[i]->exposure;
585 float tHigh = stack[0]->exposure;
588 for (
size_t j=0; j<stack.size(); j++) {
590 float tj = stack[j]->exposure;
592 if (tj > t && tj < tHigh) {
596 if (tj < t && tj > tLow) {
603 if (lower[i] == -1) {
607 if (higher[i] == -1) {
614 float * lin =
new float[256];
615 for (
int i=0; i<256; i++) {
616 lin[i] = float(2.0 * i / 255.0);
618 this->icrf.push_back(lin);
620 for (
int i=1; i<channels; i++) {
621 float * col =
new float[256];
623 this->icrf.push_back(col);
628 std::vector<unsigned char *> qstack;
629 for (
Image * slice : stack) {
630 assert(slice->frames == 1);
636 for (
int ch=0; ch<channels; ch++) {
637 float * fun = this->icrf[ch];
641 std::vector<float> x(pixelcount);
643 float prevDelta = 0.0f;
644 for (
size_t iter=0; iter<maxIterations; iter++) {
648 size_t minIdx, maxIdx;
649 for (minIdx = 0 ; minIdx < 255 && fun[minIdx]==0 ; minIdx++);
650 for (maxIdx = 255 ; maxIdx > 0 && fun[maxIdx]==0 ; maxIdx--);
652 size_t midIdx = minIdx+(maxIdx-minIdx)/2;
653 float mid = fun[midIdx];
657 while (midIdx < maxIdx && fun[midIdx] == 0.0f) {
669 for (
int i=0; i<pixelcount; i++) {
671 float divisor = 0.0f;
674 float mint = FLT_MAX;
676 int ind = i * channels + ch;
678 for (
size_t s=0; s<qstack.size(); s++) {
679 unsigned char * qslice = qstack[s];
680 const float t = stack[s]->exposure;
686 mint = std::min(mint, t);
690 maxt = std::max(maxt, t);
694 int mLow = qstack[lower [s]][ind];
695 int mHigh = qstack[higher[s]][ind];
696 if (mLow > m || mHigh < m) {
700 const float wm = this->w[m];
702 sum += wm * t * fun[m];
703 divisor += wm * t * t;
706 if (divisor == 0.0f) {
709 x[i] = fun[minM] / maxt;
712 if (mint < FLT_MAX) {
713 x[i] = fun[maxM] / mint;
715 }
else if (divisor < 1e-4f) {
718 x[i] = sum / divisor;
724 size_t cardEm[256] = { 0 };
725 float sum[256] = { 0.0f };
726 float minSatTime = FLT_MAX;
727 for (
size_t s=0; s<qstack.size(); s++) {
728 unsigned char * qslice = qstack[s];
729 const float t = stack[s]->exposure;
731 for (
int i=0; i<pixelcount; i++) {
736 const int m = int(qslice[i*channels+ch]);
739 if (t < minSatTime) {
744 else if (t == minSatTime)
746 sum[m] = std::min(sum[m], t * x[i]);
757 for (
int m=0; m<256; m++) {
758 if (cardEm[m] != 0) {
759 fun[m] = prev = sum[m] / cardEm[m];
768 static const float MaxDelta = 1e-7f;
772 for (
int m=0; m<256; m++) {
773 if( fun[m] != 0.0f ) {
774 float diff = fun[m] - funPrev[m];
775 delta += diff * diff;
782 if (delta < MaxDelta) {
794 for (
int ch=0; ch<channels; ch++) {
799 for (
int ch=0; ch<channels; ch++) {
801 this->icrf[ch][255] = 1.0f;
805 for (
unsigned char * qslice : qstack) {
SubSampleStack stackOut
Definition: camera_response_function.hpp:175
static T getMax(T *data, int size, int &ind)
getMax
Definition: array.hpp:516
std::vector< Polynomial > poly
Definition: camera_response_function.hpp:184
IMG_LIN
Definition: camera_response_function.hpp:43
float * data
data is the main buffer where pixel values are stored.
Definition: image.hpp:91
float w[256]
Definition: camera_response_function.hpp:177
int channels
Definition: image.hpp:80
PIC_INLINE bool ImageVecCheckSimilarType(ImageVec &stack)
ImageVecCheckSimilarType.
Definition: image_vec.hpp:126
std::vector< Image * > ImageVec
ImageVec an std::vector of pic::Image.
Definition: image_vec.hpp:29
Definition: camera_response_function.hpp:43
IMG_LIN type_linearization
Definition: camera_response_function.hpp:176
bool isSimilarType(const Image *img)
isSimilarType checks if the current image is similar to img; i.e. if they have the same width...
CameraResponseFunction()
CameraResponseFunction.
Definition: camera_response_function.hpp:189
void createTabledICRF()
createTabledICRF
Definition: camera_response_function.hpp:148
~CameraResponseFunction()
Definition: camera_response_function.hpp:194
Definition: camera_response_function.hpp:43
Definition: camera_response_function.hpp:43
std::vector< float * > icrf
Definition: camera_response_function.hpp:181
Definition: camera_response_function.hpp:43
void fromRAWJPEG(Image *img_raw, Image *img_jpg, int filteringSize=11)
FromRAWJPEG computes the CRF by exploiting the couple RAW/JPEG from cameras.
Definition: camera_response_function.hpp:306
static T * div(T *buffer, int n, T value)
div divides by a constant value
Definition: buffer.hpp:303
void DebevecMalik(ImageVec stack, CRF_WEIGHT type=CW_DEB97, int nSamples=256, float lambda=20.0f)
DebevecMalik computes the CRF of a camera using multiple exposures value following Debevec and Malik ...
Definition: camera_response_function.hpp:392
PIC_INLINE float MitsunagaNayarFull(int *samples, const std::size_t nSamples, const std::vector< float > &exposures, std::vector< float > &coefficients, bool computeRatios, std::vector< std::vector< float >> &R, const float eps, const std::size_t max_iterations)
MitsunagaNayarFull computes the inverse CRF of a camera as a polynomial function, using all exposure ...
Definition: mitsunaga_nayar_crf.hpp:244
bool MitsunagaNayar(ImageVec &stack, int polynomial_degree=-3, int nSamples=256, const bool full=false, const float alpha=0.04f, const bool computeRatios=false, const float eps=0.0001f, const std::size_t max_iterations=100)
MitsunagaNayar computes the inverse CRF of a camera as a polynomial function.
Definition: camera_response_function.hpp:451
Definition: weight_function.hpp:28
std::vector< float * > crf
Definition: camera_response_function.hpp:182
PIC_INLINE float MitsunagaNayarClassic(int *samples, const std::size_t nSamples, const std::vector< float > &exposures, std::vector< float > &coefficients, const bool computeRatios, std::vector< float > &R, const float eps, const std::size_t max_iterations)
MitsunagaNayarClassic computes the inverse CRF of a camera as a polynomial function.
Definition: mitsunaga_nayar_crf.hpp:52
void release()
release frees memory.
Definition: camera_response_function.hpp:122
The SubSampleStack class.
Definition: sub_sample_stack.hpp:34
Definition: dynamic_range.hpp:29
The CameraResponseFunction class.
Definition: camera_response_function.hpp:48
Definition: weight_function.hpp:28
PIC_INLINE void ImaveVecSortByExposureTime(ImageVec &stack)
ImaveVecSortByExposureTime.
Definition: image_vec.hpp:96
PIC_INLINE float weightFunction(float x, CRF_WEIGHT type)
weightFunction computes weight functions for x in [0,1].
Definition: weight_function.hpp:36
The Image class stores an image as buffer of float.
Definition: image.hpp:60
int * get()
get
Definition: sub_sample_stack.hpp:232
PIC_INLINE void ImaveVecGetExposureTimesAsArray(ImageVec &stack, std::vector< float > &output, bool bLog)
ImaveVecGetExposureTimesAsArray.
Definition: image_vec.hpp:111
void execute(ImageVec &stack, int nSamples, float alpha=0.f, bool bSpatial=false, SAMPLER_TYPE sub_type=ST_MONTECARLO_S)
execute
Definition: sub_sample_stack.hpp:190
#define CLAMPi(x, a, b)
Definition: math.hpp:81
void setCRFtoGamma2_2()
setCRFtoGamma2_2
Definition: camera_response_function.hpp:287
Definition: bilateral_separation.hpp:25
#define CLAMP(x, a)
Definition: math.hpp:77
static Image * execute(Image *imgIn, Image *imgOut, int size)
execute
Definition: filter_mean.hpp:89
void release()
release
Definition: sub_sample_stack.hpp:173
float * gsolve(int *samples, std::vector< float > &log_exposure, float lambda, int nSamples)
gsolve computes the inverse CRF of a camera.
Definition: camera_response_function.hpp:55
int width
Definition: image.hpp:80
int height
Definition: image.hpp:80
PIC_INLINE unsigned char * convertHDR2LDR(const float *dataIn, unsigned char *dataOut, int size, LDR_type type, float gamma=2.2f)
convertHDR2LDR converts a buffer of float into unsigned char.
Definition: dynamic_range.hpp:132
void setCRFtoLinear()
setCRFtoLinear
Definition: camera_response_function.hpp:295
static T * assign(T *buffer, int n, T value)
assign assigns value to buffer
Definition: buffer.hpp:45
CRF_WEIGHT
The CRF_WEIGHT enum.
Definition: weight_function.hpp:28
int getNSamples() const
getNSamples
Definition: sub_sample_stack.hpp:241
void Robertson(ImageVec &stack, const size_t maxIterations=50)
Robertson computes the CRF of a camera using all multiple exposures value Robertson et al 1999's meth...
Definition: camera_response_function.hpp:542
float apply(float x, int channel)
apply
Definition: camera_response_function.hpp:242