How to resolve the algorithm Zhang-Suen thinning algorithm step by step in the Go programming language

Published on 12 May 2024 09:40 PM
#Go

How to resolve the algorithm Zhang-Suen thinning algorithm step by step in the Go programming language

Table of Contents

Problem Statement

This is an algorithm used to thin a black and white i.e. one bit per pixel images. For example, with an input image of: It produces the thinned output: Assume black pixels are one and white pixels zero, and that the input image is a rectangular N by M array of ones and zeroes. The algorithm operates on all black pixels P1 that can have eight neighbours. The neighbours are, in order, arranged as:

Obviously the boundary pixels of the image cannot have the full eight neighbours.

All pixels are tested and pixels satisfying all the following conditions (simultaneously) are just noted at this stage. After iterating over the image and collecting all the pixels satisfying all step 1 conditions, all these condition satisfying pixels are set to white.

All pixels are again tested and pixels satisfying all the following conditions are just noted at this stage. After iterating over the image and collecting all the pixels satisfying all step 2 conditions, all these condition satisfying pixels are again set to white.

If any pixels were set in this round of either step 1 or step 2 then all steps are repeated until no image pixels are so changed.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm Zhang-Suen thinning algorithm step by step in the Go programming language

Overview

The provided Go code is an implementation of the Zhang-Suen thinning algorithm, which is used for thinning binary images. It iteratively removes pixels from the image while preserving its topological structure.

Data Structures

  • wbArray: A 2D array of bytes representing the binary image. Each element can be either white or black.
  • step1 and step2: Arrays of neighbor offsets used in the thinning algorithm.

Functions

  • wbFromString(s string, blk byte): Initializes a wbArray from a string representation of the image.
  • (b wbArray) String() string: Converts the wbArray back to a string representation.
  • (b wbArray) reset(en []int): Resets pixels in the image based on the given neighbor offsets.
  • (b wbArray) zhangSuen(): Applies the Zhang-Suen thinning algorithm to the image.

Zhang-Suen Thinning Algorithm

The Zhang-Suen algorithm works in two steps, which are executed iteratively until no more pixels are removed:

  1. Step 1: Loops through the image and removes pixels that meet certain conditions based on their neighbors.
  2. Step 2: Repeats step 1 but with different neighbor offsets.

Algorithm Details

The algorithm defines several conditions that a pixel must satisfy in order to be removed:

  • It must be black.
  • It must have at least 2 and at most 6 black neighbors.
  • The number of white-to-black transitions in the neighborhood must be 1.
  • The black pixels at positions 2, 4, 6, and 8 in the neighborhood must not all be black.
  • The black pixels at positions 2, 4, 8, and 6 in the neighborhood must not all be black.

Usage

The sample input in represents a binary image as a string. The main function initializes a wbArray from the input and applies the zhangSuen function to thin the image. The thinned image is then printed as a string representation.

Additional Notes

  • The code assumes that the input string represents a valid binary image with leading and trailing newlines.
  • The thinning algorithm is hard-coded for this specific image format, but can be generalized to handle different image formats.

Source code in the go programming language

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 // elements are white or black.

// parameter blk is character to read as black.  otherwise kinda rigid,
// expects ascii, leading newline, no trailing newline,
// takes color from low bit of character.
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
}

// rigid again, hard coded to output space for white, # for black,
// no leading or trailing newline.
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)
}

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

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]]
    }

    // returns "A", count of white->black transitions in circuit of neighbors
    // of an interior pixel b[r][c]
    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
    }

    // returns "B", count of black pixels neighboring interior pixel b[r][c].
    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 { // column loop
            m := false
            // test for failure of any of the five conditions,
            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
                }
            }
            // no conditions failed, mark this pixel for reset
            m = true
            rs = true // and mark that image changes
        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
        }
    }
}


  

You may also check:How to resolve the algorithm Stable marriage problem step by step in the SPARK / Ada programming language
You may also check:How to resolve the algorithm Search a list step by step in the ActionScript programming language
You may also check:How to resolve the algorithm Sorting algorithms/Patience sort step by step in the OCaml programming language
You may also check:How to resolve the algorithm Stack step by step in the Common Lisp programming language
You may also check:How to resolve the algorithm Matrix transposition step by step in the J programming language