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