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 "histogram.h"
33 #include "histogramconfig.h"
34 #include "histogramwindow.h"
37 #include "loadbalance.h"
44 class HistogramEngine;
45 class HistogramWindow;
51 REGISTER_PLUGIN(HistogramMain)
67 HistogramMain::HistogramMain(PluginServer *server)
68 : PluginVClient(server)
72 for(int i = 0; i < HISTOGRAM_MODES; i++)
80 mode = HISTOGRAM_VALUE;
86 HistogramMain::~HistogramMain()
89 for(int i = 0; i < HISTOGRAM_MODES;i++)
92 delete [] smoothed[i];
99 char* HistogramMain::plugin_title() { return _("Histogram"); }
100 int HistogramMain::is_realtime() { return 1; }
104 SHOW_GUI_MACRO(HistogramMain, HistogramThread)
106 SET_STRING_MACRO(HistogramMain)
108 RAISE_WINDOW_MACRO(HistogramMain)
110 LOAD_CONFIGURATION_MACRO(HistogramMain, HistogramConfig)
112 void HistogramMain::render_gui(void *data)
116 calculate_histogram((VFrame*)data);
120 calculate_automatic((VFrame*)data);
123 thread->window->lock_window("HistogramMain::render_gui");
124 thread->window->update_canvas();
127 thread->window->update_input();
129 thread->window->unlock_window();
133 void HistogramMain::update_gui()
137 thread->window->lock_window("HistogramMain::update_gui");
138 int reconfigure = load_configuration();
141 thread->window->update(0);
142 if(!config.automatic)
144 thread->window->update_input();
147 thread->window->unlock_window();
152 int HistogramMain::load_defaults()
154 char directory[BCTEXTLEN], string[BCTEXTLEN];
155 // set the default directory
156 sprintf(directory, "%shistogram.rc", BCASTDIR);
159 defaults = new BC_Hash(directory);
162 for(int j = 0; j < HISTOGRAM_MODES; j++)
164 while(config.points[j].last) delete config.points[j].last;
166 sprintf(string, "TOTAL_POINTS_%d", j);
167 int total_points = defaults->get(string, 0);
169 for(int i = 0; i < total_points; i++)
171 HistogramPoint *point = new HistogramPoint;
172 sprintf(string, "INPUT_X_%d_%d", j, i);
173 point->x = defaults->get(string, point->x);
174 sprintf(string, "INPUT_Y_%d_%d", j, i);
175 point->y = defaults->get(string, point->y);
176 sprintf(string, "GRADIENT_%d_%d", j, i);
177 point->gradient = defaults->get(string, point->gradient);
178 sprintf(string, "XOFFSET_LEFT_%d_%d", j, i);
179 point->xoffset_left = defaults->get(string, point->xoffset_left);
180 sprintf(string, "XOFFSET_RIGHT_%d_%d", j, i);
181 point->xoffset_right = defaults->get(string, point->xoffset_right);
182 config.points[j].append(point);
187 for(int i = 0; i < HISTOGRAM_MODES; i++)
189 sprintf(string, "OUTPUT_MIN_%d", i);
190 config.output_min[i] = defaults->get(string, config.output_min[i]);
191 sprintf(string, "OUTPUT_MAX_%d", i);
192 config.output_max[i] = defaults->get(string, config.output_max[i]);
195 config.automatic = defaults->get("AUTOMATIC", config.automatic);
196 mode = defaults->get("MODE", mode);
197 CLAMP(mode, 0, HISTOGRAM_MODES - 1);
198 config.threshold = defaults->get("THRESHOLD", config.threshold);
199 config.split = defaults->get("SPLIT", config.split);
200 config.smoothMode = defaults->get("INTERPOLATION", config.smoothMode);
206 int HistogramMain::save_defaults()
208 char string[BCTEXTLEN];
212 for(int j = 0; j < HISTOGRAM_MODES; j++)
214 int total_points = config.points[j].total();
215 sprintf(string, "TOTAL_POINTS_%d", j);
216 defaults->update(string, total_points);
217 HistogramPoint *current = config.points[j].first;
221 sprintf(string, "INPUT_X_%d_%d", j, number);
222 defaults->update(string, current->x);
223 sprintf(string, "INPUT_Y_%d_%d", j, number);
224 defaults->update(string, current->y);
225 sprintf(string, "GRADIENT_%d_%d", j, number);
226 defaults->update(string, current->gradient);
227 sprintf(string, "XOFFSET_LEFT_%d_%d", j, number);
228 defaults->update(string, current->xoffset_left);
229 sprintf(string, "XOFFSET_RIGHT_%d_%d", j, number);
230 defaults->update(string, current->xoffset_right);
237 for(int i = 0; i < HISTOGRAM_MODES; i++)
239 sprintf(string, "OUTPUT_MIN_%d", i);
240 defaults->update(string, config.output_min[i]);
241 sprintf(string, "OUTPUT_MAX_%d", i);
242 defaults->update(string, config.output_max[i]);
245 defaults->update("AUTOMATIC", config.automatic);
246 defaults->update("MODE", mode);
247 defaults->update("THRESHOLD", config.threshold);
248 defaults->update("SPLIT", config.split);
249 defaults->update("INTERPOLATION", config.smoothMode);
256 void HistogramMain::save_data(KeyFrame *keyframe)
260 // cause data to be stored directly in text
261 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
262 output.tag.set_title("HISTOGRAM");
264 char string[BCTEXTLEN];
267 for(int i = 0; i < HISTOGRAM_MODES; i++)
269 sprintf(string, "OUTPUT_MIN_%d", i);
270 output.tag.set_property(string, config.output_min[i]);
271 sprintf(string, "OUTPUT_MAX_%d", i);
272 output.tag.set_property(string, config.output_max[i]);
273 //printf("HistogramMain::save_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
276 output.tag.set_property("AUTOMATIC", config.automatic);
277 output.tag.set_property("THRESHOLD", config.threshold);
278 output.tag.set_property("SPLIT", config.split);
279 output.tag.set_property("INTERPOLATION", config.smoothMode);
281 output.append_newline();
287 for(int j = 0; j < HISTOGRAM_MODES; j++)
289 output.tag.set_title("POINTS");
291 output.append_newline();
294 HistogramPoint *current = config.points[j].first;
297 output.tag.set_title("POINT");
298 output.tag.set_property("X", current->x);
299 output.tag.set_property("Y", current->y);
300 output.tag.set_property("GRADIENT", current->gradient);
301 output.tag.set_property("XOFFSET_LEFT", current->xoffset_left);
302 output.tag.set_property("XOFFSET_RIGHT", current->xoffset_right);
304 output.append_newline();
309 output.tag.set_title("/POINTS");
311 output.append_newline();
319 output.terminate_string();
322 void HistogramMain::read_data(KeyFrame *keyframe)
326 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
329 int current_input_mode = 0;
334 result = input.read_tag();
338 if(input.tag.title_is("HISTOGRAM"))
340 char string[BCTEXTLEN];
341 for(int i = 0; i < HISTOGRAM_MODES; i++)
343 sprintf(string, "OUTPUT_MIN_%d", i);
344 config.output_min[i] = input.tag.get_property(string, config.output_min[i]);
345 sprintf(string, "OUTPUT_MAX_%d", i);
346 config.output_max[i] = input.tag.get_property(string, config.output_max[i]);
347 //printf("HistogramMain::read_data %d %f %d\n", config.input_min[i], config.input_mid[i], config.input_max[i]);
349 config.automatic = input.tag.get_property("AUTOMATIC", config.automatic);
350 config.threshold = input.tag.get_property("THRESHOLD", config.threshold);
351 config.split = input.tag.get_property("SPLIT", config.split);
352 config.smoothMode = input.tag.get_property("INTERPOLATION", config.smoothMode);
355 if(input.tag.title_is("POINTS"))
357 if(current_input_mode < HISTOGRAM_MODES)
359 HistogramPoints *points = &config.points[current_input_mode];
364 result = input.read_tag();
367 if(input.tag.title_is("/POINTS"))
372 if(input.tag.title_is("POINT"))
375 input.tag.get_property("X", 0.0),
376 input.tag.get_property("Y", 0.0));
377 points->last->gradient =
378 input.tag.get_property("GRADIENT", 1.0);
379 points->last->xoffset_left =
380 input.tag.get_property("XOFFSET_LEFT", -0.02);
381 points->last->xoffset_right =
382 input.tag.get_property("XOFFSET_RIGHT", 0.02);
388 current_input_mode++;
397 float HistogramMain::calculate_linear(float input,
430 // Get 2 points surrounding current position
431 HistogramPoints *points = &config.points[subscript];
432 HistogramPoint *current = points->first;
434 while(current && !done)
436 if(current->x > input)
440 grad2 = current->gradient;
441 x2left = current->xoffset_left;
448 current = points->last;
450 while(current && !done)
452 if(current->x <= input)
456 grad1 = current->gradient;
458 x1right = current->xoffset_right;
467 if(!EQUIV(x2 - x1, 0))
469 if (config.smoothMode == HISTOGRAM_LINEAR)
470 output = (input - x1) * (y2 - y1) / (x2 - x1) + y1;
471 else if (config.smoothMode == HISTOGRAM_POLYNOMINAL)
473 /* Construct third grade polynom between every two points */
476 float delx = input - x1;
477 output = 1 / (dx * dx * dx) * (grad2 * dx + grad1 * dx - 2*dy) * delx * delx * delx + 1 / (dx * dx) * (3*dy - 2* grad1*dx - grad2*dx) * delx * delx + grad1*delx + y1;
479 else if (config.smoothMode == HISTOGRAM_BEZIER)
481 /* Using standart DeCasteljau algorithm */
482 float y1right = y1 + grad1 * x1right;
483 float y2left = y2 + grad2 * x2left;
485 float t = (input - x1) / (x2 - x1);
487 float pointAy = y1 + (y1right - y1) * t;
488 float pointBy = y1right + (y2left - y1right) * t;
489 float pointCy = y2left + (y2 - y2left) * t;
490 float pointABy = pointAy + (pointBy - pointAy) * t;
491 float pointBCy = pointBy + (pointCy - pointBy) * t;
492 output = pointABy + (pointBCy - pointABy) * t;
511 output = calculate_linear(output, HISTOGRAM_VALUE, 0);
515 float output_min = config.output_min[subscript];
516 float output_max = config.output_max[subscript];
521 // Compress output for value followed by channel
522 output = output_min +
524 (output_max - output_min);
530 float HistogramMain::calculate_smooth(float input, int subscript)
532 float x_f = (input - HIST_MIN_INPUT) * HISTOGRAM_SLOTS / FLOAT_RANGE;
535 CLAMP(x_i1, 0, HISTOGRAM_SLOTS - 1);
536 CLAMP(x_i2, 0, HISTOGRAM_SLOTS - 1);
537 CLAMP(x_f, 0, HISTOGRAM_SLOTS - 1);
539 float smooth1 = smoothed[subscript][x_i1];
540 float smooth2 = smoothed[subscript][x_i2];
541 float result = smooth1 + (smooth2 - smooth1) * (x_f - x_i1);
542 CLAMP(result, 0, 1.0);
547 void HistogramMain::calculate_histogram(VFrame *data)
550 if(!engine) engine = new HistogramEngine(this,
551 get_project_smp() + 1,
552 get_project_smp() + 1);
556 for(int i = 0; i < HISTOGRAM_MODES; i++)
557 accum[i] = new int[HISTOGRAM_SLOTS];
559 engine->process_packages(HistogramEngine::HISTOGRAM, data);
561 for(int i = 0; i < engine->get_total_clients(); i++)
563 HistogramUnit *unit = (HistogramUnit*)engine->get_client(i);
566 for(int j = 0; j < HISTOGRAM_MODES; j++)
567 memcpy(accum[j], unit->accum[j], sizeof(int) * HISTOGRAM_SLOTS);
571 for(int j = 0; j < HISTOGRAM_MODES; j++)
574 int *in = unit->accum[j];
575 for(int k = 0; k < HISTOGRAM_SLOTS; k++)
581 // Remove top and bottom from calculations. Doesn't work in high
582 // precision colormodels.
583 for(int i = 0; i < HISTOGRAM_MODES; i++)
586 accum[i][HISTOGRAM_SLOTS - 1] = 0;
591 void HistogramMain::calculate_automatic(VFrame *data)
593 calculate_histogram(data);
594 config.reset_points();
597 for(int i = 0; i < 3; i++)
599 int *accum = this->accum[i];
600 int pixels = data->get_w() * data->get_h();
601 float white_fraction = 1.0 - (1.0 - config.threshold) / 2;
602 int threshold = (int)(white_fraction * pixels);
604 float max_level = 1.0;
605 float min_level = 0.0;
607 // Get histogram slot above threshold of pixels
608 for(int j = 0; j < HISTOGRAM_SLOTS; j++)
611 if(total >= threshold)
613 max_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
618 // Get slot below 99% of pixels
620 for(int j = HISTOGRAM_SLOTS - 1; j >= 0; j--)
623 if(total >= threshold)
625 min_level = (float)j / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
631 config.points[i].insert(max_level, 1.0);
632 config.points[i].insert(min_level, 0.0);
642 int HistogramMain::process_realtime(VFrame *input_ptr, VFrame *output_ptr)
645 int need_reconfigure = load_configuration();
649 if(!engine) engine = new HistogramEngine(this,
650 get_project_smp() + 1,
651 get_project_smp() + 1);
652 this->input = input_ptr;
653 this->output = output_ptr;
655 send_render_gui(input_ptr);
657 if(input_ptr->get_rows()[0] != output_ptr->get_rows()[0])
659 output_ptr->copy_from(input_ptr);
663 // Generate tables here. The same table is used by many packages to render
664 // each horizontal stripe. Need to cover the entire output range in each
665 // table to avoid green borders
666 if(need_reconfigure ||
673 // Calculate new curves
676 calculate_automatic(input);
680 // Generate transfer tables for integer colormodels.
681 for(int i = 0; i < 3; i++)
682 tabulate_curve(i, 1);
690 engine->process_packages(HistogramEngine::APPLY, input);
697 void HistogramMain::tabulate_curve(int subscript, int use_value)
700 if(!lookup[subscript])
701 lookup[subscript] = new int[HISTOGRAM_SLOTS];
702 if(!smoothed[subscript])
703 smoothed[subscript] = new float[HISTOGRAM_SLOTS];
704 if(!linear[subscript])
705 linear[subscript] = new float[HISTOGRAM_SLOTS];
707 float *current_smooth = smoothed[subscript];
708 float *current_linear = linear[subscript];
711 for(i = 0; i < HISTOGRAM_SLOTS; i++)
713 float input = (float)i / HISTOGRAM_SLOTS * FLOAT_RANGE + HIST_MIN_INPUT;
714 current_linear[i] = calculate_linear(input, subscript, use_value);
724 for(i = 0; i < HISTOGRAM_SLOTS; i++)
726 // current_smooth[i] = current_linear[i] * 0.001 +
728 current_smooth[i] = current_linear[i];
729 prev = current_smooth[i];
733 // Generate lookup tables for integer colormodels
736 switch(input->get_color_model())
740 for(i = 0; i < 0x100; i++)
741 lookup[subscript][i] =
742 (int)(calculate_smooth((float)i / 0xff, subscript) * 0xff);
744 // All other integer colormodels are converted to 16 bit RGB
746 for(i = 0; i < 0x10000; i++)
747 lookup[subscript][i] =
748 (int)(calculate_smooth((float)i / 0xffff, subscript) * 0xffff);
765 HistogramPackage::HistogramPackage()
773 HistogramUnit::HistogramUnit(HistogramEngine *server,
774 HistogramMain *plugin)
777 this->plugin = plugin;
778 this->server = server;
779 for(int i = 0; i < HISTOGRAM_MODES; i++)
780 accum[i] = new int[HISTOGRAM_SLOTS];
783 HistogramUnit::~HistogramUnit()
785 for(int i = 0; i < HISTOGRAM_MODES; i++)
789 void HistogramUnit::process_package(LoadPackage *package)
791 HistogramPackage *pkg = (HistogramPackage*)package;
793 if(server->operation == HistogramEngine::HISTOGRAM)
796 #define HISTOGRAM_HEAD(type) \
798 for(int i = pkg->start; i < pkg->end; i++) \
800 type *row = (type*)data->get_rows()[i]; \
801 for(int j = 0; j < w; j++) \
804 #define HISTOGRAM_TAIL(components) \
805 /* v = (r * 76 + g * 150 + b * 29) >> 8; */ \
808 r += -HISTOGRAM_MIN * 0xffff / 100; \
809 g += -HISTOGRAM_MIN * 0xffff / 100; \
810 b += -HISTOGRAM_MIN * 0xffff / 100; \
811 v += -HISTOGRAM_MIN * 0xffff / 100; \
812 CLAMP(r, 0, HISTOGRAM_SLOTS); \
813 CLAMP(g, 0, HISTOGRAM_SLOTS); \
814 CLAMP(b, 0, HISTOGRAM_SLOTS); \
815 CLAMP(v, 0, HISTOGRAM_SLOTS); \
828 VFrame *data = server->data;
829 int w = data->get_w();
830 int h = data->get_h();
831 int *accum_r = accum[HISTOGRAM_RED];
832 int *accum_g = accum[HISTOGRAM_GREEN];
833 int *accum_b = accum[HISTOGRAM_BLUE];
834 int *accum_v = accum[HISTOGRAM_VALUE];
835 int r, g, b, a, y, u, v;
837 switch(data->get_color_model())
840 HISTOGRAM_HEAD(unsigned char)
841 r = (row[0] << 8) | row[0];
842 g = (row[1] << 8) | row[1];
843 b = (row[2] << 8) | row[2];
847 HISTOGRAM_HEAD(float)
848 r = (int)(row[0] * 0xffff);
849 g = (int)(row[1] * 0xffff);
850 b = (int)(row[2] * 0xffff);
854 HISTOGRAM_HEAD(unsigned char)
855 y = (row[0] << 8) | row[0];
856 u = (row[1] << 8) | row[1];
857 v = (row[2] << 8) | row[2];
858 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
862 HISTOGRAM_HEAD(unsigned char)
863 r = (row[0] << 8) | row[0];
864 g = (row[1] << 8) | row[1];
865 b = (row[2] << 8) | row[2];
869 HISTOGRAM_HEAD(float)
870 r = (int)(row[0] * 0xffff);
871 g = (int)(row[1] * 0xffff);
872 b = (int)(row[2] * 0xffff);
876 HISTOGRAM_HEAD(unsigned char)
877 y = (row[0] << 8) | row[0];
878 u = (row[1] << 8) | row[1];
879 v = (row[2] << 8) | row[2];
880 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
884 HISTOGRAM_HEAD(uint16_t)
891 HISTOGRAM_HEAD(uint16_t)
895 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
898 case BC_RGBA16161616:
899 HISTOGRAM_HEAD(uint16_t)
905 case BC_YUVA16161616:
906 HISTOGRAM_HEAD(uint16_t)
910 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v);
916 if(server->operation == HistogramEngine::APPLY)
921 #define PROCESS(type, components) \
923 for(int i = pkg->start; i < pkg->end; i++) \
925 type *row = (type*)input->get_rows()[i]; \
926 for(int j = 0; j < w; j++) \
928 if ( plugin->config.split && ((j + i * w / h) < w) ) \
930 row[0] = lookup_r[row[0]]; \
931 row[1] = lookup_g[row[1]]; \
932 row[2] = lookup_b[row[2]]; \
938 #define PROCESS_YUV(type, components, max) \
940 for(int i = pkg->start; i < pkg->end; i++) \
942 type *row = (type*)input->get_rows()[i]; \
943 for(int j = 0; j < w; j++) \
945 if ( plugin->config.split && ((j + i * w / h) < w) ) \
947 /* Convert to 16 bit RGB */ \
950 y = (row[0] << 8) | row[0]; \
951 u = (row[1] << 8) | row[1]; \
952 v = (row[2] << 8) | row[2]; \
961 plugin->yuv.yuv_to_rgb_16(r, g, b, y, u, v); \
963 /* Look up in RGB domain */ \
968 /* Convert to 16 bit YUV */ \
969 plugin->yuv.rgb_to_yuv_16(r, g, b, y, u, v); \
988 #define PROCESS_FLOAT(components) \
990 for(int i = pkg->start; i < pkg->end; i++) \
992 float *row = (float*)input->get_rows()[i]; \
993 for(int j = 0; j < w; j++) \
995 if ( plugin->config.split && ((j + i * w / h) < w) ) \
1001 r = plugin->calculate_smooth(r, HISTOGRAM_RED); \
1002 g = plugin->calculate_smooth(g, HISTOGRAM_GREEN); \
1003 b = plugin->calculate_smooth(b, HISTOGRAM_BLUE); \
1009 row += components; \
1015 VFrame *input = plugin->input;
1016 VFrame *output = plugin->output;
1017 int w = input->get_w();
1018 int h = input->get_h();
1019 int *lookup_r = plugin->lookup[0];
1020 int *lookup_g = plugin->lookup[1];
1021 int *lookup_b = plugin->lookup[2];
1022 int r, g, b, y, u, v, a;
1023 switch(input->get_color_model())
1026 PROCESS(unsigned char, 3)
1032 PROCESS(unsigned char, 4)
1038 PROCESS(uint16_t, 3)
1040 case BC_RGBA16161616:
1041 PROCESS(uint16_t, 4)
1044 PROCESS_YUV(unsigned char, 3, 0xff)
1047 PROCESS_YUV(unsigned char, 4, 0xff)
1050 PROCESS_YUV(uint16_t, 3, 0xffff)
1052 case BC_YUVA16161616:
1053 PROCESS_YUV(uint16_t, 4, 0xffff)
1064 HistogramEngine::HistogramEngine(HistogramMain *plugin,
1067 : LoadServer(total_clients, total_packages)
1069 this->plugin = plugin;
1072 void HistogramEngine::init_packages()
1077 total_size = data->get_h();
1080 total_size = data->get_h();
1085 int package_size = (int)((float)total_size /
1086 get_total_packages() + 1);
1089 for(int i = 0; i < get_total_packages(); i++)
1091 HistogramPackage *package = (HistogramPackage*)get_package(i);
1092 package->start = total_size * i / get_total_packages();
1093 package->end = total_size * (i + 1) / get_total_packages();
1096 // Initialize clients here in case some don't get run.
1097 for(int i = 0; i < get_total_clients(); i++)
1099 HistogramUnit *unit = (HistogramUnit*)get_client(i);
1100 for(int i = 0; i < HISTOGRAM_MODES; i++)
1101 bzero(unit->accum[i], sizeof(int) * HISTOGRAM_SLOTS);
1106 LoadClient* HistogramEngine::new_client()
1108 return new HistogramUnit(this, plugin);
1111 LoadPackage* HistogramEngine::new_package()
1113 return new HistogramPackage;
1116 void HistogramEngine::process_packages(int operation, VFrame *data)
1119 this->operation = operation;
1120 LoadServer::process_packages();