1 #include <math.h>
2 #include <algorithm>
3 #include <iostream>
4
5 #include <config.h>
6
7 #include "compression_tmo.h"
8
9
10 /**
11 * Lookup table on a uniform array & interpolation
12 *
13 * x_i must be at least two elements
14 * y_i must be initialized after creating an object
15 */
16 class UniformArrayLUT
17 {
18 double start_v;
19 size_t lut_size;
20 double delta;
21
22 bool own_y_i;
23 public:
24 double *y_i;
25
UniformArrayLUT(double from,double to,int lut_size,double * y_i=NULL)26 UniformArrayLUT( double from, double to, int lut_size, double *y_i = NULL ) :
27 start_v(from), lut_size( lut_size ), delta( (to-from)/(double)lut_size )
28 {
29 if( y_i == NULL ) {
30 this->y_i = new double[lut_size];
31 own_y_i = true;
32 } else {
33 this->y_i = y_i;
34 own_y_i = false;
35 }
36 }
37
UniformArrayLUT()38 UniformArrayLUT() : y_i(NULL), lut_size( 0 ), delta( 0. ), own_y_i( false ) {}
39
UniformArrayLUT(const UniformArrayLUT & other)40 UniformArrayLUT(const UniformArrayLUT& other) : start_v( other.start_v ), lut_size( other.lut_size ), delta( other.delta )
41 {
42 this->y_i = new double[lut_size];
43 own_y_i = true;
44 memcpy(this->y_i, other.y_i, lut_size * sizeof(double));
45 }
46
operator =(const UniformArrayLUT & other)47 UniformArrayLUT& operator = (const UniformArrayLUT& other)
48 {
49 this->lut_size = other.lut_size;
50 this->delta = other.delta;
51 this->start_v = other.start_v;
52 this->y_i = new double[lut_size];
53 own_y_i = true;
54 memcpy(this->y_i, other.y_i, lut_size * sizeof(double));
55 return *this;
56 }
57
~UniformArrayLUT()58 ~UniformArrayLUT()
59 {
60 if( own_y_i )
61 delete []y_i;
62 }
63
interp(double x)64 double interp( double x )
65 {
66 const double ind_f = (x - start_v)/delta;
67 const size_t ind_low = (size_t)(ind_f);
68 const size_t ind_hi = (size_t)ceil(ind_f);
69
70 if( unlikely(ind_f < 0) ) // Out of range checks
71 return y_i[0];
72 if( unlikely(ind_hi >= lut_size) )
73 return y_i[lut_size-1];
74
75 if( unlikely(ind_low == ind_hi) )
76 return y_i[ind_low]; // No interpolation necessary
77
78 return y_i[ind_low] + (y_i[ind_hi]-y_i[ind_low])*(ind_f-(double)ind_low); // Interpolation
79 }
80
81 };
82
83
84 class ImgHistogram
85 {
86 public:
87 const float L_min, L_max;
88 const float delta;
89 int *bins;
90 double *p;
91 int bin_count;
92
ImgHistogram()93 ImgHistogram() : L_min( -6.f ), L_max( 9.f ), delta( 0.1 ), bins( NULL ), p( NULL )
94 {
95 bin_count = (int)ceil((L_max-L_min)/delta);
96 bins = new int[bin_count];
97 p = new double[bin_count];
98 }
99
~ImgHistogram()100 ~ImgHistogram()
101 {
102 delete [] bins;
103 delete [] p;
104 }
105
compute(const float * img,size_t pixel_count)106 void compute( const float *img, size_t pixel_count )
107 {
108
109 std::fill( bins, bins + bin_count, 0 );
110
111 int pp_count = 0;
112 for( size_t pp = 0; pp < pixel_count; pp++ )
113 {
114 int bin_index = (img[pp]-L_min)/delta;
115 // ignore anything outside the range
116 if( bin_index < 0 || bin_index >= bin_count )
117 continue;
118 bins[bin_index]++;
119 pp_count++;
120 }
121
122 for( int bb = 0; bb < bin_count; bb++ ) {
123 p[bb] = (double)bins[bb] / (double)pp_count;
124 }
125 }
126
127
128 };
129
safelog10f(float x)130 inline float safelog10f( float x )
131 {
132 if( unlikely(x < 1e-5f) )
133 return -5.f;
134 return log10f( x );
135 }
136
137
138
tonemap(const float * R_in,const float * G_in,float * B_in,int width,int height,float * R_out,float * G_out,float * B_out,const float * L_in,pfstmo_progress_callback progress_cb)139 void CompressionTMO::tonemap( const float *R_in, const float *G_in, float *B_in, int width, int height,
140 float *R_out, float *G_out, float *B_out, const float *L_in,
141 pfstmo_progress_callback progress_cb )
142 {
143
144 const size_t pix_count = width*height;
145
146 // Compute log of Luminance
147 float *logL = new float[pix_count];
148 // std::unique_ptr<float[]> logL(new float[pix_count]);
149 for( size_t pp = 0; pp < pix_count; pp++ ) {
150 logL[pp] = safelog10f( L_in[pp] );
151 }
152
153 ImgHistogram H;
154 H.compute(logL, pix_count );
155
156 //Compute slopes
157 // std::unique_ptr<double[]> s(new double[H.bin_count]);
158 double *s = new double[H.bin_count];
159 {
160 double d = 0;
161 for( int bb = 0; bb < H.bin_count; bb++ ) {
162 d += pow( H.p[bb], 1./3. );
163 }
164 d *= H.delta;
165 for( int bb = 0; bb < H.bin_count; bb++ ) {
166 s[bb] = pow( H.p[bb], 1./3. )/d;
167 }
168
169 }
170
171 #if 0
172 // TODO: Handling of degenerated cases, e.g. when an image contains uniform color
173 const double s_max = 2.; // Maximum slope, to avoid enhancing noise
174 double s_renorm = 1;
175 for( int bb = 0; bb < H.bin_count; bb++ ) {
176 if( s[bb] >= s_max ) {
177 s[bb] = s_max;
178 s_renorm -= s_max * H.delta;
179 }
180 }
181 for( int bb = 0; bb < H.bin_count; bb++ ) {
182 if( s[bb] < s_max ) {
183 s[bb] = s_max;
184 s_renorm -= s_max * H.delta;
185 }
186
187 }
188
189 #endif
190 progress_cb( 50 );
191
192 //Create a tone-curve
193 UniformArrayLUT lut( H.L_min, H.L_max, H.bin_count );
194 lut.y_i[0] = 0;
195 for( int bb = 1; bb < H.bin_count; bb++ ) {
196 lut.y_i[bb] = lut.y_i[bb-1] + s[bb] * H.delta;
197 }
198
199 // Apply the tone-curve
200 for( int pp = 0; pp < pix_count; pp++ ) {
201 R_out[pp] = lut.interp( safelog10f(R_in[pp]) );
202 G_out[pp] = lut.interp( safelog10f(G_in[pp]) );
203 B_out[pp] = lut.interp( safelog10f(B_in[pp]) );
204 }
205
206 delete [] s;
207 delete [] logL;
208
209 }
210