#encoding: utf-8
def radius_i( x, y )
return (Math.sqrt( x*x + y*y ) + 0.5 ).ceil;
end
class Canvas
attr_reader :width, :height;
def initialize( width, height )
@width = width; @height = height;
@scr = Array.new( height ){ Array.new( width ){ ' ' } };
print "- 描画サイズは 幅#{@width}x高さ#{@height}です。\n";
end
def draw_circle( a, b, r )
for y in (0 ... @height)
for x in (0 ... @width)
@scr[y][x] = '*' if r == radius_i( x - a, y - b );
end
end
print "- 点(#{a},#{b})に半径#{r}の円を描きました。\n";
end
def get( x, y )
return @scr[y][x];
end
def set( x, y, c )
@scr[y][x] = c;
end
def show
@scr.each do |l|
print l.join, "\n";
end
end
end
def h( canvas )
wdt = canvas.width; hgt = canvas.height;
rmax = (wdt < hgt ? wdt : hgt) / 2 + 1;
rmax2 = rmax * 2 + 1;
rmin = 3;
htbl_ndots_by_r = Array.new( rmax ){ 0 };
htbl = Array.new( rmax2 ){ Array.new( rmax2 ){ 0 } };
for x in ( 0 ... rmax2 )
for y in ( 0 ... rmax2 )
r = radius_i( x - rmax, y - rmax );
if rmin < r && r < rmax then
htbl_ndots_by_r[ r ] += 1;
htbl[ x ][ y ] = r;
else
htbl[ x ][ y ] = -1;
end
end
end
for r in ( 0 ... rmax )
if rmin < r then
htbl_ndots_by_r[r] -= htbl_ndots_by_r[r] / 10;
else
htbl_ndots_by_r[r] = 1 << 31 - 1;
end
end
counter = Array.new( wdt ){ Array.new( hgt ){ Array.new( rmax ){ 0 } } };
cnt_loop = 0;
for x in ( 0 ... wdt )
for y in ( 0 ... hgt )
next if canvas.get( x, y ) == ' ';
for u in ( -rmax ... rmax )
for v in ( -rmax ... rmax )
cnt_loop += 1;
x2 = x + u; y2 = y + v;
if 0 <= x2 && x2 < wdt && 0 <= y2 && y2 < hgt then
r = htbl[ u + rmax ][ v + rmax ];
counter[x2][y2][r] += 1 if r != -1;
end
end
end
end
end
print 'Hough tranform fin - ', cnt_loop , " looped.\n";
cnt_loop = 0;
for x in ( 0 ... wdt )
for y in ( 0 ... hgt )
for r in ( 0 ... rmax )
cnt_loop += 1;
if counter[x][y][r] >= htbl_ndots_by_r[ r ] then
print "+ Circle found: (#{x},#{y}) r=#{r}\n";
canvas.set( x, y, '@' );
end
end
end
end
print 'Inverse fin - ', cnt_loop , " looped.\n";
end
$C = Canvas.new( 170, 60 );
$C.draw_circle( 42, 29, 20 );
$C.draw_circle( 40, 18, 4 );
$C.draw_circle( 60, 22, 8 );
h $C;
$C.show;
I2VuY29kaW5nOiB1dGYtOApkZWYgcmFkaXVzX2koIHgsIHkgKQogIHJldHVybiAoTWF0aC5zcXJ0KCB4KnggKyB5KnkgKSArIDAuNSApLmNlaWw7CmVuZCAgICAgICAgCmNsYXNzIENhbnZhcwogIGF0dHJfcmVhZGVyIDp3aWR0aCwgOmhlaWdodDsKICBkZWYgaW5pdGlhbGl6ZSggd2lkdGgsIGhlaWdodCApCiAgICBAd2lkdGggPSB3aWR0aDsgQGhlaWdodCA9IGhlaWdodDsKICAgIEBzY3IgPSBBcnJheS5uZXcoIGhlaWdodCApeyBBcnJheS5uZXcoIHdpZHRoICl7ICcgJyB9IH07CiAgICBwcmludCAiLSDmj4/nlLvjgrXjgqTjgrrjga8g5bmFI3tAd2lkdGh9eOmrmOOBlSN7QGhlaWdodH3jgafjgZnjgIJcbiI7CiAgZW5kCiAgZGVmIGRyYXdfY2lyY2xlKCBhLCBiLCByICkKICAgIGZvciB5IGluICgwIC4uLiBAaGVpZ2h0KQogICAgICBmb3IgeCBpbiAoMCAuLi4gQHdpZHRoKQogICAgICAgIEBzY3JbeV1beF0gPSAnKicgaWYgciA9PSByYWRpdXNfaSggeCAtIGEsIHkgLSBiICk7CiAgICAgIGVuZAogICAgZW5kCiAgICBwcmludCAiLSDngrkoI3thfSwje2J9KeOBq+WNiuW+hCN7cn3jga7lhobjgpLmj4/jgY3jgb7jgZfjgZ/jgIJcbiI7CiAgZW5kCiAgZGVmIGdldCggeCwgeSApCiAgICByZXR1cm4gQHNjclt5XVt4XTsKICBlbmQKICBkZWYgc2V0KCB4LCB5LCBjICkKICAgIEBzY3JbeV1beF0gPSBjOwogIGVuZAogIGRlZiBzaG93CiAgICBAc2NyLmVhY2ggZG8gfGx8CiAgICAgcHJpbnQgbC5qb2luLCAiXG4iOwogICAgZW5kCiAgZW5kCmVuZAoKZGVmIGgoIGNhbnZhcyApCiAgd2R0ID0gY2FudmFzLndpZHRoOyBoZ3QgPSBjYW52YXMuaGVpZ2h0OwogIHJtYXggPSAod2R0IDwgaGd0ID8gd2R0IDogaGd0KSAvIDIgKyAxOwogIHJtYXgyID0gcm1heCAqIDIgKyAxOwogIHJtaW4gPSAzOwogIGh0YmxfbmRvdHNfYnlfciA9IEFycmF5Lm5ldyggcm1heCApeyAwIH07CiAgaHRibCA9IEFycmF5Lm5ldyggcm1heDIgKXsgQXJyYXkubmV3KCBybWF4MiApeyAwIH0gfTsKICBmb3IgeCBpbiAoIDAgLi4uIHJtYXgyICkKICAgIGZvciB5IGluICggMCAuLi4gcm1heDIgKQogICAgICByID0gcmFkaXVzX2koIHggLSBybWF4LCB5IC0gcm1heCApOwogICAgICBpZiBybWluIDwgciAmJiByIDwgcm1heCB0aGVuCiAgICAgICAgaHRibF9uZG90c19ieV9yWyByIF0gKz0gMTsKICAgICAgICBodGJsWyB4IF1bIHkgXSA9IHI7CiAgICAgIGVsc2UKICAgICAgICBodGJsWyB4IF1bIHkgXSA9IC0xOwogICAgICBlbmQKICAgIGVuZAogIGVuZAogIGZvciByIGluICggMCAuLi4gcm1heCApCiAgICBpZiBybWluIDwgciB0aGVuCiAgICAgIGh0YmxfbmRvdHNfYnlfcltyXSAtPSBodGJsX25kb3RzX2J5X3Jbcl0gLyAxMDsKICAgIGVsc2UKICAgICAgaHRibF9uZG90c19ieV9yW3JdID0gMSA8PCAzMSAtIDE7CiAgICBlbmQKICBlbmQKICBjb3VudGVyID0gQXJyYXkubmV3KCB3ZHQgKXsgQXJyYXkubmV3KCBoZ3QgKXsgQXJyYXkubmV3KCBybWF4ICl7IDAgfSB9IH07CiAgY250X2xvb3AgPSAwOwogIGZvciB4IGluICggMCAuLi4gd2R0ICkKICAgIGZvciB5IGluICggMCAuLi4gaGd0ICkKICAgICAgbmV4dCBpZiBjYW52YXMuZ2V0KCB4LCB5ICkgPT0gJyAnOwogICAgICBmb3IgdSBpbiAoIC1ybWF4IC4uLiBybWF4ICkKICAgICAgICBmb3IgdiBpbiAoIC1ybWF4IC4uLiBybWF4ICkKICAgICAgICAgIGNudF9sb29wICs9IDE7CiAgICAgICAgICB4MiA9IHggKyB1OyB5MiA9IHkgKyB2OwogICAgICAgICAgaWYgMCA8PSB4MiAmJiB4MiA8IHdkdCAmJiAwIDw9IHkyICYmIHkyIDwgaGd0IHRoZW4KICAgICAgICAgICAgciA9IGh0YmxbIHUgKyBybWF4IF1bIHYgKyBybWF4IF07CiAgICAgICAgICAgIGNvdW50ZXJbeDJdW3kyXVtyXSArPSAxIGlmIHIgIT0gLTE7CiAgICAgICAgICBlbmQKICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKICBwcmludCAnSG91Z2ggdHJhbmZvcm0gZmluIC0gJywgY250X2xvb3AgLCAiIGxvb3BlZC5cbiI7CiAgY250X2xvb3AgPSAwOwogIGZvciB4IGluICggMCAuLi4gd2R0ICkKICAgIGZvciB5IGluICggMCAuLi4gaGd0ICkKICAgICAgZm9yIHIgaW4gKCAwIC4uLiBybWF4ICkKICAgICAgICBjbnRfbG9vcCArPSAxOwogICAgICAgIGlmIGNvdW50ZXJbeF1beV1bcl0gPj0gaHRibF9uZG90c19ieV9yWyByIF0gdGhlbgogICAgICAgICAgcHJpbnQgIisgQ2lyY2xlIGZvdW5kOiAoI3t4fSwje3l9KSByPSN7cn1cbiI7CiAgICAgICAgICBjYW52YXMuc2V0KCB4LCB5LCAnQCcgKTsKICAgICAgICBlbmQKICAgICAgZW5kCiAgICBlbmQKICBlbmQKICBwcmludCAnSW52ZXJzZSBmaW4gLSAnLCBjbnRfbG9vcCAsICIgbG9vcGVkLlxuIjsKZW5kCiRDID0gQ2FudmFzLm5ldyggMTcwLCA2MCApOwokQy5kcmF3X2NpcmNsZSggNDIsIDI5LCAyMCApOwokQy5kcmF3X2NpcmNsZSggNDAsIDE4LCAgNCApOwokQy5kcmF3X2NpcmNsZSggNjAsIDIyLCAgOCApOwpoICRDOwokQy5zaG93Owo=