2016 Update
This commit is contained in:
parent
948b86eafa
commit
dcf5d15da3
7965 changed files with 139854 additions and 31002 deletions
|
|
@ -92,3 +92,4 @@ If any pixels were set in this round of either step 1 or step 2 then all steps a
|
|||
;Reference:
|
||||
* [http://nayefreza.wordpress.com/2013/05/11/zhang-suen-thinning-algorithm-java-implementation/ Zhang-Suen Thinning Algorithm, Java Implementation] by Nayef Reza.
|
||||
* "Character Recognition Systems: A Guide for Students and Practitioners" By Mohamed Cheriet, Nawwaf Kharma, Cheng-Lin Liu, Ching Suen
|
||||
<br><br>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
defmodule ZhangSuen do
|
||||
@neighbours [{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1}] # 8 neighbours
|
||||
|
||||
def thinning(str, black \\ ?#) do
|
||||
s0 = for {line, i} <- (String.split(str, "\n") |> Enum.with_index),
|
||||
{c, j} <- (to_char_list(line) |> Enum.with_index),
|
||||
into: Map.new,
|
||||
do: {{i,j}, (if c==black, do: 1, else: 0)}
|
||||
{xrange, yrange} = range(s0)
|
||||
print(s0, xrange, yrange)
|
||||
s1 = thinning_loop(s0, xrange, yrange)
|
||||
print(s1, xrange, yrange)
|
||||
end
|
||||
|
||||
defp thinning_loop(s0, xrange, yrange) do
|
||||
s1 = step(s0, xrange, yrange, 1) # Step 1
|
||||
s2 = step(s1, xrange, yrange, 0) # Step 2
|
||||
if Map.equal?(s0, s2), do: s2, else: thinning_loop(s2, xrange, yrange)
|
||||
end
|
||||
|
||||
defp step(s, xrange, yrange, g) do
|
||||
for x <- xrange, y <- yrange, into: Map.new, do: {{x,y}, s[{x,y}] - zs(s,x,y,g)}
|
||||
end
|
||||
|
||||
defp zs(s, x, y, g) do
|
||||
if get(s,x,y) == 0 or # P1
|
||||
(get(s,x-1,y) + get(s,x,y+1) + get(s,x+g,y-1+g)) == 3 or # P2, P4, P6/P8
|
||||
(get(s,x-1+g,y+g) + get(s,x+1,y) + get(s,x,y-1)) == 3 do # P4/P2, P6, P8
|
||||
0
|
||||
else
|
||||
next = for {i,j} <- @neighbours, do: get(s, x+i, y+j)
|
||||
bp1 = Enum.sum(next) # B(P1)
|
||||
if bp1 in 2..6 do
|
||||
ap1 = (next++[hd(next)]) |> Enum.chunk(2,1) |> Enum.count(fn [a,b] -> a<b end) # A(P1)
|
||||
if ap1 == 1, do: 1, else: 0
|
||||
else
|
||||
0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp get(map, x, y), do: Map.get(map, {x,y}, 0)
|
||||
|
||||
defp range(map), do: range(Map.keys(map), 0, 0)
|
||||
defp range([], xmax, ymax), do: {0 .. xmax, 0 .. ymax}
|
||||
defp range([{x,y} | t], xmax, ymax), do: range(t, max(x,xmax), max(y,ymax))
|
||||
|
||||
@display %{0 => " ", 1 => "#"}
|
||||
defp print(map, xrange, yrange) do
|
||||
Enum.each(xrange, fn x ->
|
||||
IO.puts (for y <- yrange, do: @display[map[{x,y}]])
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
str = """
|
||||
...........................................................
|
||||
.#################...................#############.........
|
||||
.##################...............################.........
|
||||
.###################............##################.........
|
||||
.########.....#######..........###################.........
|
||||
...######.....#######.........#######.......######.........
|
||||
...######.....#######........#######.......................
|
||||
...#################.........#######.......................
|
||||
...###############...........#######.......................
|
||||
...#################.........#######.......................
|
||||
...######....########........#######.......................
|
||||
...######.....#######........#######.......................
|
||||
...######.....#######.........#######.......######.........
|
||||
.########.....#######..........###################.........
|
||||
.########.....#######..#####....##################.######..
|
||||
.########.....#######..#####......################.######..
|
||||
.########.....#######..#####.........#############.######..
|
||||
...........................................................
|
||||
"""
|
||||
ZhangSuen.thinning(str)
|
||||
|
||||
str = """
|
||||
00000000000000000000000000000000
|
||||
01111111110000000111111110000000
|
||||
01110001111000001111001111000000
|
||||
01110000111000001110000111000000
|
||||
01110001111000001110000000000000
|
||||
01111111110000001110000000000000
|
||||
01110111100000001110000111000000
|
||||
01110011110011101111001111011100
|
||||
01110001111011100111111110011100
|
||||
00000000000000000000000000000000
|
||||
"""
|
||||
ZhangSuen.thinning(str, ?1)
|
||||
|
|
@ -73,7 +73,7 @@ public class ZhangSuen {
|
|||
grid[p.y][p.x] = ' ';
|
||||
toWhite.clear();
|
||||
|
||||
} while (hasChanged || firstStep);
|
||||
} while (firstStep || hasChanged);
|
||||
|
||||
printResult();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,41 +1,41 @@
|
|||
/*REXX pgm thins a NxM char grid using the Zhang-Suen thinning algorithm*/
|
||||
/*REXX program thins a NxM character grid using the Zhang-Suen thinning algorithm.*/
|
||||
parse arg iFID .; if iFID=='' then iFID='ZHANG_SUEN.DAT'
|
||||
white=' '; @.=white /* [↓] read the input char grid. */
|
||||
white=' '; @.=white /* [↓] read the input character grid. */
|
||||
do row=1 while lines(iFID)\==0; _=linein(iFID)
|
||||
_=translate(_,,.0); cols.row=length(_)
|
||||
do col=1 for cols.row; @.row.col=substr(_,col,1)
|
||||
end /*col*/ /* [↑] assign whole row of chars*/
|
||||
end /*col*/ /* [↑] assign whole row of characters.*/
|
||||
end /*row*/
|
||||
rows=row-1 /* adjust ROWS because of DO loop*/
|
||||
call show@ 'input file ' iFID " contents:" /*show the input char grid.*/
|
||||
rows=row-1 /*adjust ROWS because of the DO loop. */
|
||||
call show@ 'input file ' iFID " contents:" /*display show the input character grid*/
|
||||
|
||||
do until changed==0; changed=0 /*keep slimming until we're done.*/
|
||||
do step=1 for 2 /*keep track of step 1 │ step 2.*/
|
||||
do r=1 for rows /*process all rows and columns. */
|
||||
do c=1 for cols.r; !.r.c=@.r.c /*assign alternate grid.*/
|
||||
if r==1|r==rows|c==1|c==cols.r then iterate /*is an edge?*/
|
||||
if @.r.c==white then iterate /*White? Then skip it.*/
|
||||
call Ps; b=b() /*define Ps and "b". */
|
||||
if b<2 | b>6 then iterate /*is B within range?*/
|
||||
if a()\==1 then iterate /*count the transitions.*/ /* ╔══╦══╦══╗ */
|
||||
if step==1 then if (p2 & p4 & p6) | p4 & p6 & p8 then iterate /* ║p9║p2║p3║ */
|
||||
if step==2 then if (p2 & p4 & p8) | p2 & p6 & p8 then iterate /* ╠══╬══╬══╣ */
|
||||
!.r.c=white /*set a grid character to white.*/ /* ║p8║p1║p4║ */
|
||||
changed=1 /*indicate a char was changed. */ /* ╠══╬══╬══╣ */
|
||||
end /*c*/ /* ║p7║p6║p5║ */
|
||||
end /*r*/ /* ╚══╩══╩══╝ */
|
||||
call copy!2@ /*copy alternate to working grid.*/
|
||||
do until changed==0; changed=0 /*keep slimming until we're finished. */
|
||||
do step=1 for 2 /*keep track of step one or step two.*/
|
||||
do r=1 for rows /*process all the rows and columns. */
|
||||
do c=1 for cols.r; !.r.c=@.r.c /*assign an alternate grid. */
|
||||
if r==1|r==rows|c==1|c==cols.r then iterate /*is this an edge?*/
|
||||
if @.r.c==white then iterate /*Is the character white? Then skip it*/
|
||||
call Ps; b=b() /*define Ps and also "b". */
|
||||
if b<2 | b>6 then iterate /*is B within the range ? */
|
||||
if a()\==1 then iterate /*count the number of transitions. */ /* ╔══╦══╦══╗ */
|
||||
if step==1 then if (p2 & p4 & p6) | p4 & p6 & p8 then iterate /* ║p9║p2║p3║ */
|
||||
if step==2 then if (p2 & p4 & p8) | p2 & p6 & p8 then iterate /* ╠══╬══╬══╣ */
|
||||
!.r.c=white /*set a grid character to white. */ /* ║p8║p1║p4║ */
|
||||
changed=1 /*indicate a character was changed. */ /* ╠══╬══╬══╣ */
|
||||
end /*c*/ /* ║p7║p6║p5║ */
|
||||
end /*r*/ /* ╚══╩══╩══╝ */
|
||||
call copy! /*copy the alternate to working grid. */
|
||||
end /*step*/
|
||||
end /*until changed==0*/
|
||||
|
||||
call show@ 'slimmed output:' /*display the slimmed char grid. */
|
||||
exit /*stick a fork in it, we're done.*/
|
||||
/*──────────────────────────────────subroutines─────────────────────────*/
|
||||
call show@ 'slimmed output:' /*display the slimmed character grid. */
|
||||
exit /*stick a fork in it, we're all done. */
|
||||
/*─────────────────────────────────────────────────────────────────────────────────────────────────────────────*/
|
||||
a: return (\p2==p3&p3)+(\p3==p4&p4)+(\p4==p5&p5)+(\p5==p6&p6)+(\p6==p7&p7)+(\p7==p8&p8)+(\p8==p9&p9)+(\p9==p2&p2)
|
||||
b: return p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
|
||||
copy!2@: do r=1 for rows; do c=1 for cols.r; @.r.c=!.r.c; end;end; return
|
||||
show@: say; say arg(1); say; do r=1 for rows; _=; do c=1 for cols.r; _=_||@.r.c; end; say _; end; return
|
||||
|
||||
Ps: rm=r-1; rp=r+1; cm=c-1; cp=c+1 /*calculate shortcuts.*/
|
||||
copy!: do r=1 for rows; do c=1 for cols.r; @.r.c=!.r.c; end; end; return
|
||||
show@: say; say arg(1); say; do r=1 for rows; _=; do c=1 for cols.r; _=_ || @.r.c; end; say _; end; return
|
||||
/*──────────────────────────────────────────────────────────────────────────────────────*/
|
||||
Ps: rm=r-1; rp=r+1; cm=c-1; cp=c+1 /*calculate some shortcuts.*/
|
||||
p2=@.rm.c\==white; p3=@.rm.cp\==white; p4=@.r.cp\==white; p5=@.rp.cp\==white
|
||||
p6=@.rp.c\==white; p7=@.rp.cm\==white; p8=@.r.cm\==white; p9=@.rm.cm\==white; return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue