#@cli chough : radius>0,_BackgroundNoiseElim(15)>0,_precision(2)>=1,0<=_decimate(0.875)<=1,0.1<=_sensitivity(0.9)<1,_verbose(False) #@cli : Select image(s) to be scanned for circular-like artifacts, via #@cli : Circular Hough. 'radius' sets the scan target pixel radius #@cli : (required). Replace selected images with 1xN vector array lists of the #@cli : N discovered center points of matching circles, along with the #@cli : specified target radius parameter, these data are for the benefit of #@cli : downstream mask plotters. Increasing 'BackgroundNoiseElim' favors #@cli : edges, but faint edges may be taken as noise and #@cli : eliminated. Increasing 'precision' sizes an internal 'parameter space' #@cli : image. It is a multiplier that scales parameter space upward from the #@cli : selected input image. Multipliers of 2-4 are less susceptible noise #@cli : but consumes memory. 'decimate' reduces an internally generated #@cli : pointcloud of candidate center points. Unity decimates the entire #@cli : pointcloud (including solutions); zero leaves it intact, resulting in #@cli : excessive plotting that can obscure solutions. The pointcloud tends to #@cli : be highly redundant; decimations of 0.7 -> 0.9 are typical. Use #@cli : smaller values (getting more points) for faint or noisy #@cli : images. 'sensitivity' cuts all but the most likely solutions of #@cli : clusters around potential center points; 0.9 is typical. Use lower (less #@cli : sensitive) values to capture less likely center points, increase to #@cli : reduce false solutions. Try to get 'radius' correct before adjusting #@cli : 'sensitivity'. 'verbose' turns on intermediary displays of internal #@cli : images and dumps of trial lists of center points. #@cli : $ chough[imagetocheck] 55 -if h repeat {h} a='{I(0,$>)}' echo \$a done -fi chough : check "${1}>0 && \ ${2=15}>0 && \ ${3=2}>=1.0 && \ ${4=0.875}>=0 && ${4}<=1 && \ ${5=0.9}>=0.1 && ${5}<=1 && \ isbool(${6=0})" rad,nsmooth,pr,decim,sensitivity,verb=${1-6} -echo[^-1] "Search for circles of radius"$1" in selected images and report centers in 1xN image vectors." -foreach { nm={-1,n} -name. inputimage ow={w#$inputimage} oh={h#$inputimage} # Sprite radius sd={_round($pr*$rad+1,1)} -edges[inputimage] $nsmooth% -oneminus[inputimage] -if $verb -display[inputimage] , -fi # Trimming the data # has little effect on solution -fill[inputimage] 'i>0?(u(1)>($decim)?1:0):0' -input {$ow*$pr},{$oh*$pr},1,1 -name. sprite # Draw circular sprite for shift/adding paramspace # edgepoints. Draw just one circle, then rubber- # stamp with shift/add stat={ellipse(#$sprite,{w/2},{h/2},-$sd,-$sd,0,1,0xffffffff,1.0);1.0} -pointcloud[inputimage] 3 -if w#$inputimage -channels[inputimage] 0,1 -fill[inputimage] "$pr*I" ox,oy={I(#$inputimage,0,0)} # Shift sprite image by way of deltas # from point-to-point. Get those deltas # by subtracting the pointcloud from a # one-plot-point-shifted copy of itself, # i.e., foreach p(n)-p(n-1) +shift[inputimage] -1,0,0,0,2,0 -move. 0 -sub[-3,-2] -name.. diffs -input {$ow*$pr},{$oh*$pr},1,1 -name. paramspace -shift[sprite] {$ox-w/2},{$oy-h/2},0,0,2,0 -repeat {w#$diffs} -add[paramspace] [sprite] -shift[sprite] {I(#$diffs,$>,0)},0,0,2,0 -done -keep[paramspace] -if $verb -display , -fi -fill[paramspace] i>=$sensitivity*iM?1:0 -if $verb -display , -fi -if im==iM -echo[^-1] "No\ detected\ coins\ @\ "$rad"\ radius." -else # Find means of the noisy patches. -medianofclusters[paramspace] 32,$verb +fill[paramspace] "V=I(x,y);V=V/$pr;V[2]=$rad;V" -keep. -name. $nm -fi -else # Seems that w#$inputimage == 0 ... echo[^-1] $nm"\ seems to have no features. No points found." -fi } #@cli medianofclusters: errorball(32)>0,_verbose(False) #@cli : Find clusters in pointclouds; compute their #@cli : medians. Package these in a column vector of #@cli : centers. 'errorball' defines cluster radius. Points #@cli : farther apart than this distance are not in the #@cli : same cluster. 'verbose', when true, emits debug #@cli : data to G'MIC shell log. #@cli : $ medianofclusters[pointcloudimage] 64 medianofclusters: -skip ${1=32},${2=0} ed={$1^2} verb=$2 echo[^-1] "Extract from selected images of impulse pixels \ point cluster median locations with radius "$ed" or less." -foreach { -pointcloud. 3 -channels. 0,2 -if w>1 # Takes at least two points to cluster # Consolidate clusters: find # their average centers. nm={-1,n} -name. cloud -input 0 -name. cstack -shift[cloud] 0,0,0,1,2 -permute[cloud] cxzy -eval ">P=crop(#$cloud); psz=size(P)/3; erb=$ed; vrb=$verb; cc=0; ACC=vector2(0); if(vrb,print(P)); for( # --------------- INIT t=1; k=1; Q=P[1,2]; ACC+=Q; if(vrb, print(ACC)); chk=P[0]; P[0]=t; cc=1, # --------------- CONDITION chk==0, # --------------- PROCEDURE if( chk==0, t=t+1; k=1; P[0]=t; cc=1; Q=P[1,2]; ACC=Q ), # --------------- BODY do( if(vrb, print(Q); print(P[3*k,3]) ); if( P[3*k]==0, err = mse(Q,P[3*k+1,2]); if(vrb, print(err,erb)); if( err1 - Takes at least two points to cluster } # foreach image in command's selection #@cli circlefix : radius>0,_preblur(0)>=0,_outer(2)>0,_nsmooth(15)>=0,_medianf(10)>0,_medians(10)>0,_decimate(0.125)>=0&&<=1,senverbose=0 #@cli : Replace circular features with given 'radius' with an inpaint-generated #@cli : synthesized patch. #@cli : 'preblur' Optional pre-blurring to reduce false identification of #@cli : circular features. Default to no blurring (0). #@cli : 'outer' Optionally extend synthesized path boundary by outer number #@cli : of pixels. Default to two pixels. #@cli : 'nsmooth' Edge-detection smoothing. Default to 15, Larger to further #@cli : smooth edges; smaller to detect faint circular features. #@cli : 'medianf/medians' Blur/sharpen parameters for edge detection. Default #@cli : to 10/10. Adjust blur down and sharpen up for greater edge #@cli : detection, but also susceptibility to false detection. #@cli : 'decimate' Decimate pointcloud by this factor. Default to #@cli : 0.125; limit to 1 (no decimation). Smaller arguments yield #@cli : greater decimation and faster results, but a greater #@cli : liklihood of missed circular features. #@cli : 'sensitivity' Reduces all but the most likely solutions of pointcloud #@cli : clusters around potential center points; 0.9 is typical. #@cli : Use lower (less sensitive) values to capture less likely #@cli : center points, increase to reduce false solutions. #@cli : Try to get 'radius' correct before adjusting 'sensitivity'. #@cli : 'verbose' Set to enable display of intermediary analytic images. #@cli : $ circlefix[0] 32 circlefix : check "${1}>0 && \ ${2=0}>=0 && \ ${3=2}>0 && \ ${4=15}>0 && \ ${5=10}>0 && \ ${6=10}>0 && \ ${7=0.125}>0 && ${7}<=1 && \ ${8=0.9}>=0.1 && ${8}<=1 && \ isbool(${9=0})" rad,preblur,outer,nsmooth,medianf,medians,decimate,sensitivity,verbose=${1-9} -echo[^-1] "Seek "$1" pixel radius circles in selected images; perform inpaint removal of same." -foreach { nm={-1,n} -name. coinpic +blur[coinpic] {$preblur},2 -median. {$medianf},{$medians} -name. medianpic -if $verbose -v + -fi +chough[medianpic] $rad,$nsmooth,1,$decimate,$sensitivity,$verbose -name. clist +fill[medianpic] 0 -name. mask xmin,ymin=1e06 xmax,ymax=-1e06 -repeat {h#$clist} cx,cy,r={I(#$clist,0,$>)} ofs={$r+$outer} -if isnum($sprite)==0 -input {2*$ofs+1},{2*$ofs+1},{d#$medianpic},{s#$medianpic},1 -name. sprite -fi -image[mask] [sprite],{$cx-$ofs},{$cy-$ofs} -if $xmin>$cx-$ofs xmin={$cx-$ofs} -fi -if $xmax<$cx+$ofs xmax={$cx+$ofs} -fi -if $ymin>$cy-$ofs ymin={$cy-$ofs} -fi -if $ymax<$cy+$ofs ymax={$cy+$ofs} -fi -done -remove[^coinpic,mask] -if $verbose -display , -fi -inpaint[coinpic] [mask],15,45,1.5,1,1.5,0,0.05,50,{$outer>0} +luminance[coinpic] -name. varpix +crop[varpix] $xmin,$ymin,$xmax,$ymax,2 -if $verbose -echo {iv}","{iv#$varpix} -display , -fi -if (iv/iv#$varpix)<=0.25 -inpaint_matchpatch[coinpic] [mask],0,15,13,45,1,1 -fi -keep[coinpic] -name. $nm }