diff --git a/c++/iir_blur/iir_blur.cpp b/c++/iir_blur/iir_blur.cpp index be45b26..d083d5e 100644 --- a/c++/iir_blur/iir_blur.cpp +++ b/c++/iir_blur/iir_blur.cpp @@ -1,3 +1,20 @@ +/* + ......... 2016 Ivan Mahonin + + 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 3 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, see . +*/ + #include #include #include @@ -68,14 +85,13 @@ private: vector tmp; public: - int maxi; - double maxv; + int offset; bool valid; double radius; - Checker(double radius): maxi(), maxv(), valid(), radius(radius) { + Checker(double radius): offset(3), valid(), radius(radius) { blank.resize((int)ceil(radius*10)); - tmp.resize(blank.size()); + tmp.resize(blank.size() + 2*offset); for(int i = 0; i < (int)blank.size(); ++i) blank[i] = gauss(i, radius); } @@ -86,82 +102,76 @@ public: double c = 1.0/params.k2; Params p; - p.k0 = -(a*a + 2.0*c*b)/(c*a*a); - p.k1 = (c + 2.0*b)/(c*a*a); - p.k2 = -1.0/(c*a*a); + p.k0 = (a*a + 2.0*c*b)/(c*a*a); + p.k1 = -(c + 2.0*b)/(c*a*a); + p.k2 = 1.0/(c*a*a); return p; } double check2(const Params ¶ms) { if ( abs(params.k0) > 0.99999999 - || abs(params.k1) > 0.99999999 + //|| abs(params.k1) > 0.99999999 || abs(params.k2) > 0.99999999 || params.k0 < 1e-6 || abs(params.k2) < 1e-6 ) return INFINITY; - return check(convert_params(params), true); + return check(convert_params(params)); + //return check(params); } - double check(const Params ¶ms, bool mode3a = true) { + double check(const Params ¶ms) { valid = false; //if (mode3a && fabs(params.k0 + (params.k1 + params.k2)/(params.k0 + params.k1 + params.k2)) >= 0.999999999) // return INFINITY; - if (!mode3a && fabs(params.k1 + params.k2/(params.k1 + params.k2)) >= 0.999999999) - return INFINITY; if (fabs(params.k0) < 1e-10 && fabs(params.k1) < 1e-10 && fabs(params.k2) < 1e-10) return INFINITY; - maxv = 1.0 - params.k0 - params.k1 - params.k2; - int offset = 3; - maxi = offset; - tmp[offset] = 1.0; - for(int i = offset; i < (int)tmp.size(); ++i) { - if (mode3a) - tmp[i] = (i == offset ? maxv : 0.0) - + params.k0*tmp[i-1] - + params.k1*tmp[i-2] - + params.k2*tmp[i-3]; - else - tmp[i] = (i == offset ? 1.0 : 0.0) - + (i-1 == offset ? params.k0 : 0.0) - + params.k1*tmp[i-1] - + params.k2*tmp[i-2]; - if (i < (int)tmp.size()/2 && maxv < tmp[i]) { maxv = tmp[i]; maxi = i; } - } - - //if (maxv < 1e-20 || isnan(maxv)) return INFINITY; - //maxi = (int)round(radius) + offset; + double k = 1.0 - params.k0 - params.k1 - params.k2; + tmp[offset] = k; + for(int i = offset+1, end = (int)tmp.size()-offset; i < end; ++i) + tmp[i] = params.k0*tmp[i-1] + + params.k1*tmp[i-2] + + params.k2*tmp[i-3]; + for(int i = (int)tmp.size()-offset-1, end = offset-1; i > end; --i) + tmp[i] = k*tmp[i] + + params.k0*tmp[i+1] + + params.k1*tmp[i+2] + + params.k2*tmp[i+3]; - //double k = blank[0]/maxv; double delta = 0.0; - for(int i = maxi; i < (int)tmp.size(); ++i) { - double t = tmp[i]; - //double d = blank[i - maxi] - t; - //delta += d*d; - //if (t < 0) delta += 5*d*d; - delta += fabs(blank[i - maxi] - t); - if (t < 0) delta += fabs(t); + //double minv = tmp[offset]; + //double kblank = 1.0/blank[0]; + for(int i = 0; i < (int)blank.size(); ++i) { + double t = tmp[i + offset]; + double d = blank[i] - t; + delta += fabs(d*d); + //if (t < minv) minv = t; + //if (minv < 0) minv = 0; + //delta += 2*fabs(t - minv); + //delta += 0.1*fabs(d/(kblank*blank[i] + 0.1)); } if (isinf(delta) || isnan(delta)) return INFINITY; valid = true; - return delta; + return sqrt(delta/blank.size())*blank.size(); } void graph(const string &filename) { + int base_size = 768; int count = blank.size(); - int scale = 256/count + 1; + int scale = base_size/count + 1; + int div = count/768 + 1; - int pad = max(16, count/8); - int gw = count*scale; + int pad = max(16, count*scale/div/8); + int gw = count*scale/div; int gh = gw; - Surface::Color axis = Surface::convert_color(0, 0, 0, 1); - Surface::Color ca = Surface::convert_color(0, 0, 1, 0.75); - Surface::Color cb = Surface::convert_color(1, 0, 0, 0.75); + Surface::Color axis = Surface::convert_color(0, 0, 1, 1); + Surface::Color ca = Surface::convert_color(1, 0, 0, 0.25); + Surface::Color cb = Surface::convert_color(0, 0, 0, 0.75); Surface s(gw + 2*pad, gh + 2*pad); s.clear(1.0, 1.0, 1.0, 1.0); @@ -174,13 +184,13 @@ public: s.set_color(ca); s.move_to(pad, pad); for(int i = 0; i < count; ++i) - s.line_to(pad + i*scale, pad + (int)round((1.0 - blank[i]/blank[0])*gh)); + s.line_to(pad + i*scale/div, pad + (int)round((1.0 - blank[i]/blank[0])*gh)); if (valid) { s.set_color(cb); s.move_to(pad, pad); - for(int i = 0; i < count - maxi; ++i) - s.line_to(pad + i*scale, pad + (int)round((1.0 - tmp[i + maxi]/maxv)*gh)); + for(int i = 0; i < count; ++i) + s.line_to(pad + i*scale/div, pad + (int)round((1.0 - tmp[i + offset]/blank[0])*gh)); } s.save(filename); @@ -231,11 +241,16 @@ public: if (level < finder->max_report_level) { cout << "level " << level << " "; report_index(); - cout << (open ? " begin" : " end") + for(int i = level; i < finder->max_report_level; ++i) + cout << " "; + cout << (open ? " begin" : " end ") + << fixed + << setprecision(8) << " " << current.k0 << " " << current.k1 << " " << current.k2 << " " << value + << " (" << finder->best << ")" << endl; } } @@ -243,25 +258,48 @@ public: double find() { if (max_variants <= 0) return value; + if (value < finder->best) finder->best = value; report(true); int i[3]; for(i[0] = 0; i[0] < finder->sub_division; ++i[0]) for(i[1] = 0; i[1] < finder->sub_division; ++i[1]) for(i[2] = 0; i[2] < finder->sub_division; ++i[2]) { - Entry sub; - sub.finder = finder; - sub.parent = this; - sub.level = level + 1; - sub.max_variants = max_variants/4; - if (sub.max_variants < 1) sub.max_variants = 1; - for(int j = 0; j < 3; ++j) { - sub.min.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(i[j] + 0.5 - 4.0) + min.k[j]; - sub.current.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(i[j] + 0.5 + 0.0) + min.k[j]; - sub.max.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(i[j] + 0.5 + 4.0) + min.k[j]; + Params p; + for(int j = 0; j < 3; ++j) + p.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(i[j] + 0.5) + min.k[j]; + finder->value(i[0], i[1], i[2]) = finder->checker.check2(p); + } + + finder->clear_zones(); + for(i[0] = 1; i[0] < finder->sub_division-1; ++i[0]) + for(i[1] = 1; i[1] < finder->sub_division-1; ++i[1]) + for(i[2] = 1; i[2] < finder->sub_division-1; ++i[2]) + if (finder->is_local_minimum(i[0], i[1], i[2])) { + Zone &z = finder->zone_for_value(i[0], i[1], i[2]); + double &v = finder->value(i[0], i[1], i[2]); + if (v < z.value) z.set(v, i); + } + + for(i[0] = 0; i[0] < finder->zone_count; ++i[0]) + for(i[1] = 0; i[1] < finder->zone_count; ++i[1]) + for(i[2] = 0; i[2] < finder->zone_count; ++i[2]) { + Zone &z = finder->zone(i[0], i[1], i[2]); + if (!isinf(z.value)) { + Entry sub; + sub.finder = finder; + sub.parent = this; + sub.level = level + 1; + sub.max_variants = max_variants/2; + if (sub.max_variants < 1) sub.max_variants = 1; + for(int j = 0; j < 3; ++j) { + sub.min.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(z.index[j] + 0.5 - finder->zone_size) + min.k[j]; + sub.current.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(z.index[j] + 0.5) + min.k[j]; + sub.max.k[j] = (max.k[j] - min.k[j])/finder->sub_division*(z.index[j] + 0.5 + finder->zone_size) + min.k[j]; + } + sub.value = z.value; + add_variant(sub); } - sub.value = finder->checker.check2(sub.current); - add_variant(sub); } if (level < finder->max_level) { @@ -279,32 +317,59 @@ public: current = variants.begin()->current; } + if (value < finder->best) finder->best = value; report(false); return value; } }; + struct Zone { + double value; + int index[3]; + + Zone() { clear(); } + void clear() { value = INFINITY; memset(index, 0, sizeof(index)); } + void set(double value, int *i) { this->value = value; memcpy(index, i, sizeof(index)); } + }; + Checker checker; int sub_division; + int zone_count; + int zone_size; + int s0; + int s1; + int s2; int max_level; int max_report_level; + vector values; + vector zones; + double best; Entry root; Finder( double radius, Params min, Params max, - int sub_division, + int zone_count, + int zone_size, int max_variants, int max_level, int max_report_level ): checker(radius), - sub_division(sub_division), + sub_division(zone_count*zone_size), + zone_count(zone_count), + zone_size(zone_size), + s0(1), + s1(sub_division), + s2(sub_division*sub_division), max_level(max_level), - max_report_level(max_report_level) + max_report_level(max_report_level), + values(sub_division*sub_division*sub_division), + zones(zone_count*zone_count*zone_count), + best(INFINITY) { root.finder = this; root.min = min; @@ -316,8 +381,30 @@ public: for(int j = 0; j < 3; ++j) root.current.k[j] = 0.5*(root.max.k[j] - root.min.k[j]) + root.min.k[j]; root.value = checker.check2(root.current); + best = root.value; return root.find(); } + + double& value(int i0, int i1, int i2) + { return values[i0*s0 + i1*s1 + i2*s2]; } + + bool is_local_minimum(int i0, int i1, int i2) { + double *vp = &value(i0, i1, i2); + double v = *vp; + return v < *(vp-s0) && v <= *(vp+s0) + && v < *(vp-s1) && v <= *(vp+s1) + && v < *(vp-s2) && v <= *(vp+s2); + } + + Zone& zone_for_value(int i0, int i1, int i2) + { return zones[(i2/zone_size*zone_count + i1/zone_size)*zone_count + i0/zone_size]; } + Zone& zone(int i0, int i1, int i2) + { return zones[(i2*zone_count + i1)*zone_count + i0]; } + + void clear_zones() { + for(vector::iterator i = zones.begin(); i != zones.end(); ++i) + i->clear(); + } }; double find_near(Checker &checker, double step, Params ¶ms) { @@ -338,17 +425,31 @@ double find_near(Checker &checker, double step, Params ¶ms) { return best_value; } +double find_near2(Checker &checker, double step, Params ¶ms) { + Finder finder( + checker.radius, + Params(params.k0-step, params.k1-step, params.k2-step), + Params(params.k0+step, params.k1+step, params.k2+step), + 4, + 4, + 4, + 16, + 1 + ); + finder.find(); + params = finder.root.current; + return finder.root.value; +} + void log_begin() { ofstream f(logfile, ios_base::app); f << endl << "double coefs[][7] = {" << endl; } -void log_params(double radius, int maxi, double maxv, const Params ¶ms, double value) { +void log_params(double radius, const Params ¶ms, double value) { ofstream f(logfile, ios_base::app); f << " { " << radius - << ", " << maxi << setprecision(13) - << ", " << maxv << ", " << params.k0 << ", " << params.k1 << ", " << params.k2 @@ -358,7 +459,7 @@ void log_params(double radius, int maxi, double maxv, const Params ¶ms, doub } void log_params(const Checker &checker, const Params ¶ms, double value) { - log_params(checker.radius, checker.maxi, checker.maxv, params, value); + log_params(checker.radius, params, value); } void log_params(const Finder &finder) { @@ -385,40 +486,51 @@ void graph(Checker &checker, const Params ¶ms) { checker.graph(strprintf("iir_%04d.tga", ri)); } +void test() { + Checker checker(16); + Params p(0.9304775714874, -0.02728819847107, 0.9257968425751); + cout << fixed << setprecision(8) << checker.check2(p) << endl; + checker.graph("test.tga"); +} + void process() { - const double initial_radius = 8.0; - //const double min_radius = 1.0; - //const double max_radius = 2048; - //const double step = 0.1; - //const double params_step = 1e-7; + const double initial_radius = 16.0; + const double min_radius = 1.0; + const double max_radius = 2048; + const double step = 0.1; + const double params_step = 0.05; cout << "find initial params" << endl; Finder finder( initial_radius, - Params( 0.25, -1, -1), - Params( 1, 1, 1), - 256, + Params(0, -1, -1), + Params(1, 1, 1), + 16, + 16, 1, - 3, - 5 + 4, + 8 ); finder.find(); - finder.checker.check2(finder.root.current); + cout << finder.checker.check2(finder.root.current) << endl; + cout << find_near(finder.checker, params_step, finder.root.current) << endl; graph(finder.checker, finder.root.current); log_begin(); log_params(finder); - /* cout << "walk back" << endl; Params params = finder.root.current; for(double r = finder.checker.radius; r > min_radius - 0.5*step; r -= step) { Checker checker(r); - cout << " " << r << flush; + cout << " " << r << endl; - double value = find_near(checker, params_step, params); + double value = find_near2(checker, params_step, params); + checker.check2(params); graph(checker, params); + if (abs(round(r*10) - r*10) < 1e-10) + log_params(checker, params, value); cout << " " << params.k0 << " " << params.k1 @@ -431,10 +543,13 @@ void process() { params = finder.root.current; for(double r = finder.checker.radius; r < max_radius + 0.5*step; r += step) { Checker checker(r); - cout << " " << r << flush; + cout << " " << r << endl; - double value = find_near(checker, params_step, params); + double value = find_near2(checker, params_step, params); graph(checker, params); + checker.check2(params); + if (abs(round(r*10) - r*10) < 1e-10) + log_params(checker, params, value); cout << " " << params.k0 << " " << params.k1 @@ -442,7 +557,6 @@ void process() { << " " << value << endl; } - */ log_end(); } @@ -450,49 +564,6 @@ void process() { int main() { + test(); process(); - - /* - string logfile = "results/coef.log"; - - { ofstream f(logfile.c_str(), ios_base::app); f << endl << "double coefs[][7] = {" << endl; } - - Params prev(0.914, 0.129, -0.216); - for(double r = 4.0; r < 2048.1; r *= pow(2.0, 0.125)) { - cout << endl; - cout << "find coefficients for radius " << r << endl; - - double x = 0.1*log2(r)/2; - - Finder finder( - r, - Params(prev.k0 - x, prev.k1 - x, prev.k2 - x), - Params(prev.k0 + x, prev.k1 + x, prev.k2 + x), - 128, - 1, - 3, - 5 - ); - - finder.find(); - finder.checker.check(finder.root.current); - finder.checker.graph(strprintf("iir_%08.3f.tga", finder.checker.radius)); - - ofstream f(logfile.c_str(), ios_base::app); - f << " { " << finder.checker.radius - << ", " << finder.checker.maxi - << setprecision(20) - << ", " << finder.checker.maxv - << ", " << finder.root.current.k0 - << ", " << finder.root.current.k1 - << ", " << finder.root.current.k2 - << ", " << finder.root.value - << " }," << endl; - cout << endl; - - prev = finder.root.current; - } - - { ofstream f(logfile.c_str(), ios_base::app); f << "};" << endl << endl; } - */ }