X-Git-Url: https://git.cinelerra-gg.org/git/?a=blobdiff_plain;f=cinelerra-5.1%2Fplugins%2Fdescratch%2Fdescratch.C;h=ba6ea71b1032a85f957874bd0aa603516c5f66fa;hb=a2d9ab21fbb8e29310d9cd0be439522d05d0d0ba;hp=8375510f0c242b4f446bca16f7cf2427a8bda91f;hpb=1c43268ae0d6d6fad8beff33946e1115a8ec34b2;p=goodguy%2Fhistory.git diff --git a/cinelerra-5.1/plugins/descratch/descratch.C b/cinelerra-5.1/plugins/descratch/descratch.C index 8375510f..ba6ea71b 100644 --- a/cinelerra-5.1/plugins/descratch/descratch.C +++ b/cinelerra-5.1/plugins/descratch/descratch.C @@ -8,6 +8,7 @@ This program is FREE software under GPL licence v2. This plugin removes vertical scratches from digitized films. Reworked for cin5 by GG. 03/2018, from the laws of Fizick's +Adapted strategy to mark, test, draw during port. */ @@ -16,6 +17,8 @@ Reworked for cin5 by GG. 03/2018, from the laws of Fizick's #include "language.h" #include "descratch.h" +#include + REGISTER_PLUGIN(DeScratchMain) DeScratchMain::DeScratchMain(PluginServer *server) @@ -23,8 +26,7 @@ DeScratchMain::DeScratchMain(PluginServer *server) { inf = 0; sz_inf = 0; src = 0; dst = 0; - tmp_frame = 0; - blurry = 0; + tmpy = 0; blury = 0; overlay_frame = 0; } @@ -33,8 +35,8 @@ DeScratchMain::~DeScratchMain() delete [] inf; delete src; delete dst; - delete blurry; - delete tmp_frame; + delete blury; + delete tmpy; delete overlay_frame; } @@ -43,21 +45,22 @@ int DeScratchMain::is_realtime() { return 1; } void DeScratchConfig::reset() { - threshold = 24; - asymmetry = 16; + threshold = 12; + asymmetry = 25; min_width = 1; - max_width = 3; - min_len = 1; + max_width = 2; + min_len = 10; max_len = 100; - max_angle = 45; - blur_len = 4; - gap_len = 10; + max_angle = 5; + blur_len = 2; + gap_len = 0; mode_y = MODE_ALL; mode_u = MODE_NONE; mode_v = MODE_NONE; mark = 0; ffade = 100; border = 2; + edge_only = 0; } DeScratchConfig::DeScratchConfig() @@ -85,7 +88,8 @@ int DeScratchConfig::equivalent(DeScratchConfig &that) mode_v == that.mode_v && mark == that.mark && ffade == that.ffade && - border == that.border; + border == that.border && + edge_only == that.edge_only; } void DeScratchConfig::copy_from(DeScratchConfig &that) { @@ -104,6 +108,7 @@ void DeScratchConfig::copy_from(DeScratchConfig &that) mark = that.mark; ffade = that.ffade; border = that.border; + edge_only = that.edge_only; } void DeScratchConfig::interpolate(DeScratchConfig &prev, DeScratchConfig &next, @@ -136,6 +141,7 @@ void DeScratchMain::save_data(KeyFrame *keyframe) output.tag.set_property("MARK", config.mark); output.tag.set_property("FFADE", config.ffade); output.tag.set_property("BORDER", config.border); + output.tag.set_property("EDGE_ONLY", config.edge_only); output.append_tag(); output.tag.set_title("/DESCRATCH"); output.append_tag(); @@ -153,7 +159,7 @@ void DeScratchMain::read_data(KeyFrame *keyframe) while( !(result = input.read_tag()) ) { if(input.tag.title_is("DESCRATCH")) { config.threshold = input.tag.get_property("THRESHOLD", config.threshold); - config.asymmetry = input.tag.get_property("ASYMMETRY", config.asymmetry); + config.asymmetry = input.tag.get_property("ASYMMETRY", config.asymmetry); config.min_width = input.tag.get_property("MIN_WIDTH", config.min_width); config.max_width = input.tag.get_property("MAX_WIDTH", config.max_width); config.min_len = input.tag.get_property("MIN_LEN", config.min_len); @@ -167,100 +173,64 @@ void DeScratchMain::read_data(KeyFrame *keyframe) config.mark = input.tag.get_property("MARK", config.mark); config.ffade = input.tag.get_property("FFADE", config.ffade); config.border = input.tag.get_property("BORDER", config.border); + config.edge_only = input.tag.get_property("EDGE_ONLY", config.edge_only); } } } -void DeScratchMain::get_extrems_plane(int comp, int thresh) +void DeScratchMain::set_extrems_plane(int width, int comp, int thresh) { - uint8_t **rows = blurry->get_rows(); - int d = config.max_width, d1 = d+1, wd = src_w - d1; - int bpp = 3, dsz = d * bpp; - int asym = config.asymmetry; + uint8_t **rows = blury->get_rows(); + int r = width, r1 = r+1, wd = src_w - r1; + int bpp = 3, dsz = r * bpp; + int asym = config.asymmetry * 256 / 100; if( thresh > 0 ) { // black (low value) scratches for( int y=0; y thresh && (rp[0]-*dp) > thresh && - (abs(lp[-bpp]-rp[+bpp]) <= asym) && - ((lp[0]-lp[+bpp]) + (rp[0]-rp[-bpp]) > - (lp[-bpp]-lp[0]) + (rp[bpp]-rp[0])) ? - SD_EXTREM : SD_NULL; // sharp extremum found - } - for( ; xget_rows(); - int d = config.min_width, d1 = d+1, wd = src_w - d1; - int bpp = 3, dsz = d * bpp; - int asym = config.asymmetry; - if( thresh > 0 ) { // black (low value) scratches - for( int y=0; y thresh && (rp[0]-*dp) > thresh && - (abs(lp[-bpp]-rp[+bpp]) <= asym) && - ((lp[0]-lp[+bpp]) + (rp[0]-rp[-bpp]) > - (lp[-bpp]-lp[0]) + (rp[bpp]-rp[0])) ) - *ip = SD_NULL; // sharp extremum found + (abs(lp[-bpp]-rp[+bpp]) <= asym) && + ((lp[0]-lp[+bpp]) + (rp[0]-rp[-bpp]) > + (lp[-bpp]-lp[0]) + (rp[bpp]-rp[0])) ) { + *ip = SD_EXTREM; + for( int i=1; i0; ) *(bp-=src_w) = SD_EXTREM; + if( !(*ip & SD_EXTREM) ) continue; + uint8_t *bp = ip, b = *bp; // expand to previous lines in range + int i = len < y ? len : y; + while( --i>=0 ) *(bp-=src_w) = b; } } } @@ -270,153 +240,140 @@ void DeScratchMain::test_scratches() int w2 = src_w - 2; int min_len = config.min_len * src_h / 100; int max_len = config.max_len * src_h / 100; - int maxwidth = config.max_width*2 + 1; - int maxangle = config.max_angle; + int maxwidth = config.max_width; + float sin_mxa = sin(config.max_angle * M_PI/180.); for( int y=0; y= 3 ) { - if( ip[-2] == SD_EXTREM ) { ip[-2] = SD_TESTED; nctr = ctr-2; ++n; } - if( ip[+2] == SD_EXTREM ) { ip[+2] = SD_TESTED; nctr = ctr+2; ++n; } - } - if( ip[-1] == SD_EXTREM ) { ip[-1] = SD_TESTED; nctr = ctr-1; ++n; } - if( ip[+1] == SD_EXTREM ) { ip[+1] = SD_TESTED; nctr = ctr+1; ++n; } - if( ip[+0] == SD_EXTREM ) { ip[+0] = SD_TESTED; nctr = ctr+0; ++n; } - // end of points tests, check result for row: - // check gap and angle, if no points or big angle, it is end of inf - if( !n || abs(nctr%src_w - x) >= maxwidth+len*maxangle/57 ) break; - ctr = nctr + src_w; // new center for next row test + if( ip[-1] & SD_EXTREM ) { ip[-1] |= SD_TESTED; nctr = ctr-1; ++n; } + if( ip[+1] & SD_EXTREM ) { ip[+1] |= SD_TESTED; nctr = ctr+1; ++n; } + if( ip[+0] & SD_EXTREM ) { ip[+0] |= SD_TESTED; nctr = ctr+0; ++n; } + if( !n ) break; + ctr = nctr + src_w; // new center for next row test } - int mask = len >= min_len && len <= max_len ? SD_GOOD : SD_REJECT; - ctr = ofs+1; nctr = ctr; // pass2 + int v = (!config.edge_only || y == 0 || y+len>=src_h) && + abs(nctr%src_w - x) < maxwidth + len*sin_mxa && + len >= min_len && len <= max_len ? SD_GOOD : SD_REJECT; + ctr = ofs; nctr = ctr; // pass2 for( len=0; len= 3 ) { - if( ip[-2] == SD_TESTED ) { ip[-2] = mask; nctr = ctr-2; ++n; } - if( ip[+2] == SD_TESTED ) { ip[+2] = mask; nctr = ctr+2; ++n; } - } - if( ip[-1] == SD_TESTED ) { ip[-1] = mask; nctr = ctr-1; ++n; } - if( ip[+1] == SD_TESTED ) { ip[+1] = mask; nctr = ctr+1; ++n; } - if( ip[+0] == SD_TESTED ) { ip[+0] = mask; nctr = ctr+0; ++n; } - // end of points tests, check result for row: - // check gap and angle, if no points or big angle, it is end of inf - if( !n || abs(nctr%src_w - x) >= maxwidth+len*maxangle/57 ) break; - ctr = nctr + src_w; // new center for next row test + if( ip[-1] & SD_TESTED ) { ip[-1] = v; nctr = ctr-1; ++n; } + if( ip[+1] & SD_TESTED ) { ip[+1] = v; nctr = ctr+1; ++n; } + if( ip[+0] & SD_TESTED ) { ip[+0] = v; nctr = ctr+0; ++n; } + if( !n ) break; + ctr = nctr + src_w; // new center for next row test } } } } -void DeScratchMain::mark_scratches_plane(int comp, int mask, int value) +void DeScratchMain::mark_scratches_plane() { int bpp = 3, dst_w = dst->get_w(), dst_h = dst->get_h(); uint8_t **rows = dst->get_rows(); for( int y=0; y 0 ? grn_yuv[comp] : red_yuv[comp]; + else if( *ip & SD_REJECT ) + *dp = ylw_yuv[comp]; + } } } } void DeScratchMain::remove_scratches_plane(int comp) { - int r = config.max_width; - int fade = (config.ffade * 1024) / 100; // norm 2^10 - int fade1 = 1024 - fade; + int bpp = 3, w1 = src_w-1; + int border = config.border; uint8_t *ip = inf; - uint8_t **src_rows = src->get_rows(); uint8_t **dst_rows = dst->get_rows(); - uint8_t **blur_rows = blurry->get_rows(); - int bpp = 3, margin = r+config.border+2, wm = src_w-margin; - float nrm = 1. / 1024.f, nrm2r = nrm / (2*r*bpp); + uint8_t **blur_rows = blury->get_rows(); + float a = config.ffade / 100, b = 1 - a; for( int y=0; yrs; i-=bpp ) { // at right border - int rv = inp[i] + blur[rt] - blur[i]; - int v = nrm*(fade*rv + fade1*inp[rt]); - out[i] = CLIP(v,0,255); - } - left = 0; + if( !(dp[-1]&SD_GOOD) && (dp[0]&SD_GOOD) ) left = x; + if( left < 0 || !(dp[0]&SD_GOOD) || (dp[1]&SD_GOOD) ) continue; + int right = x; + int ctr = (left + right) / 2; // scratch center + int r = (right - left + border) / 2 + 1; + left = 0; + int ls = ctr - r, rs = ctr + r; // scratch edges + int lt = ls - border, rt = rs + border; // border edges + if( ls < 0 ) ls = 0; + if( rs > w1 ) rs = w1; + if( lt < 0 ) lt = 0; + if( rt > w1 ) rt = w1; + ls *= bpp; rs *= bpp; + lt *= bpp; rt *= bpp; + if( rs > ls ) { + float s = 1. / (rs - ls); + for( int i=ls; (i+=bpp) lt ) { + float s = 1. / (ls - lt); + for( int i=lt; (i+=bpp)<=ls; ) { // at left border + int lv = a * out[lt] + b * out[i]; + int rv = a * blur[i] + b * out[i]; + int v = s * (lv*(ls-i) + rv*(i-lt)); + out[i] = CLIP(v, 0, 255); + } + } + if( rt > rs ) { + float s = 1. / (rt - rs); + for( int i=rt; (i-=bpp)>=rs; ) { // at right border + int lv = a * blur[i] + b * out[i]; + int rv = a * out[rt] + b * out[i]; + int v = s * (rv*(i-rs) + lv*(rt-i)); + out[i] = CLIP(v, 0, 255); + } } } ip += src_w; } } -void DeScratchMain::pass(int comp, int thresh) -{ -// pass for current plane and current sign - get_extrems_plane(comp, thresh); - if( config.min_width > 1 ) - remove_min_extrems_plane(comp, thresh); - close_gaps(); - test_scratches(); - if( config.mark ) { - int value = config.threshold > 0 ? 0 : 255; - mark_scratches_plane(comp, SD_GOOD, value); - mark_scratches_plane(comp, SD_REJECT, 127); - } - else - remove_scratches_plane(comp); -} - void DeScratchMain::blur(int scale) { - int tw = src_w, th = (src_h / scale) & ~1; - if( tmp_frame && - (tmp_frame->get_w() != tw || tmp_frame->get_h() != th) ) { - delete tmp_frame; tmp_frame = 0; + int th = (src_h / scale) & ~1; + if( tmpy&& (tmpy->get_w() != src_w || tmpy->get_h() != th) ) { + delete tmpy; tmpy= 0; } - if( !tmp_frame ) - tmp_frame = new VFrame(tw, th, BC_YUV888); - - if( blurry && - (blurry->get_w() != src_w || blurry->get_h() != src_h) ) { - delete blurry; blurry = 0; + if( !tmpy ) tmpy = new VFrame(src_w, th, BC_YUV888); + if( blury && (blury->get_w() != src_w || blury->get_h() != src_h) ) { + delete blury; blury= 0; } - if( !blurry ) - blurry = new VFrame(src_w, src_h, BC_YUV888); - - overlay_frame->overlay(tmp_frame, src, - 0,0,src_w,src_h, 0,0,tw,th, 1.f, TRANSFER_NORMAL, LINEAR_LINEAR); - overlay_frame->overlay(blurry, tmp_frame, - 0,0,tw,th, 0,0,src_w,src_h, 1.f, TRANSFER_NORMAL, CUBIC_CUBIC); + if( !blury ) blury = new VFrame(src_w, src_h, BC_YUV888); + overlay_frame->overlay(tmpy, src, + 0,0,src_w,src_h, 0,0,src_w,th, 1.f, TRANSFER_NORMAL, LINEAR_LINEAR); + overlay_frame->overlay(blury, tmpy, + 0,0,src_w,th, 0,0,src_w,src_h, 1.f, TRANSFER_NORMAL, CUBIC_CUBIC); } void DeScratchMain::copy(int comp) @@ -429,6 +386,15 @@ void DeScratchMain::copy(int comp) } } +void DeScratchMain::pass(int comp, int thresh) +{ +// pass for current plane and current sign + int w0 = config.min_width, w1 = config.max_width; + if( w1 < w0 ) w1 = w0; + for( int iw=w0; iw<=w1; ++iw ) + set_extrems_plane(iw, comp, thresh); +} + void DeScratchMain::plane_pass(int comp, int mode) { int threshold = config.threshold; @@ -445,6 +411,12 @@ void DeScratchMain::plane_pass(int comp, int mode) } } +void DeScratchMain::plane_proc(int comp, int mode) +{ + if( mode == MODE_NONE ) return; + remove_scratches_plane(comp); +} + int DeScratchMain::process_realtime(VFrame *input, VFrame *output) { load_configuration(); @@ -470,9 +442,19 @@ int DeScratchMain::process_realtime(VFrame *input, VFrame *output) if( sz_inf != sz ) { delete [] inf; inf = 0; } if( !inf ) inf = new uint8_t[sz_inf=sz]; blur(config.blur_len + 1); + memset(inf, SD_NULL, sz_inf); plane_pass(0, config.mode_y); plane_pass(1, config.mode_u); plane_pass(2, config.mode_v); + close_gaps(); + test_scratches(); + if( !config.mark ) { + plane_proc(0, config.mode_y); + plane_proc(1, config.mode_u); + plane_proc(2, config.mode_v); + } + else + mark_scratches_plane(); output->transfer_from(dst); } return 0; @@ -492,7 +474,7 @@ NEW_WINDOW_MACRO(DeScratchMain, DeScratchWindow) DeScratchWindow::DeScratchWindow(DeScratchMain *plugin) - : PluginClientWindow(plugin, 512, 256, 512, 256, 0) + : PluginClientWindow(plugin, 512, 270, 512, 270, 0) { this->plugin = plugin; } @@ -509,20 +491,24 @@ void DeScratchWindow::create_objects() BC_Title *title; add_tool(title = new BC_Title(x, y, _("DeScratch:"))); - y += title->get_h() + 5; + + int w1 = DeScratchReset::calculate_w(this, _("Reset")); + add_tool(reset = new DeScratchReset(this, get_w()-w1-15, y)); + + y += title->get_h() + 15; int x1 = x, x2 = get_w()/2; add_tool(title = new BC_Title(x1=x, y, _("threshold:"))); x1 += title->get_w()+16; add_tool(threshold = new DeScratchISlider(this, x1, y, x2-x1-10, 0,64, &config.threshold)); add_tool(title = new BC_Title(x1=x2, y, _("asymmetry:"))); x1 += title->get_w()+16; - add_tool(asymmetry = new DeScratchISlider(this, x1, y, get_w()-x1-15, 0,64, &config.asymmetry)); + add_tool(asymmetry = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0,100., &config.asymmetry)); y += threshold->get_h() + 10; add_tool(title = new BC_Title(x1=x, y, _("Mode:"))); x1 += title->get_w()+16; add_tool(title = new BC_Title(x1, y, _("y:"))); - int w1 = title->get_w()+16; + w1 = title->get_w()+16; add_tool(y_mode = new DeScratchMode(this, (x1+=w1), y, &config.mode_y)); y_mode->create_objects(); x1 += y_mode->get_w()+16; add_tool(title = new BC_Title(x1, y, _("u:"))); @@ -537,10 +523,10 @@ void DeScratchWindow::create_objects() w1 = title->get_w()+16; x1 += w1; add_tool(title = new BC_Title(x1, y, _("min:"))); x1 += title->get_w()+16; - add_tool(min_width = new DeScratchISlider(this, x1, y, x2-x1-10, 0,16, &config.min_width)); + add_tool(min_width = new DeScratchISlider(this, x1, y, x2-x1-10, 1,16, &config.min_width)); add_tool(title = new BC_Title(x1=x2, y, _("max:"))); x1 += title->get_w()+16; - add_tool(max_width = new DeScratchISlider(this, x1, y, get_w()-x1-15, 0,16, &config.max_width)); + add_tool(max_width = new DeScratchISlider(this, x1, y, get_w()-x1-15, 1,16, &config.max_width)); y += min_width->get_h() + 10; add_tool(title = new BC_Title(x1=x, y, _("len:"))); @@ -557,7 +543,7 @@ void DeScratchWindow::create_objects() w1 = title->get_w()+16; x1 += w1; add_tool(title = new BC_Title(x1, y, _("blur:"))); x1 += title->get_w()+16; - add_tool(blur_len = new DeScratchISlider(this, x1, y, x2-x1-10, 0,16, &config.blur_len)); + add_tool(blur_len = new DeScratchISlider(this, x1, y, x2-x1-10, 0,8, &config.blur_len)); add_tool(title = new BC_Title(x1=x2, y, _("gap:"))); x1 += title->get_w()+16; add_tool(gap_len = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0.0,100.0, &config.gap_len)); @@ -565,7 +551,7 @@ void DeScratchWindow::create_objects() add_tool(title = new BC_Title(x1=x, y, _("max angle:"))); w1 = title->get_w()+16; x1 += w1; - add_tool(max_angle = new DeScratchFSlider(this, x1, y, x2-x1-10, 0.0,90.0, &config.max_angle)); + add_tool(max_angle = new DeScratchFSlider(this, x1, y, x2-x1-10, 0.0,15.0, &config.max_angle)); add_tool(title = new BC_Title(x1=x2, y, _("fade:"))); x1 += title->get_w()+16; add_tool(ffade = new DeScratchFSlider(this, x1, y, get_w()-x1-15, 0.0,100.0, &config.ffade)); @@ -575,8 +561,8 @@ void DeScratchWindow::create_objects() x1 += title->get_w()+16; add_tool(border = new DeScratchISlider(this, x1, y, x2-x1-10, 0,16, &config.border)); add_tool(mark = new DeScratchMark(this, x1=x2, y)); - w1 = DeScratchReset::calculate_w(this, _("Reset")); - add_tool(reset = new DeScratchReset(this, get_w()-w1-15, y)); + x1 += mark->get_w() + 10; + add_tool(edge_only = new DeScratchEdgeOnly(this, x1, y)); show_window(); } @@ -584,18 +570,22 @@ void DeScratchWindow::create_objects() void DeScratchWindow::update_gui() { DeScratchConfig &config = plugin->config; - y_mode->update(config.mode_y); - u_mode->update(config.mode_u); - v_mode->update(config.mode_v); + threshold->update(config.threshold); + asymmetry->update(config.asymmetry); min_width->update(config.min_width); max_width->update(config.max_width); min_len->update(config.min_len); max_len->update(config.max_len); + max_angle->update(config.max_angle); blur_len->update(config.blur_len); gap_len->update(config.gap_len); - max_angle->update(config.max_angle); - ffade->update(config.ffade); + y_mode->update(config.mode_y); + u_mode->update(config.mode_u); + v_mode->update(config.mode_v); mark->update(config.mark); + ffade->update(config.ffade); + border->update(config.border); + edge_only->update(config.edge_only); } @@ -709,6 +699,23 @@ int DeScratchMark::handle_event() return ret; } +DeScratchEdgeOnly::DeScratchEdgeOnly(DeScratchWindow *win, int x, int y) + : BC_CheckBox(x, y, &win->plugin->config.edge_only, _("Edge")) +{ + this->win = win; +}; + +DeScratchEdgeOnly::~DeScratchEdgeOnly() +{ +} + +int DeScratchEdgeOnly::handle_event() +{ + int ret = BC_CheckBox::handle_event(); + win->plugin->send_configure_change(); + return ret; +} + DeScratchReset::DeScratchReset(DeScratchWindow *win, int x, int y) : BC_GenericButton(x, y, _("Reset")) {