1 /**
2 * @brief Create a web page with an HDR viewer
3 *
4 * This file is a part of PFSTOOLS package.
5 * ----------------------------------------------------------------------
6 * Copyright (C) 2009 Rafal Mantiuk
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * ----------------------------------------------------------------------
22 *
23 * @author Rafal Mantiuk, <mantiuk@mpi-sb.mpg.de>
24 *
25 * $Id: hdrhtml.cpp,v 1.8 2014/06/16 21:50:08 rafm Exp $
26 */
27
28 #include "hdrhtml.h"
29
30 #include <limits>
31 #include <algorithm>
32 #include <math.h>
33 #include <fstream>
34 #include <sstream>
35 #include <iostream>
36 #include <limits>
37
38 #include <pfs.h>
39
40 #include <Magick++.h>
41 // This is to get rid of warnings due to double defines from Magick++
42 #undef PACKAGE
43 #undef PACKAGE_BUGREPORT
44 #undef PACKAGE_NAME
45 #undef PACKAGE_STRING
46 #undef PACKAGE_TARNAME
47 #undef PACKAGE_VERSION
48 #undef VERSION
49
50 #include <config.h>
51
52 using namespace std;
53
54 // ================================================
55 // Parameters controllig the web page
56 // ================================================
57
58 const int f_step_res = 3; // How many steps per f-stop (do not change)
59 const int pix_per_fstop = 25; // Distance in pixels between f-stops shown on the histogram
60 const char *hdrhtml_version = "1.0"; // Version of the HDRHTML code
61
62
63 // ================================================
64 // Histogram
65 // ================================================
66
67 template<class T>
68 class Histogram
69 {
70 public:
71 T *x; // Bin centers
72 size_t *n; // No of items in a bin
73 size_t bins;
74
Histogram()75 Histogram() : x( NULL ), n( NULL )
76 {
77 }
78
~Histogram()79 ~Histogram()
80 {
81 free();
82 }
83
free()84 void free()
85 {
86 delete []x;
87 delete []n;
88 bins = 0;
89 }
90
compute(const T * data,size_t d_size,int bins=30,T min_val=1,T max_val=-1,bool reject_outofrange=true)91 void compute( const T *data, size_t d_size, int bins = 30, T min_val = 1, T max_val = -1, bool reject_outofrange = true )
92 {
93 assert( bins > 0 );
94
95 free();
96 this->bins = bins;
97
98 if( min_val > max_val ) // missing min/max info
99 {
100 min_val = numeric_limits<T>::max();
101 max_val = numeric_limits<T>::min();
102
103 for( int k=0; k < d_size; k++ ) {
104 if( data[k] > max_val ) max_val = data[k];
105 if( data[k] < min_val ) min_val = data[k];
106 }
107 }
108
109 x = new T[bins];
110 n = new size_t[bins];
111
112 T delta = (max_val-min_val) / (float)bins; // width of a single bin
113
114 // T *e = new T[bins+1]; // bin edges
115 // for( int k=0; k <= bins; k++ ) {
116 // e[k] = min_val + (float)k * delta;
117 // }
118 for( int k=0; k < bins; k++ ) {
119 x[k] = min_val + (float)k * delta + delta/2;
120 n[k] = 0;
121 }
122
123 if( reject_outofrange ) {
124 for( int k=0; k < d_size; k++ ) {
125 int ind = floor( (data[k]-min_val) / (max_val-min_val) * (float)bins );
126 if( ind < 0 )
127 continue;
128 if( ind >= bins )
129 continue;
130 n[ind]++;
131 }
132 } else {
133 for( int k=0; k < d_size; k++ ) {
134 int ind = floor( (data[k]-min_val) / (max_val-min_val) * (float)bins );
135 if( ind < 0 ) {
136 n[0]++;
137 continue;
138 }
139 if( ind >= bins ) {
140 n[bins-1]++;
141 continue;
142 }
143 n[ind]++;
144 }
145 }
146
147
148
149 }
150
151 };
152
153 // ================================================
154 // Lookup table
155 // ================================================
156
157 /**
158 * Lookup table on a uniform array & interpolation
159 *
160 * x_i must be at least two elements
161 * y_i must be initialized after creating an object
162 */
163 class UniformArrayLUT
164 {
165 const float *x_i;
166 size_t lut_size;
167 float delta;
168
169 bool own_y_i;
170 public:
171 float *y_i;
172
UniformArrayLUT(size_t lut_size,const float * x_i,float * y_i=NULL)173 UniformArrayLUT( size_t lut_size, const float *x_i, float *y_i = NULL ) : x_i( x_i ), lut_size( lut_size ), delta( x_i[1]-x_i[0] )
174 {
175 if( y_i == NULL ) {
176 this->y_i = new float[lut_size];
177 own_y_i = true;
178 } else {
179 this->y_i = y_i;
180 own_y_i = false;
181 }
182 }
183
UniformArrayLUT()184 UniformArrayLUT() : x_i( 0 ), y_i(0), lut_size( 0 ), delta( 0. ) {}
185
UniformArrayLUT(const UniformArrayLUT & other)186 UniformArrayLUT(const UniformArrayLUT& other) : x_i( other.x_i ), lut_size( other.lut_size ), delta( other.delta )
187 {
188 this->y_i = new float[lut_size];
189 own_y_i = true;
190 memcpy(this->y_i, other.y_i, lut_size * sizeof(float));
191 }
192
operator =(const UniformArrayLUT & other)193 UniformArrayLUT& operator = (const UniformArrayLUT& other)
194 {
195 this->lut_size = other.lut_size;
196 this->delta = other.delta;
197 this->x_i = other.x_i;
198 this->y_i = new float[lut_size];
199 own_y_i = true;
200 memcpy(this->y_i, other.y_i, lut_size * sizeof(float));
201 return *this;
202 }
203
~UniformArrayLUT()204 ~UniformArrayLUT()
205 {
206 if( own_y_i )
207 delete []y_i;
208 }
209
interp(float x)210 float interp( float x )
211 {
212 const float ind_f = (x - x_i[0])/delta;
213 const size_t ind_low = (size_t)(ind_f);
214 const size_t ind_hi = (size_t)ceil(ind_f);
215
216 if( (ind_f < 0) ) // Out of range checks
217 return y_i[0];
218 if( (ind_hi >= lut_size) )
219 return y_i[lut_size-1];
220
221 if( (ind_low == ind_hi) )
222 return y_i[ind_low]; // No interpolation necessary
223
224 return y_i[ind_low] + (y_i[ind_hi]-y_i[ind_low])*(ind_f-(float)ind_low); // Interpolation
225 }
226
227 };
228
229 template<class T>
clamp(T x,T min,T max)230 inline T clamp( T x, T min, T max )
231 {
232 if( x < min )
233 return min;
234 if( x > max )
235 return max;
236 return x;
237 }
238
239 /**
240 * Lookup table on an arbitrary array
241 *
242 * x_i must be at least two elements
243 * y_i must be initialized after creating an object
244 */
245 template<class Tx, class Ty>
246 class ArrayLUT
247 {
248 const Tx *x_i;
249 size_t lut_size;
250
251 bool own_y_i;
252 public:
253 Ty *y_i;
254
ArrayLUT(size_t lut_size,const Tx * x_i,Ty * y_i=NULL)255 ArrayLUT( size_t lut_size, const Tx *x_i, Ty *y_i = NULL ) : x_i( x_i ), lut_size( lut_size )
256 {
257 assert( lut_size > 0 );
258
259 if( y_i == NULL ) {
260 this->y_i = new Ty[lut_size];
261 own_y_i = true;
262 } else {
263 this->y_i = y_i;
264 own_y_i = false;
265 }
266 }
267
ArrayLUT()268 ArrayLUT() : x_i( 0 ), y_i(0), lut_size( 0 ) {}
269
ArrayLUT(const ArrayLUT & other)270 ArrayLUT(const ArrayLUT& other) : x_i( other.x_i ), lut_size( other.lut_size )
271 {
272 this->y_i = new Ty[lut_size];
273 own_y_i = true;
274 memcpy(this->y_i, other.y_i, lut_size * sizeof(Ty));
275 }
276
operator =(const ArrayLUT & other)277 ArrayLUT& operator = (const ArrayLUT& other)
278 {
279 this->lut_size = other.lut_size;
280 this->x_i = other.x_i;
281 this->y_i = new Ty[lut_size];
282 own_y_i = true;
283 memcpy(this->y_i, other.y_i, lut_size * sizeof(Ty));
284 }
285
~ArrayLUT()286 ~ArrayLUT()
287 {
288 if( own_y_i )
289 delete []y_i;
290 }
291
interp(Tx x)292 Ty interp( Tx x )
293 {
294 if( (x <= x_i[0]) ) // Out of range checks
295 return y_i[0];
296 if( (x >= x_i[lut_size-1]) )
297 return y_i[lut_size-1];
298
299 // binary search
300 size_t l = 0, r = lut_size-1;
301 while( true ) {
302 size_t m = (l+r)/2;
303 if( m == l ) break;
304 if( x < x_i[m] )
305 r = m;
306 else
307 l = m;
308 }
309
310 float alpha = (x - x_i[l])/(x_i[r]-x_i[l]);
311
312 return y_i[l] + (Ty)(alpha * (y_i[r]-y_i[l]));
313 }
314
315
316 };
317
318
319 // ================================================
320 // Percentiles
321 // ================================================
322
323
324 /**
325 * Compute prctiles using image cummulative histogram, which is less
326 * accurate method but much faster than sorting.
327 */
328 template<class T>
329 class Percentiles
330 {
331 Histogram<T> hist;
332 const size_t bin_n;
333 size_t d_size;
334 public:
335
336 /**
337 * @param data - table with samples
338 * @param d_size - number of samples
339 */
Percentiles(const T * data,size_t d_size)340 Percentiles( const T *data, size_t d_size ) :
341 bin_n( 1000 ), d_size( d_size ) // Accuracy 0.1 prctile
342 {
343 hist.compute( data, d_size, bin_n, 1, -1, false );
344 // Compute cummulative histogram
345 for( int k = 1; k < bin_n; k++ )
346 hist.n[k] += hist.n[k-1];
347
348 // cerr << "d_size: " << d_size << " hist.n: " << hist.n[bin_n-1] << "\n";
349 assert( hist.n[bin_n-1] == d_size );
350 }
351
prctile(double p)352 T prctile( double p )
353 {
354 ArrayLUT<size_t,T> lut( hist.bins, hist.n, hist.x );
355
356 return lut.interp( (size_t)(p*(double)d_size/100.) );
357 }
358
359
360
361 };
362
363
364 // ================================================
365 // Text template file utils
366 // ================================================
367
368 typedef void (*replace_callback)( ostream &out, void *user_data, const char *parameter );
369
370 class ReplacePattern
371 {
372
373 public:
374
375 const char* pattern;
376 std::string replace_with;
377 replace_callback callback;
378 void *user_data;
379
ReplacePattern(const char * pattern,std::string replace_with)380 ReplacePattern( const char* pattern, std::string replace_with ) :
381 pattern( pattern ), replace_with( replace_with ), callback( NULL )
382 {
383 }
384
ReplacePattern(const char * pattern,float replace_with_num)385 ReplacePattern( const char* pattern, float replace_with_num ) :
386 pattern( pattern ), callback( NULL )
387 {
388 std::ostringstream num_str;
389 num_str << replace_with_num;
390 replace_with = num_str.str();
391 }
392
ReplacePattern(const char * pattern,int replace_with_num)393 ReplacePattern( const char* pattern, int replace_with_num ) :
394 pattern( pattern ), callback( NULL )
395 {
396 std::ostringstream num_str;
397 num_str << replace_with_num;
398 replace_with = num_str.str();
399 }
400
ReplacePattern(const char * pattern,replace_callback callback,void * user_data=NULL)401 ReplacePattern( const char* pattern, replace_callback callback, void *user_data = NULL ) :
402 pattern( pattern ), callback( callback ), user_data( user_data )
403 {
404 }
405
ReplacePattern()406 ReplacePattern() : pattern( NULL ), callback( NULL )
407 {
408 }
409
write_replacement(ostream & out,const char * parameter=NULL)410 virtual void write_replacement( ostream &out, const char *parameter = NULL )
411 {
412 if( callback != NULL )
413 callback( out, user_data, parameter );
414 else
415 out << replace_with;
416 }
417
418
419 };
420
421
create_from_template(std::ostream & outfs,const char * template_file_name,ReplacePattern * pattern_list)422 void create_from_template( std::ostream &outfs, const char *template_file_name,
423 ReplacePattern *pattern_list )
424 {
425 std::ifstream infs( template_file_name );
426 if( !infs.good() ) {
427 std::ostringstream error_message;
428 error_message << "Cannot open '" << template_file_name << "' for reading";
429 throw pfs::Exception( error_message.str().c_str() );
430 }
431
432
433 const int MAX_LINE_LENGTH = 2048;
434 // int lines = 0;
435 while( true ) {
436 char line[MAX_LINE_LENGTH];
437 infs.getline( line, MAX_LINE_LENGTH );
438
439 if( !infs.good() )
440 break;
441
442 std::string line_str( line );
443 int pos = 0;
444
445 while( true ) {
446 int find_pos = line_str.find_first_of( '@', pos );
447 if( find_pos == std::string::npos ) {
448 outfs << line_str.substr( pos, std::string::npos );
449 break;
450 }
451
452 bool replaced = false;
453 int end_marker = line_str.find_first_of( "@[", find_pos+1 );
454 if( end_marker != std::string::npos ) {
455
456 for( int k = 0; pattern_list[k].pattern != NULL; k++ )
457 {
458 if( line_str.compare( find_pos+1, end_marker-find_pos-1, pattern_list[k].pattern ) == 0 ) {
459 outfs << line_str.substr( pos, find_pos-pos );
460
461 std::string parameter;
462 if( line_str[end_marker] == '[' ) {
463 int param_endmarker = line_str.find_first_of( ']', end_marker+1 );
464 if( param_endmarker == std::string::npos )
465 throw pfs::Exception( "Non-closed bracker in the replacement keyword" );
466 parameter = line_str.substr( end_marker+1, param_endmarker-end_marker-1 );
467 end_marker = param_endmarker+1;
468 }
469
470 pattern_list[k].write_replacement( outfs, parameter.empty() ? NULL : parameter.c_str() );
471 pos = end_marker + 1;
472 replaced = true;
473 break;
474 }
475 }
476
477 }
478 if( !replaced ) {
479 outfs << line_str.substr( pos, find_pos-pos+1 );
480 pos = find_pos+1;
481 }
482
483 }
484
485
486 outfs << "\n";
487
488 }
489
490 }
491
create_from_template(const char * output_file_name,const char * template_file_name,ReplacePattern * pattern_list)492 void create_from_template( const char *output_file_name, const char *template_file_name,
493 ReplacePattern *pattern_list )
494 {
495 std::ofstream outfs( output_file_name );
496 if( !outfs.good() ) {
497 std::ostringstream error_message;
498 error_message << "Cannot open '" << output_file_name << "' for writing";
499 throw pfs::Exception( error_message.str().c_str() );
500 }
501 create_from_template( outfs, template_file_name, pattern_list );
502 }
503
504
505
506
507 // ================================================
508 // Read and parse CVS files
509 // ================================================
510
511
512 class CSVTable
513 {
514 public:
515
516 float **data;
517 int columns, rows;
518
CSVTable()519 CSVTable() : data( NULL )
520 {
521 }
522
523
~CSVTable()524 ~CSVTable()
525 {
526 free();
527 }
528
free()529 void free()
530 {
531 if( data == NULL )
532 return;
533
534 for( int k = 0; k < columns; k++ )
535 delete [] data[k];
536
537 delete []data;
538
539 data = NULL;
540 }
541
542
read(const char * file_name,int columns)543 void read( const char *file_name, int columns )
544 {
545 free();
546
547 this->columns = columns;
548
549 std::ifstream ifs( file_name );
550
551 if( !ifs.is_open() ) {
552 std::string full_message( "Cannot open file: " );
553 full_message += file_name;
554 throw pfs::Exception( full_message.c_str() );
555 }
556
557 std::list<float> value_list;
558
559 const int MAX_LINE_LENGTH = 1024;
560 int lines = 0;
561 while( 1 ) {
562 char line[MAX_LINE_LENGTH];
563 ifs.getline( line, MAX_LINE_LENGTH );
564
565 if( !ifs.good() )
566 break;
567
568 std::string line_str( line );
569 int pos = 0;
570 for( int k=0; k < columns; k++ ) {
571 // Skip white spaces
572 while( line_str[pos] == ' ' || line_str[pos] == '\t' ) pos++;
573 int new_pos = line_str.find_first_of( ',', pos );
574 size_t len;
575 if( new_pos == std::string::npos ) {
576 if( k != columns-1 ) {
577 std::string full_message( "Missing column data in the file: " );
578 full_message += file_name;
579 throw pfs::Exception( full_message.c_str() );
580 }
581 len = std::string::npos;
582 } else
583 len = new_pos-pos;
584
585 float value;
586 if( len == 0 ) {
587 value = numeric_limits<float>::quiet_NaN();
588 } else {
589 std::string token = line_str.substr( pos, len );
590 const char *str_beg = token.c_str();
591 char *str_end;
592 // cerr << "token: " << str_beg << "\n";
593 value = strtof( str_beg, &str_end );
594 if( str_beg == str_end ) {
595 std::ostringstream error_message;
596 error_message << "Error parsing line " << lines+1 << " of " << file_name << "\n";
597 throw pfs::Exception( error_message.str().c_str() );
598 }
599 }
600
601
602 value_list.push_back( value );
603
604 pos = new_pos+1;
605 }
606
607 lines++;
608 }
609
610 float **table = new float*[columns];
611 for( int c=0; c < columns; c++ )
612 table[c] = new float[lines];
613
614 for( int l=0; l < lines; l++ )
615 for( int c=0; c < columns; c++ ) {
616 table[c][l] = value_list.front();
617 value_list.pop_front();
618 }
619
620 data = table;
621 this->rows = lines;
622 }
623
624 };
625
626
627 // ================================================
628 // HDR HTML code
629 // ================================================
630
631
add_image(int width,int height,float * R,float * G,float * B,float * Y,const char * base_name,int quality)632 void HDRHTMLSet::add_image( int width, int height, float *R, float *G, float *B,
633 float *Y,
634 const char *base_name, int quality )
635 {
636
637 const int pixels = width*height;
638 const int basis_no = quality;
639
640 // Load LUT for the basis tone-curves
641 std::ostringstream lut_filename;
642 lut_filename << PKG_DATADIR "/hdrhtml_t_b" << basis_no+1 << ".csv";
643 CSVTable basis_table;
644 basis_table.read( lut_filename.str().c_str(), basis_no+1 );
645 // Transform the first row (luminance factors) to the log domain
646 for( int k = 0; k < basis_table.rows; k++ )
647 basis_table.data[0][k] = log2f( basis_table.data[0][k] );
648
649 // Fix zero and negative values in the image, convert to log2 space, find min and max values
650 float img_min = numeric_limits<float>::max();
651 float img_max = numeric_limits<float>::min();
652 {
653 float *arrays[] = { R, G, B, Y };
654 int k;
655
656 for( k = 0; k < 4; k++ ) {
657 float *x = arrays[k];
658 float min_val = numeric_limits<float>::max(), max_val = numeric_limits<float>::min();
659 for( int i=0; i < pixels; i++ ) {
660 if( x[i] < min_val && x[i] > 0)
661 min_val = x[i];
662 if( x[i] > max_val )
663 max_val = x[i];
664 }
665 img_max = std::max( img_max, log2f(max_val) );
666 img_min = std::min( img_min, log2f(min_val) );
667
668 for( int i=0; i < pixels; i++ ) {
669 if( x[i] < min_val )
670 x[i] = log2f(min_val);
671 else
672 x[i] = log2f(x[i]);
673 }
674 }
675 }
676
677
678 Percentiles<float> prc( Y, pixels );
679 img_min = prc.prctile( 0.1 );
680 img_max = prc.prctile( 99.9 );
681
682 img_min -= 4; // give extra room for brightenning
683 // how many 8-fstop segments we need to cover the DR
684 int f8_stops = ceil((img_max-img_min)/8);
685
686 // start with this f-stop
687 float l_start = img_min + (img_max-img_min-f8_stops*8)/2;
688
689 float l_med = prc.prctile( 50 );
690 float best_exp = round(l_med-l_start-4);
691
692 // pix_per_fstop = 25;
693
694 // % generate image histogram
695
696 const int hist_height = 36;
697 int hist_width = width;
698 // float hist_img_sz = [36 size(img,2)];
699 float hist_fstops = hist_width / pix_per_fstop;
700 float hist_start = (img_max-img_min-hist_fstops)/2;
701 {
702
703 Histogram<float> hist;
704 hist.compute( Y, pixels, hist_width, img_min+hist_start, img_min+hist_start+hist_fstops );
705
706 unsigned short *hist_buffer = new unsigned short[hist_width*hist_height*3];
707 float hist_n_max = -1;
708 for( int k = 0; k < hist_width; k++ )
709 hist_n_max = std::max( hist_n_max, (float)hist.n[k] );
710
711 for( int k = 0; k < hist_width; k++ ) {
712 float top = hist_height - round((float)hist.n[k]/hist_n_max * hist_height);
713 for( int r = 0; r < hist_height; r++ ) {
714 hist_buffer[(r*hist_width+k)*3+0] = 0;
715 hist_buffer[(r*hist_width+k)*3+1] = (r>=top ? (1<<16) -1 : 0);
716 hist_buffer[(r*hist_width+k)*3+2] = 0;
717 }
718 }
719
720
721 // tick_fstops = (floor(hist_l(end))-ceil(hist_l(1)));
722 // ticks = round((ceil(hist_l(1))-hist_l(1))*pix_per_fstop) + (1:tick_fstops)*pix_per_fstop;
723 // hist_img(1:5,ticks,1:2) = 0.5;
724 // hist_img(end-4:end,ticks,1:2) = 0.5;
725 // plot_name = sprintf( '%s_hist.png', base_name );
726 // imwrite( hist_img, plot_name );
727
728 Magick::Image hist_image( hist_width, hist_height,
729 "RGB", Magick::ShortPixel, hist_buffer );
730
731 std::ostringstream img_filename;
732 if( image_dir != NULL )
733 img_filename << image_dir << "/";
734 img_filename << base_name << "_hist.png";
735 std::cerr << "Writing: " << img_filename.str() << "\n";
736 hist_image.write( img_filename.str().c_str() );
737
738 delete []hist_buffer;
739 }
740
741 // generate basis images
742
743 unsigned short *imgBuffer =
744 new unsigned short[pixels*3];
745 for( int k=1; k <= f8_stops+1; k++ ) {
746
747
748 float max_value = (float)numeric_limits<unsigned short>::max(); //(1<<16) -1;
749
750 float exp_multip = log2f(1/powf( 2, l_start + k*8 ));
751
752 int max_basis = basis_no;
753 if( k == f8_stops+1 ) // Do only one shared basis for the last 8-fstop segment
754 max_basis = 1;
755
756 for( int b=0; b < max_basis; b++ ) {
757 UniformArrayLUT basis_lut( basis_table.rows, basis_table.data[0], basis_table.data[b+1] );
758
759 int i = 0;
760 for( int pix = 0; pix < pixels; pix++ ) {
761
762 float rgb[3];
763 rgb[0] = R[pix];
764 rgb[1] = G[pix];
765 rgb[2] = B[pix];
766
767 for( int c=0; c < 3; c++ ) {
768 float exposure_comp_v = rgb[c] + exp_multip;
769 float v = (basis_lut.interp(exposure_comp_v)*max_value);
770 imgBuffer[i++] = (unsigned short)(basis_lut.interp(exposure_comp_v)*max_value);
771 }
772 }
773 Magick::Image imImage( width, height,
774 "RGB", Magick::ShortPixel, imgBuffer );
775
776 std::ostringstream img_filename;
777 if( image_dir != NULL )
778 img_filename << image_dir << "/";
779 img_filename << base_name << '_' << k-1 << '_' << b+1 << ".jpg";
780 std::cerr << "Writing: " << img_filename.str() << "\n";
781 imImage.write( img_filename.str().c_str() );
782 }
783
784 }
785 delete [] imgBuffer;
786
787 HDRHTMLImage new_image( base_name, width, height );
788
789 new_image.hist_width = hist_width;
790 new_image.f8_stops = f8_stops;
791 new_image.f_step_res = f_step_res;
792 new_image.basis = basis_no;
793 new_image.shared_basis = 1;
794 new_image.pix_per_fstop = pix_per_fstop;
795 new_image.hist_start = hist_start;
796 new_image.hist_width = hist_width;
797 new_image.best_exp = best_exp;
798
799 image_list.push_back( new_image );
800
801 }
802
803 void print_image_objects( ostream &out, void *user_data, const char *parameter );
804 void print_cf_table( ostream &out, void *user_data, const char *parameter );
805 void print_image_htmlcode( ostream &out, void *user_data, const char *parameter );
806
generate_webpage(const char * page_template,const char * image_template,const char * object_output,const char * html_output)807 void HDRHTMLSet::generate_webpage( const char *page_template, const char *image_template,
808 const char *object_output, const char *html_output)
809 {
810 if( image_list.empty() )
811 return;
812
813 std::ostringstream out_file_name;
814 if( page_name == NULL )
815 out_file_name << image_list.front().base_name << ".html";
816 else
817 out_file_name << page_name;
818
819 // Load the table of the opacity coeffcients
820 std::ostringstream lut_filename;
821 lut_filename << PKG_DATADIR "/hdrhtml_c_b" << image_list.front().basis+1 << ".csv";
822 CSVTable coeff_table;
823 coeff_table.read( lut_filename.str().c_str(), image_list.front().basis+1 );
824
825 ReplacePattern replace_list[] = {
826 ReplacePattern( "cf_array_def", print_cf_table, &coeff_table ),
827 ReplacePattern( "hdr_img_def", print_image_objects, this ),
828 ReplacePattern( "image_htmlcode", print_image_htmlcode, this ),
829 ReplacePattern( "title", page_name == NULL ? "HDRHTML viewer" : page_name ),
830 ReplacePattern( "version", hdrhtml_version ),
831 ReplacePattern()
832 };
833
834 this->image_template = image_template;
835 create_from_template( out_file_name.str().c_str(), page_template, replace_list );
836
837 if( object_output != NULL ) {
838 std::ofstream oofs( object_output );
839 if( !oofs.good() ) {
840 std::ostringstream error_message;
841 error_message << "Cannot open '" << object_output << "' for writing";
842 throw pfs::Exception( error_message.str().c_str() );
843 }
844 print_image_objects( oofs, this, NULL );
845 }
846
847 if( html_output != NULL ) {
848 std::ofstream hofs( html_output );
849 if( !hofs.good() ) {
850 std::ostringstream error_message;
851 error_message << "Cannot open '" << html_output << "' for writing";
852 throw pfs::Exception( error_message.str().c_str() );
853 }
854 print_image_htmlcode( hofs, this, NULL );
855 }
856
857 }
858
print_image_objects(ostream & out,void * user_data,const char * parameter)859 void print_image_objects( ostream &out, void *user_data, const char *parameter )
860 {
861 HDRHTMLSet *hdrhtml_set = (HDRHTMLSet*)user_data;
862
863 list<HDRHTMLImage>::iterator it;
864 for( it = hdrhtml_set->image_list.begin(); it != hdrhtml_set->image_list.end(); it++ ) {
865 std::string obj_name( "hdr_" );
866 obj_name.append( it->base_name );
867
868 out << obj_name << " = new Object();\n";
869 out << obj_name << ".width = " << it->width << ";\n";
870 out << obj_name << ".height = " << it->height << ";\n";
871 out << obj_name << ".f8_stops = " << it->f8_stops << ";\n";
872 out << obj_name << ".f_step_res = " << it->f_step_res << ";\n";
873 out << obj_name << ".base_name = \"" << it->base_name << "\";\n";
874 if( hdrhtml_set->image_dir==NULL )
875 out << obj_name << ".image_dir = \"\";\n";
876 else
877 out << obj_name << ".image_dir = \"" << hdrhtml_set->image_dir << "/\";\n";
878 out << obj_name << ".basis = " << it->basis << ";\n";
879 out << obj_name << ".shared_basis = " << it->shared_basis << ";\n";
880 out << obj_name << ".pix_per_fstop = " << it->pix_per_fstop << ";\n";
881 out << obj_name << ".hist_start = " << it->hist_start << ";\n";
882 out << obj_name << ".hist_width = " << it->hist_width << ";\n";
883 out << obj_name << ".exposure = " << it->best_exp << ";\n";
884 out << obj_name << ".best_exp = " << it->best_exp << ";\n\n";
885 }
886
887 }
888
print_image_htmlcode(ostream & out,HDRHTMLSet * hdrhtml_set,const HDRHTMLImage & it)889 void print_image_htmlcode( ostream &out, HDRHTMLSet *hdrhtml_set, const HDRHTMLImage &it )
890 {
891 std::string obj_name( "hdr_" );
892 obj_name.append( it.base_name );
893
894 std::ostringstream img_dir;
895 if( hdrhtml_set->image_dir != NULL )
896 img_dir << hdrhtml_set->image_dir << "/";
897
898 ReplacePattern replace_list[] = {
899 ReplacePattern( "hdr_img_width", it.width ),
900 ReplacePattern( "hdr_img_height", it.height ),
901 ReplacePattern( "img_dir", img_dir.str() ),
902 ReplacePattern( "hist_width", it.hist_width ),
903 ReplacePattern( "base_name", it.base_name ),
904 ReplacePattern( "help_mark_pos", it.hist_width-12 ),
905 ReplacePattern( "hdr_img_object", obj_name ),
906 ReplacePattern( "version", hdrhtml_version ),
907 ReplacePattern()
908 };
909
910 create_from_template( out, hdrhtml_set->image_template, replace_list );
911
912 }
913
print_image_htmlcode(ostream & out,void * user_data,const char * parameter)914 void print_image_htmlcode( ostream &out, void *user_data, const char *parameter )
915 {
916 HDRHTMLSet *hdrhtml_set = (HDRHTMLSet*)user_data;
917
918 if( parameter != NULL ) {
919
920 list<HDRHTMLImage>::iterator it;
921 for( it = hdrhtml_set->image_list.begin(); it != hdrhtml_set->image_list.end(); it++ ) {
922 if( it->base_name.compare( parameter ) == 0 )
923 break;
924 }
925 if( it == hdrhtml_set->image_list.end() )
926 std::cerr << "Warning: image '" << parameter << "' not found\n";
927
928 print_image_htmlcode( out, hdrhtml_set, *it );
929
930 } else {
931
932 list<HDRHTMLImage>::iterator it;
933 for( it = hdrhtml_set->image_list.begin(); it != hdrhtml_set->image_list.end(); it++ ) {
934
935 print_image_htmlcode( out, hdrhtml_set, *it );
936
937 }
938 }
939
940 }
941
print_cf_table(ostream & out,void * user_data,const char * parameter)942 void print_cf_table( ostream &out, void *user_data, const char *parameter )
943 {
944 CSVTable *cf = (CSVTable*)user_data;
945
946 out << "var cf = new Array(\n";
947 for( int b=0; b < cf->rows; b++ ) {
948 out << " new Array(";
949 for( int ex=0; ex < cf->columns; ex++ ) {
950 out << ' ' << cf->data[ex][b];
951 if( ex != cf->columns-1 )
952 out << ',';
953 }
954 out << ')';
955 if( b != cf->rows-1 )
956 out << ',';
957 out << "\n";
958 }
959 out << ");\n";
960
961 }
962
963
964