X-Git-Url: http://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fdecimate%2Fdecimate.C;fp=cinelerra-5.1%2Fplugins%2Fdecimate%2Fdecimate.C;h=0a08373f595fce7ce33ac66489bf8ae05907df6b;hb=30bdb85eb33a8ee7ba675038a86c6be59c43d7bd;hp=0000000000000000000000000000000000000000;hpb=52fcc46226f9df46f9ce9d0566dc568455a7db0b;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/decimate/decimate.C b/cinelerra-5.1/plugins/decimate/decimate.C new file mode 100644 index 00000000..0a08373f --- /dev/null +++ b/cinelerra-5.1/plugins/decimate/decimate.C @@ -0,0 +1,831 @@ + +/* + * CINELERRA + * Copyright (C) 2008 Adam Williams + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "bcdisplayinfo.h" +#include "clip.h" +#include "bchash.h" +#include "filexml.h" +#include "guicast.h" +#include "keyframe.h" +#include "language.h" +#include "pluginvclient.h" +#include "vframe.h" + +#include +#include + + +#define TOP_FIELD_FIRST 0 +#define BOTTOM_FIELD_FIRST 1 +#define TOTAL_FRAMES 10 + +class Decimate; +class DecimateWindow; + + +class DecimateConfig +{ +public: + DecimateConfig(); + void copy_from(DecimateConfig *config); + int equivalent(DecimateConfig *config); + + double input_rate; +// Averaged frames is useless. Some of the frames are permanently +// destroyed in conversion from PAL to NTSC. + int averaged_frames; + int least_difference; +}; + + + + +class DecimateRate : public BC_TextBox +{ +public: + DecimateRate(Decimate *plugin, + DecimateWindow *gui, + int x, + int y); + int handle_event(); + Decimate *plugin; + DecimateWindow *gui; +}; + +class DecimateRateMenu : public BC_ListBox +{ +public: + DecimateRateMenu(Decimate *plugin, + DecimateWindow *gui, + int x, + int y); + int handle_event(); + Decimate *plugin; + DecimateWindow *gui; +}; + +class DecimateDifference : public BC_CheckBox +{ +public: + DecimateDifference(Decimate *plugin, + int x, + int y); + int handle_event(); + Decimate *plugin; +}; + +class DecimateAvgDifference : public BC_CheckBox +{ +public: + DecimateAvgDifference(Decimate *plugin, + int x, + int y); + int handle_event(); + Decimate *plugin; +}; + + +class DecimateWindow : public PluginClientWindow +{ +public: + DecimateWindow(Decimate *plugin); + ~DecimateWindow(); + + void create_objects(); + + ArrayList frame_rates; + Decimate *plugin; + DecimateRate *rate; + DecimateRateMenu *rate_menu; + BC_Title *last_dropped; +// DecimateDifference *difference; +// DecimateAvgDifference *avg_difference; +}; + + + + + +class Decimate : public PluginVClient +{ +public: + Decimate(PluginServer *server); + ~Decimate(); + + PLUGIN_CLASS_MEMBERS(DecimateConfig) + + int process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate); + int is_realtime(); + void save_data(KeyFrame *keyframe); + void read_data(KeyFrame *keyframe); + void update_gui(); + void render_gui(void *data); + + int64_t calculate_difference(VFrame *frame1, VFrame *frame2); + void fill_lookahead(double frame_rate, + int64_t start_position); + void decimate_frame(); + void init_fdct(); + void fdct(uint16_t *block); + int64_t calculate_fdct(VFrame *frame); + +// fdct coefficients + double c[8][8]; + int fdct_ready; + +// each difference is the difference between the previous frame and the +// subscripted frame + int64_t differences[TOTAL_FRAMES]; + +// read ahead number of frames + VFrame *frames[TOTAL_FRAMES]; +// Number of frames in the lookahead buffer + int lookahead_size; +// Next position beyond end of lookahead buffer relative to input rate + int64_t lookahead_end; +// Framerate of lookahead buffer + double lookahead_rate; +// Last requested position + int64_t last_position; + +}; + + + + + + + + + + + + +DecimateConfig::DecimateConfig() +{ + input_rate = (double)30000 / 1001; + least_difference = 1; + averaged_frames = 0; +} + +void DecimateConfig::copy_from(DecimateConfig *config) +{ + this->input_rate = config->input_rate; + this->least_difference = config->least_difference; + this->averaged_frames = config->averaged_frames; +} + +int DecimateConfig::equivalent(DecimateConfig *config) +{ + return EQUIV(this->input_rate, config->input_rate); +} + + + + + + + + + +DecimateWindow::DecimateWindow(Decimate *plugin) + : PluginClientWindow(plugin, + 210, + 160, + 200, + 160, + 0) +{ + this->plugin = plugin; +} + +DecimateWindow::~DecimateWindow() +{ + frame_rates.remove_all_objects(); +} + +void DecimateWindow::create_objects() +{ + int x = 10, y = 10; + + frame_rates.append(new BC_ListBoxItem("1")); + frame_rates.append(new BC_ListBoxItem("5")); + frame_rates.append(new BC_ListBoxItem("10")); + frame_rates.append(new BC_ListBoxItem("12")); + frame_rates.append(new BC_ListBoxItem("15")); + frame_rates.append(new BC_ListBoxItem("23.97")); + frame_rates.append(new BC_ListBoxItem("24")); + frame_rates.append(new BC_ListBoxItem("25")); + frame_rates.append(new BC_ListBoxItem("29.97")); + frame_rates.append(new BC_ListBoxItem("30")); + frame_rates.append(new BC_ListBoxItem("50")); + frame_rates.append(new BC_ListBoxItem("59.94")); + frame_rates.append(new BC_ListBoxItem("60")); + + BC_Title *title; + add_subwindow(title = new BC_Title(x, y, _("Input frames per second:"))); + y += 30; + add_subwindow(rate = new DecimateRate(plugin, + this, + x, + y)); + add_subwindow(rate_menu = new DecimateRateMenu(plugin, + this, + x + rate->get_w() + 5, + y)); + y += 30; + add_subwindow(title = new BC_Title(x, y, _("Last frame dropped: "))); + add_subwindow(last_dropped = new BC_Title(x + title->get_w() + 5, y, "")); + +// y += 30; +// add_subwindow(difference = new DecimateDifference(plugin, +// x, +// y)); +// y += 30; +// add_subwindow(avg_difference = new DecimateAvgDifference(plugin, +// x, +// y)); + show_window(); + flush(); +} + + + + + + + + + + + + + +DecimateRate::DecimateRate(Decimate *plugin, + DecimateWindow *gui, + int x, + int y) + : BC_TextBox(x, + y, + 90, + 1, + (float)plugin->config.input_rate) +{ + this->plugin = plugin; + this->gui = gui; +} + +int DecimateRate::handle_event() +{ + plugin->config.input_rate = Units::atoframerate(get_text()); + plugin->send_configure_change(); + return 1; +} + + + +// DecimateDifference::DecimateDifference(Decimate *plugin, +// int x, +// int y) +// : BC_CheckBox(x, y, plugin->config.least_difference, "Drop least difference") +// { +// this->plugin = plugin; +// } +// int DecimateDifference::handle_event() +// { +// plugin->config.least_difference = get_value(); +// plugin->send_configure_change(); +// return 1; +// } +// +// +// +// +// DecimateAvgDifference::DecimateAvgDifference(Decimate *plugin, +// int x, +// int y) +// : BC_CheckBox(x, y, plugin->config.averaged_frames, "Drop averaged frames") +// { +// this->plugin = plugin; +// } +// +// int DecimateAvgDifference::handle_event() +// { +// plugin->config.averaged_frames = get_value(); +// plugin->send_configure_change(); +// return 1; +// } +// + + + +DecimateRateMenu::DecimateRateMenu(Decimate *plugin, + DecimateWindow *gui, + int x, + int y) + : BC_ListBox(x, + y, + 100, + 200, + LISTBOX_TEXT, + &gui->frame_rates, + 0, + 0, + 1, + 0, + 1) +{ + this->plugin = plugin; + this->gui = gui; +} + +int DecimateRateMenu::handle_event() +{ + char *text = get_selection(0, 0)->get_text(); + plugin->config.input_rate = atof(text); + gui->rate->update(text); + plugin->send_configure_change(); + return 1; +} + + + + + + + + + + + + + + + + + + + + + +REGISTER_PLUGIN(Decimate) + + + + + + +Decimate::Decimate(PluginServer *server) + : PluginVClient(server) +{ + + bzero(frames, sizeof(VFrame*) * TOTAL_FRAMES); + for(int i = 0; i < TOTAL_FRAMES; i++) + differences[i] = -1; + lookahead_size = 0; + lookahead_end = -1; + last_position = -1; + fdct_ready = 0; +} + + +Decimate::~Decimate() +{ + + if(frames[0]) + { + for(int i = 0; i < TOTAL_FRAMES; i++) + { + delete frames[i]; + } + } +} + +#define DIFFERENCE_MACRO(type, temp_type, components) \ +{ \ + temp_type result2 = 0; \ + for(int i = 0; i < h; i++) \ + { \ + type *row1 = (type*)frame1->get_rows()[i]; \ + type *row2 = (type*)frame2->get_rows()[i]; \ + for(int j = 0; j < w * components; j++) \ + { \ + temp_type temp = *row1 - *row2; \ + result2 += (temp > 0 ? temp : -temp); \ + row1++; \ + row2++; \ + } \ + } \ + result = (int64_t)result2; \ +} + +int64_t Decimate::calculate_difference(VFrame *frame1, VFrame *frame2) +{ + int w = frame1->get_w(); + int h = frame1->get_h(); + int64_t result = 0; + switch(frame1->get_color_model()) + { + case BC_RGB888: + case BC_YUV888: + DIFFERENCE_MACRO(unsigned char, int64_t, 3); + break; + case BC_RGB_FLOAT: + DIFFERENCE_MACRO(float, double, 3); + break; + case BC_RGBA8888: + case BC_YUVA8888: + DIFFERENCE_MACRO(unsigned char, int64_t, 4); + break; + case BC_RGBA_FLOAT: + DIFFERENCE_MACRO(float, double, 4); + break; + case BC_RGB161616: + case BC_YUV161616: + DIFFERENCE_MACRO(uint16_t, int64_t, 3); + break; + case BC_RGBA16161616: + case BC_YUVA16161616: + DIFFERENCE_MACRO(uint16_t, int64_t, 4); + break; + } + return result; +} + +void Decimate::init_fdct() +{ + int i, j; + double s; + + for (i=0; i<8; i++) + { + s = (i==0) ? sqrt(0.125) : 0.5; + + for (j=0; j<8; j++) + c[i][j] = s * cos((M_PI/8.0)*i*(j+0.5)); + } +} + +void Decimate::fdct(uint16_t *block) +{ + int i, j; + double s; + double tmp[64]; + + for(i = 0; i < 8; i++) + for(j = 0; j < 8; j++) + { + s = 0.0; + +/* + * for(k = 0; k < 8; k++) + * s += c[j][k] * block[8 * i + k]; + */ + s += c[j][0] * block[8 * i + 0]; + s += c[j][1] * block[8 * i + 1]; + s += c[j][2] * block[8 * i + 2]; + s += c[j][3] * block[8 * i + 3]; + s += c[j][4] * block[8 * i + 4]; + s += c[j][5] * block[8 * i + 5]; + s += c[j][6] * block[8 * i + 6]; + s += c[j][7] * block[8 * i + 7]; + + tmp[8 * i + j] = s; + } + + for(j = 0; j < 8; j++) + for(i = 0; i < 8; i++) + { + s = 0.0; + +/* + * for(k = 0; k < 8; k++) + * s += c[i][k] * tmp[8 * k + j]; + */ + s += c[i][0] * tmp[8 * 0 + j]; + s += c[i][1] * tmp[8 * 1 + j]; + s += c[i][2] * tmp[8 * 2 + j]; + s += c[i][3] * tmp[8 * 3 + j]; + s += c[i][4] * tmp[8 * 4 + j]; + s += c[i][5] * tmp[8 * 5 + j]; + s += c[i][6] * tmp[8 * 6 + j]; + s += c[i][7] * tmp[8 * 7 + j]; + + block[8 * i + j] = (int)floor(s + 0.499999); +/* + * reason for adding 0.499999 instead of 0.5: + * s is quite often x.5 (at least for i and/or j = 0 or 4) + * and setting the rounding threshold exactly to 0.5 leads to an + * extremely high arithmetic implementation dependency of the result; + * s being between x.5 and x.500001 (which is now incorrectly rounded + * downwards instead of upwards) is assumed to occur less often + * (if at all) + */ + } +} + + +#define CALCULATE_DCT(type, components) \ +{ \ + uint16_t *output = temp; \ + for(int k = 0; k < 8; k++) \ + { \ + type *input = (type*)frame->get_rows()[i + k] + j * components; \ + for(int l = 0; l < 8; l++) \ + { \ + *output = (*input << 8) | *input; \ + output++; \ + input += components; \ + } \ + } \ + fdct(temp); \ +} + +int64_t Decimate::calculate_fdct(VFrame *frame) +{ + if(!fdct_ready) + { + init_fdct(); + fdct_ready = 1; + } + + uint16_t temp[64]; + uint64_t result[64]; + bzero(result, sizeof(int64_t) * 64); + int w = frame->get_w(); + int h = frame->get_h(); + + + for(int i = 0; i < h - 8; i += 8) + { + for(int j = 0; j < w - 8; j += 8) + { + CALCULATE_DCT(unsigned char, 3) +// Add result to accumulation of transforms + for(int k = 0; k < 64; k++) + { + result[k] += temp[k]; + } + } + } + + uint64_t max_result = 0; + int highest = 0; + for(int i = 0; i < 64; i++) + { + if(result[i] > max_result) + { + max_result = result[i]; + highest = i; + } + } + + return highest; +} + +void Decimate::decimate_frame() +{ + int64_t min_difference = 0x7fffffffffffffffLL; + int result = -1; + + if(!lookahead_size) return; + + for(int i = 0; i < lookahead_size; i++) + { +// Drop least different frame from sequence + if(config.least_difference && + differences[i] >= 0 && + differences[i] < min_difference) + { + min_difference = differences[i]; + result = i; + } + } + +// If all the frames had differences of 0, like a pure black screen, delete +// the first frame. + if(result < 0) result = 0; + + VFrame *temp = frames[result]; + for(int i = result; i < lookahead_size - 1; i++) + { + frames[i] = frames[i + 1]; + differences[i] = differences[i + 1]; + } + + + frames[lookahead_size - 1] = temp; + lookahead_size--; + send_render_gui(&result); +} + +void Decimate::fill_lookahead(double frame_rate, + int64_t start_position) +{ +// Lookahead rate changed + if(!EQUIV(config.input_rate, lookahead_rate)) + { + lookahead_size = 0; + } + + lookahead_rate = config.input_rate; + +// Start position is not contiguous with last request + if(last_position + 1 != start_position) + { + lookahead_size = 0; + } + + last_position = start_position; + +// Normalize requested position to input rate + if(!lookahead_size) + { + lookahead_end = (int64_t)((double)start_position * + config.input_rate / + frame_rate); + } + + while(lookahead_size < TOTAL_FRAMES) + { +// Import frame into next lookahead slot + read_frame(frames[lookahead_size], + 0, + lookahead_end, + config.input_rate, + 0); +// Fill difference buffer + if(lookahead_size > 0) + differences[lookahead_size] = + calculate_difference(frames[lookahead_size - 1], + frames[lookahead_size]); + +// Increase counters relative to input rate + lookahead_size++; + lookahead_end++; + +// Decimate one if last frame in buffer and lookahead_end is behind predicted +// end. + int64_t decimated_end = (int64_t)((double)(start_position + TOTAL_FRAMES) * + config.input_rate / + frame_rate); + if(lookahead_size >= TOTAL_FRAMES && + lookahead_end < decimated_end) + { + decimate_frame(); + } + } +} + + +int Decimate::process_buffer(VFrame *frame, + int64_t start_position, + double frame_rate) +{ + +//printf("Decimate::process_buffer 1 %lld %f\n", start_position, frame_rate); + load_configuration(); + + if(!frames[0]) + { + for(int i = 0; i < TOTAL_FRAMES; i++) + { + frames[i] = new VFrame(0, + -1, + frame->get_w(), + frame->get_h(), + frame->get_color_model(), + -1); + } + } + + +// Fill lookahead buffer at input rate with decimation + fill_lookahead(frame_rate, start_position); + +// printf("Decimate::process_buffer"); +// for(int i = 0; i < TOTAL_FRAMES; i++) +// printf(" %lld", differences[i]); +// printf("\n"); + + +// Pull first frame off lookahead + frame->copy_from(frames[0]); + VFrame *temp = frames[0]; + for(int i = 0; i < TOTAL_FRAMES - 1; i++) + { + frames[i] = frames[i + 1]; + differences[i] = differences[i + 1]; + } + frames[TOTAL_FRAMES - 1] = temp; + lookahead_size--; + return 0; +} + + + +const char* Decimate::plugin_title() { return _("Decimate"); } +int Decimate::is_realtime() { return 1; } + +NEW_WINDOW_MACRO(Decimate, DecimateWindow) + + +int Decimate::load_configuration() +{ + KeyFrame *prev_keyframe; + DecimateConfig old_config; + old_config.copy_from(&config); + prev_keyframe = get_prev_keyframe(get_source_position()); + read_data(prev_keyframe); + return !old_config.equivalent(&config); +} + + +void Decimate::save_data(KeyFrame *keyframe) +{ + FileXML output; + +// cause data to be stored directly in text + output.set_shared_output(keyframe->get_data(), MESSAGESIZE); + output.tag.set_title("DECIMATE"); + output.tag.set_property("INPUT_RATE", config.input_rate); +// output.tag.set_property("AVERAGED_FRAMES", config.averaged_frames); +// output.tag.set_property("LEAST_DIFFERENCE", config.least_difference); + output.append_tag(); + output.tag.set_title("/DECIMATE"); + output.append_tag(); + output.append_newline(); + output.terminate_string(); +} + +void Decimate::read_data(KeyFrame *keyframe) +{ + FileXML input; + + input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data())); + + while(!input.read_tag()) + { + if(input.tag.title_is("DECIMATE")) + { + config.input_rate = input.tag.get_property("INPUT_RATE", config.input_rate); +// config.averaged_frames = input.tag.get_property("AVERAGED_FRAMES", config.averaged_frames); +// config.least_difference = input.tag.get_property("LEAST_DIFFERENCE", config.least_difference); + config.input_rate = Units::fix_framerate(config.input_rate); + } + } +} + +void Decimate::update_gui() +{ + if(thread) + { + if(load_configuration()) + { + ((DecimateWindow*)thread->window)->lock_window("Decimate::update_gui"); + ((DecimateWindow*)thread->window)->rate->update((float)config.input_rate); +// ((DecimateWindow*)thread->window)->difference->update(config.least_difference); +// ((DecimateWindow*)thread->window)->avg_difference->update(config.averaged_frames); + ((DecimateWindow*)thread->window)->unlock_window(); + } + } +} + +void Decimate::render_gui(void *data) +{ + if(thread) + { + ((DecimateWindow*)thread->window)->lock_window("Decimate::render_gui"); + + int dropped = *(int*)data; + char string[BCTEXTLEN]; + + sprintf(string, "%d", dropped); + ((DecimateWindow*)thread->window)->last_dropped->update(string); + + ((DecimateWindow*)thread->window)->unlock_window(); + } +} + + +