Task: Zhang-Suen thinning algorithm




Go: package main

import (
    "bytes"
    "fmt"
    "strings"
)

var in = `
00000000000000000000000000000000
01111111110000000111111110000000
01110001111000001111001111000000
01110000111000001110000111000000
01110001111000001110000000000000
01111111110000001110000000000000
01110111100000001110000111000000
01110011110011101111001111011100
01110001111011100111111110011100
00000000000000000000000000000000`

func main() {
    b := wbFromString(in, '1')
    b.zhangSuen()
    fmt.Println(b)
}

const (
    white = 0
    black = 1
)

type wbArray [][]byte 




func wbFromString(s string, blk byte) wbArray {
    lines := strings.Split(s, "\n")[1:]
    b := make(wbArray, len(lines))
    for i, sl := range lines {
        bl := make([]byte, len(sl))
        for j := 0; j < len(sl); j++ {
            bl[j] = sl[j] & 1
        }
        b[i] = bl
    }
    return b
}



var sym = [2]byte{
    white: ' ',
    black: '#',
}

func (b wbArray) String() string {
    b2 := bytes.Join(b, []byte{'\n'})
    for i, b1 := range b2 {
        if b1 > 1 {
            continue
        }
        b2[i] = sym[b1]
    }
    return string(b2)
}


var nb = [...][2]int{
    2: {-1, 0}, 
    3: {-1, 1}, 
    4: {0, 1},
    5: {1, 1},
    6: {1, 0},
    7: {1, -1},
    8: {0, -1},
    9: {-1, -1}, 
}

func (b wbArray) reset(en []int) (rs bool) {
    var r, c int
    var p [10]byte

    readP := func() {
        for nx := 1; nx <= 9; nx++ {
            n := nb[nx]
            p[nx] = b[r+n[0]][c+n[1]]
        }
    }

    shiftRead := func() {
        n := nb[3]
        p[9], p[2], p[3] = p[2], p[3], b[r+n[0]][c+n[1]]
        n = nb[4]
        p[8], p[1], p[4] = p[1], p[4], b[r+n[0]][c+n[1]]
        n = nb[5]
        p[7], p[6], p[5] = p[6], p[5], b[r+n[0]][c+n[1]]
    }

    
    
    countA := func() (ct byte) {
        bit := p[9]
        for nx := 2; nx <= 9; nx++ {
            last := bit
            bit = p[nx]
            if last == white {
                ct += bit
            }
        }
        return ct
    }

    
    countB := func() (ct byte) {
        for nx := 2; nx <= 9; nx++ {
            ct += p[nx]
        }
        return ct
    }

    lastRow := len(b) - 1
    lastCol := len(b[0]) - 1

    mark := make([][]bool, lastRow)
    for r = range mark {
        mark[r] = make([]bool, lastCol)
    }

    for r = 1; r < lastRow; r++ {
        c = 1
        readP()
        for { 
            m := false
            
            if !(p[1] == black) {
                goto markDone
            }
            if b1 := countB(); !(2 <= b1 && b1 <= 6) {
                goto markDone
            }
            if !(countA() == 1) {
                goto markDone
            }
            {
                e1, e2 := p[en[1]], p[en[2]]
                if !(p[en[0]]&e1&e2 == 0) {
                    goto markDone
                }
                if !(e1&e2&p[en[3]] == 0) {
                    goto markDone
                }
            }
            
            m = true
            rs = true 
        markDone:
            mark[r][c] = m
            c++
            if c == lastCol {
                break
            }
            shiftRead()
        }
    }
    if rs {
        for r = 1; r < lastRow; r++ {
            for c = 1; c < lastCol; c++ {
                if mark[r][c] {
                    b[r][c] = white
                }
            }
        }
    }
    return rs
}

var step1 = []int{2, 4, 6, 8}
var step2 = []int{4, 2, 8, 6}

func (b wbArray) zhangSuen() {
    for {
        rs1 := b.reset(step1)
        rs2 := b.reset(step2)
        if !rs1 && !rs2 {
            break
        }
    }
}




Python: 


beforeTxt = 


smallrc01 = 

rc01 = 

def intarray(binstring):
    
    return [[1 if ch == '1' else 0 for ch in line] 
            for line in binstring.strip().split()]

def chararray(intmatrix):
    
    return '\n'.join(''.join(str(p) for p in row) for row in intmatrix)

def toTxt(intmatrix):
    Return 8-neighbours of point p1 of picture, in order'''
    i = image
    x1, y1, x_1, y_1 = x+1, y-1, x-1, y+1
    
    return [i[y1][x],  i[y1][x1],   i[y][x1],  i[y_1][x1],  
            i[y_1][x], i[y_1][x_1], i[y][x_1], i[y1][x_1]]  

def transitions(neighbours):
    n = neighbours + neighbours[0:1]    
    return sum((n1, n2) == (0, 1) for n1, n2 in zip(n, n[1:]))

def zhangSuen(image):
    changing1 = changing2 = [(-1, -1)]
    while changing1 or changing2:
        
        changing1 = []
        for y in range(1, len(image) - 1):
            for x in range(1, len(image[0]) - 1):
                P2,P3,P4,P5,P6,P7,P8,P9 = n = neighbours(x, y, image)
                if (image[y][x] == 1 and    
                    P4 * P6 * P8 == 0 and   
                    P2 * P4 * P6 == 0 and   
                    transitions(n) == 1 and 
                    2 <= sum(n) <= 6):      
                    changing1.append((x,y))
        for x, y in changing1: image[y][x] = 0
        
        changing2 = []
        for y in range(1, len(image) - 1):
            for x in range(1, len(image[0]) - 1):
                P2,P3,P4,P5,P6,P7,P8,P9 = n = neighbours(x, y, image)
                if (image[y][x] == 1 and    
                    P2 * P6 * P8 == 0 and   
                    P2 * P4 * P8 == 0 and   
                    transitions(n) == 1 and 
                    2 <= sum(n) <= 6):      
                    changing2.append((x,y))
        for x, y in changing2: image[y][x] = 0
        
        
    return image
            

if __name__ == '__main__':
    for picture in (beforeTxt, smallrc01, rc01):
        image = intarray(picture)
        print('\nFrom:\n%s' % toTxt(image))
        after = zhangSuen(image)
        print('\nTo thinned:\n%s' % toTxt(after))