4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "bcdisplayinfo.h"
28 #include "bcsignals.h"
32 #include "bistogram.h"
33 #include "bistogramconfig.h"
34 #include "bistogramwindow.h"
37 #include "loadbalance.h"
44 class HistogramEngine;
45 class HistogramWindow;
47 REGISTER_PLUGIN(HistogramMain)
49 HistogramMain::HistogramMain(PluginServer *server)
50 : PluginVClient(server)
53 for( int i=0; i<HISTOGRAM_MODES; ++i ) {
60 mode = HISTOGRAM_VALUE;
67 HistogramMain::~HistogramMain()
72 void HistogramMain::reset()
74 for( int i=0; i<HISTOGRAM_MODES; ++i ) {
75 delete [] lookup[i]; lookup[i] = 0;
76 delete [] smoothed[i]; smoothed[i] = 0;
77 delete [] linear[i]; linear[i] = 0;
78 delete [] accum[i]; accum[i] = 0;
80 delete engine; engine = 0;
84 const char* HistogramMain::plugin_title() { return N_("Histogram Bezier"); }
85 int HistogramMain::is_realtime() { return 1; }
88 NEW_WINDOW_MACRO(HistogramMain, HistogramWindow)
89 LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
91 void HistogramMain::render_gui(void *data)
95 VFrame *input = (VFrame *)data;
96 calculate_histogram(input);
97 if( config.automatic )
98 calculate_automatic(input);
100 HistogramWindow *window = (HistogramWindow *)thread->window;
101 window->lock_window("HistogramMain::render_gui");
102 window->update_canvas();
105 window->update_input();
107 window->unlock_window();
111 void HistogramMain::update_gui()
113 if( !thread ) return;
114 HistogramWindow *window = (HistogramWindow *)thread->window;
115 // points delete in load_configuration,read_data
116 window->lock_window("HistogramMain::update_gui");
117 if( load_configuration() ) {
119 if(!config.automatic)
120 window->update_input();
122 window->unlock_window();
126 void HistogramMain::save_data(KeyFrame *keyframe)
130 // cause data to be stored directly in text
131 output.set_shared_output(keyframe->xbuf);
132 output.tag.set_title("HISTOGRAM");
134 char string[BCTEXTLEN];
137 for( int i=0; i<HISTOGRAM_MODES; ++i ) {
138 sprintf(string, "OUTPUT_MIN_%d", i);
139 output.tag.set_property(string, config.output_min[i]);
140 sprintf(string, "OUTPUT_MAX_%d", i);
141 output.tag.set_property(string, config.output_max[i]);
142 //printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
145 output.tag.set_property("AUTOMATIC", config.automatic);
146 output.tag.set_property("THRESHOLD", config.threshold);
147 output.tag.set_property("SPLIT", config.split);
148 output.tag.set_property("INTERPOLATION", config.smoothMode);
150 output.tag.set_title("/HISTOGRAM");
152 output.append_newline();
158 for( int j=0; j<HISTOGRAM_MODES; ++j ) {
159 output.tag.set_title("POINTS");
161 output.append_newline();
164 HistogramPoint *current = config.points[j].first;
166 output.tag.set_title("POINT");
167 output.tag.set_property("X", current->x);
168 output.tag.set_property("Y", current->y);
169 output.tag.set_property("GRADIENT", current->gradient);
170 output.tag.set_property("XOFFSET_LEFT", current->xoffset_left);
171 output.tag.set_property("XOFFSET_RIGHT", current->xoffset_right);
173 output.tag.set_title("/POINT");
175 output.append_newline();
180 output.tag.set_title("/POINTS");
182 output.append_newline();
190 output.terminate_string();
193 void HistogramMain::read_data(KeyFrame *keyframe)
197 input.set_shared_input(keyframe->xbuf);
200 int current_input_mode = 0;
203 while( !(result = input.read_tag()) ) {
204 if( input.tag.title_is("HISTOGRAM") ) {
205 char string[BCTEXTLEN];
206 for( int i=0; i<HISTOGRAM_MODES; ++i ) {
207 sprintf(string, "OUTPUT_MIN_%d", i);
208 config.output_min[i] = input.tag.get_property(string, config.output_min[i]);
209 sprintf(string, "OUTPUT_MAX_%d", i);
210 config.output_max[i] = input.tag.get_property(string, config.output_max[i]);
211 //printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
213 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
214 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
215 config.split = input.tag.get_property("SPLIT", config.split);
216 config.smoothMode = input.tag.get_property("INTERPOLATION", config.smoothMode);
218 else if( input.tag.title_is("POINTS") ) {
219 if( current_input_mode < HISTOGRAM_MODES ) {
220 HistogramPoints *points = &config.points[current_input_mode];
221 while( points->last )
223 while( !(result = input.read_tag()) ) {
224 if( input.tag.title_is("/POINTS") ) break;
225 if(input.tag.title_is("POINT") ) {
227 input.tag.get_property("X", 0.0),
228 input.tag.get_property("Y", 0.0));
229 points->last->gradient =
230 input.tag.get_property("GRADIENT", 1.0);
231 points->last->xoffset_left =
232 input.tag.get_property("XOFFSET_LEFT", -0.02);
233 points->last->xoffset_right =
234 input.tag.get_property("XOFFSET_RIGHT", 0.02);
237 ++current_input_mode;
246 float HistogramMain::calculate_linear(float input,
265 float x1 = 0, y1 = 0;
268 float x2 = 1, y2 = 1;
272 // Get 2 points surrounding current position
273 HistogramPoints *points = &config.points[subscript];
274 HistogramPoint *current = points->first;
276 while( current && !done ) {
277 if( current->x > input ) {
280 grad2 = current->gradient;
281 x2left = current->xoffset_left;
288 current = points->last;
290 while( current && !done ) {
291 if( current->x <= input ) {
294 grad1 = current->gradient;
296 x1right = current->xoffset_right;
302 if( !EQUIV(x2 - x1, 0) ) {
303 if( config.smoothMode == HISTOGRAM_LINEAR )
304 output = (input - x1) * (y2 - y1) / (x2 - x1) + y1;
305 else if( config.smoothMode == HISTOGRAM_POLYNOMINAL ) {
306 /* Construct third grade polynom between every two points */
309 float delx = input - x1;
310 output = (grad2 * dx + grad1 * dx - 2*dy) / (dx * dx * dx) * delx * delx * delx +
311 (3*dy - 2* grad1*dx - grad2*dx) / (dx * dx) * delx * delx + grad1*delx + y1;
313 else if( config.smoothMode == HISTOGRAM_BEZIER ) {
314 /* Using standart DeCasteljau algorithm */
315 float y1right = y1 + grad1 * x1right;
316 float y2left = y2 + grad2 * x2left;
318 float t = (input - x1) / (x2 - x1);
320 float pointAy = y1 + (y1right - y1) * t;
321 float pointBy = y1right + (y2left - y1right) * t;
322 float pointCy = y2left + (y2 - y2left) * t;
323 float pointABy = pointAy + (pointBy - pointAy) * t;
324 float pointBCy = pointBy + (pointCy - pointBy) * t;
325 output = pointABy + (pointBCy - pointABy) * t;
335 output = calculate_linear(output, HISTOGRAM_VALUE, 0);
339 float output_min = config.output_min[subscript];
340 float output_max = config.output_max[subscript];
342 // Compress output for value followed by channel
343 output = output_min + output * (output_max - output_min);
347 float HistogramMain::calculate_smooth(float input, int subscript)
349 int bins = slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
351 float x_f = (input - HIST_MIN_INPUT) * bins / FLOAT_RANGE;
354 CLAMP(x_i1, 0, bins1);
355 CLAMP(x_i2, 0, bins1);
356 CLAMP(x_f, 0, bins1);
358 float smooth1 = smoothed[subscript][x_i1];
359 float smooth2 = smoothed[subscript][x_i2];
360 float result = smooth1 + (smooth2 - smooth1) * (x_f - x_i1);
361 CLAMP(result, 0, 1.0);
366 void HistogramMain::calculate_histogram(VFrame *data)
368 int color_model = data->get_color_model();
369 int pix_sz = BC_CModels::calculate_pixelsize(color_model);
370 int comp_sz = pix_sz / BC_CModels::components(color_model);
371 int needed_slots = comp_sz > 1 ? 0x10000 : 0x100;
372 if( slots != needed_slots ) {
374 slots = needed_slots;
376 int bins = slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
378 for( int i=0; i<HISTOGRAM_MODES; ++i )
379 accum[i] = new int[bins];
383 int cpus = data->get_w() * data->get_h() * pix_sz / 0x80000 + 2;
384 int smps = get_project_smp();
385 if( cpus > smps ) cpus = smps;
386 engine = new HistogramEngine(this, cpus, cpus);
388 engine->process_packages(HistogramEngine::HISTOGRAM, data);
390 for( int i=0; i<engine->get_total_clients(); ++i ) {
391 HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
393 for( int j=0; j<HISTOGRAM_MODES; ++j )
394 memcpy(accum[j], unit->accum[j], sizeof(int)*bins);
397 for( int j=0; j<HISTOGRAM_MODES; ++j ) {
398 int *out = accum[j], *in = unit->accum[j];
399 for( int k=0; k<bins; ++k ) out[k] += in[k];
404 // Remove top and bottom from calculations. Doesn't work in high
405 // precision colormodels.
406 for( int i=0; i<HISTOGRAM_MODES; ++i ) {
408 accum[i][bins-1] = 0;
413 void HistogramMain::calculate_automatic(VFrame *data)
415 calculate_histogram(data);
416 config.reset_points();
417 int bins = slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
420 for( int i=0; i<3; ++i ) {
421 int *accum = this->accum[i];
422 int pixels = data->get_w() * data->get_h();
423 float white_fraction = 1.0 - (1.0 - config.threshold) / 2;
424 int threshold = (int)(white_fraction * pixels);
426 float max_level = 1.0;
427 float min_level = 0.0;
428 // Get histogram slot above threshold of pixels
429 for( int j=0; j<bins; ++j ) {
431 if( total >= threshold ) {
432 max_level = (float)j/bins * FLOAT_RANGE + HIST_MIN_INPUT;
437 // Get slot below 99% of pixels
439 for( int j=bins; --j>=0; ) {
441 if( total >= threshold ) {
442 min_level = (float)j/bins * FLOAT_RANGE + HIST_MIN_INPUT;
446 config.points[i].insert(max_level, 1.0);
447 config.points[i].insert(min_level, 0.0);
451 int HistogramMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
453 this->input = input_ptr;
454 this->output = output_ptr;
456 int need_reconfigure = load_configuration();
457 int color_model = input->get_color_model();
458 int pix_sz = BC_CModels::calculate_pixelsize(color_model);
459 int comp_sz = pix_sz / BC_CModels::components(color_model);
460 int needed_slots = comp_sz > 1 ? 0x10000 : 0x100;
461 if( slots != needed_slots ) {
463 slots = needed_slots;
464 need_reconfigure = 1;
467 send_render_gui(input);
469 if( input->get_rows()[0] != output_ptr->get_rows()[0] ) {
470 output_ptr->copy_from(input);
474 // Generate tables here. The same table is used by many packages to render
475 // each horizontal stripe. Need to cover the entire output range in each
476 // table to avoid green borders
477 if( !lookup[0] || !smoothed[0] || !linear[0] || config.automatic)
478 need_reconfigure = 1;
479 if( need_reconfigure ) {
481 // Calculate new curves
482 if( config.automatic ) {
483 calculate_automatic(input);
487 // Generate transfer tables for integer colormodels.
488 for( int i=0; i<3; ++i )
489 tabulate_curve(i, 1);
494 int cpus = input->get_w() * input->get_h() * pix_sz / 0x80000 + 2;
495 int smps = get_project_smp();
496 if( cpus > smps ) cpus = smps;
497 engine = new HistogramEngine(this, cpus, cpus);
500 engine->process_packages(HistogramEngine::APPLY, input);
506 void HistogramMain::tabulate_curve(int subscript, int use_value)
508 int bins = slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
509 if(!lookup[subscript])
510 lookup[subscript] = new int[bins];
511 if(!smoothed[subscript])
512 smoothed[subscript] = new float[bins];
513 if(!linear[subscript])
514 linear[subscript] = new float[bins];
516 float *current_smooth = smoothed[subscript];
517 float *current_linear = linear[subscript];
520 for( int i=0; i<bins; ++i ) {
521 float input = (float)i/bins * FLOAT_RANGE + HIST_MIN_INPUT;
522 current_linear[i] = calculate_linear(input, subscript, use_value);
526 for( int i=0; i<bins; ++i ) {
527 // current_smooth[i] = current_linear[i] * 0.001 +
529 current_smooth[i] = current_linear[i];
530 // prev = current_smooth[i];
532 // Generate lookup tables for integer colormodels
534 int slots1 = slots-1;
535 for( int i=0; i<slots; ++i ) {
536 lookup[subscript][i] =
537 (int)(calculate_smooth((float)i/slots1, subscript) * slots1);
542 HistogramPackage::HistogramPackage()
547 HistogramUnit::HistogramUnit(HistogramEngine *server,
548 HistogramMain *plugin)
551 this->plugin = plugin;
552 this->server = server;
553 int bins = plugin->slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
554 for( int i=0; i<HISTOGRAM_MODES; ++i )
555 accum[i] = new int[bins];
558 HistogramUnit::~HistogramUnit()
560 for( int i=0; i<HISTOGRAM_MODES; ++i )
564 void HistogramUnit::process_package(LoadPackage *package)
566 HistogramPackage *pkg = (HistogramPackage*)package;
567 int bins = plugin->slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
569 int slots1 = -HISTOGRAM_MIN * (plugin->slots-1) / 100;
570 if( server->operation == HistogramEngine::HISTOGRAM ) {
572 #define HISTOGRAM_HEAD(type) \
574 for( int i=pkg->start; i<pkg->end; ++i ) { \
575 type *row = (type*)data->get_rows()[i]; \
576 for( int j=0; j<w; ++j ) {
578 #define HISTOGRAM_TAIL(components) \
579 v = MAX(r, g); v = MAX(v, b); v += slots1; \
580 r += slots1; g += slots1; b += slots1; \
581 CLAMP(r, 0, bins1); ++accum_r[r]; \
582 CLAMP(g, 0, bins1); ++accum_g[g]; \
583 CLAMP(b, 0, bins1); ++accum_b[b]; \
584 CLAMP(v, 0, bins1); ++accum_v[v]; \
590 VFrame *data = server->data;
591 int w = data->get_w();
592 // int h = data->get_h();
593 int *accum_r = accum[HISTOGRAM_RED];
594 int *accum_g = accum[HISTOGRAM_GREEN];
595 int *accum_b = accum[HISTOGRAM_BLUE];
596 int *accum_v = accum[HISTOGRAM_VALUE];
597 int r, g, b, y, u, v;
599 switch( data->get_color_model() ) {
601 HISTOGRAM_HEAD(unsigned char)
602 r = row[0]; g = row[1]; b = row[2];
606 HISTOGRAM_HEAD(float)
607 r = (int)(row[0] * 0xffff);
608 g = (int)(row[1] * 0xffff);
609 b = (int)(row[2] * 0xffff);
613 HISTOGRAM_HEAD(unsigned char)
614 y = row[0]; u = row[1]; v = row[2];
615 YUV::yuv.yuv_to_rgb_8(r, g, b, y, u, v);
619 HISTOGRAM_HEAD(unsigned char)
620 r = row[0]; g = row[1]; b = row[2];
624 HISTOGRAM_HEAD(float)
625 r = (int)(row[0] * 0xffff);
626 g = (int)(row[1] * 0xffff);
627 b = (int)(row[2] * 0xffff);
631 HISTOGRAM_HEAD(unsigned char)
632 y = row[0]; u = row[1]; v = row[2];
633 YUV::yuv.yuv_to_rgb_8(r, g, b, y, u, v);
637 HISTOGRAM_HEAD(uint16_t)
638 r = row[0]; g = row[1]; b = row[2];
642 HISTOGRAM_HEAD(uint16_t)
643 y = row[0]; u = row[1]; v = row[2];
644 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
647 case BC_RGBA16161616:
648 HISTOGRAM_HEAD(uint16_t)
649 r = row[0]; g = row[1]; b = row[2];
652 case BC_YUVA16161616:
653 HISTOGRAM_HEAD(uint16_t)
654 y = row[0]; u = row[1]; v = row[2];
655 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v);
661 if( server->operation == HistogramEngine::APPLY ) {
663 #define PROCESS(type, components) \
665 for( int i=pkg->start; i<pkg->end; ++i ) { \
666 type *row = (type*)input->get_rows()[i]; \
667 for( int j=0; j<w; ++j ) { \
668 if ( plugin->config.split && ((j + i * w / h) < w) ) \
670 row[0] = lookup_r[row[0]]; \
671 row[1] = lookup_g[row[1]]; \
672 row[2] = lookup_b[row[2]]; \
678 #define PROCESS_YUV(type, components, max) \
680 for( int i=pkg->start; i<pkg->end; ++i ) { \
681 type *row = (type*)input->get_rows()[i]; \
682 for( int j=0; j<w; ++j ) { \
683 if ( plugin->config.split && ((j + i * w / h) < w) ) \
685 y = row[0]; u = row[1]; v = row[2]; \
687 YUV::yuv.yuv_to_rgb_8(r, g, b, y, u, v); \
689 YUV::yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
690 r = lookup_r[r]; g = lookup_g[g]; b = lookup_b[b]; \
692 YUV::yuv.rgb_to_yuv_8(r, g, b, y, u, v); \
694 YUV::yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
695 row[0] = y; row[1] = u; row[2] = v; \
701 #define PROCESS_FLOAT(components) \
703 for( int i=pkg->start; i<pkg->end; ++i ) { \
704 float *row = (float*)input->get_rows()[i]; \
705 for( int j=0; j<w; ++j ) { \
706 if ( plugin->config.split && ((j + i * w / h) < w) ) \
708 float r = row[0], g = row[1], b = row[2]; \
709 r = plugin->calculate_smooth(r, HISTOGRAM_RED); \
710 g = plugin->calculate_smooth(g, HISTOGRAM_GREEN); \
711 b = plugin->calculate_smooth(b, HISTOGRAM_BLUE); \
712 row[0] = r; row[1] = g; row[2] = b; \
719 VFrame *input = plugin->input;
720 // VFrame *output = plugin->output;
721 int w = input->get_w(), h = input->get_h();
722 int *lookup_r = plugin->lookup[0];
723 int *lookup_g = plugin->lookup[1];
724 int *lookup_b = plugin->lookup[2];
725 int r, g, b, y, u, v;
726 switch( input->get_color_model() ) {
728 PROCESS(unsigned char, 3)
734 PROCESS(unsigned char, 4)
742 case BC_RGBA16161616:
746 PROCESS_YUV(unsigned char, 3, 0xff)
749 PROCESS_YUV(unsigned char, 4, 0xff)
752 PROCESS_YUV(uint16_t, 3, 0xffff)
754 case BC_YUVA16161616:
755 PROCESS_YUV(uint16_t, 4, 0xffff)
762 HistogramEngine::HistogramEngine(HistogramMain *plugin,
763 int total_clients, int total_packages)
764 : LoadServer(total_clients, total_packages)
766 this->plugin = plugin;
769 void HistogramEngine::init_packages()
771 total_size = data->get_h();
772 for( int i=0,start=0,n=get_total_packages(); i<n; ) {
773 HistogramPackage *package = (HistogramPackage*)get_package(i);
774 package->start = start;
775 package->end = start = (total_size * ++i)/ n;
778 int bins = plugin->slots * (HISTOGRAM_MAX-HISTOGRAM_MIN)/100;
779 // Initialize clients here in case some don't get run.
780 for( int i=0; i<get_total_clients(); ++i ) {
781 HistogramUnit *unit = (HistogramUnit*)get_client(i);
782 for( int i=0; i<HISTOGRAM_MODES; ++i )
783 bzero(unit->accum[i], sizeof(int)*bins);
788 LoadClient* HistogramEngine::new_client()
790 return new HistogramUnit(this, plugin);
793 LoadPackage* HistogramEngine::new_package()
795 return new HistogramPackage;
798 void HistogramEngine::process_packages(int operation, VFrame *data)
801 this->operation = operation;
802 LoadServer::process_packages();