0
votes

Problem statement:

Input is a rectangular bitmap like this:

0001
0011
0110

The task is to find for each black (0) "pixel", the distance to the closest white (1) "pixel". So, the output to the above should be:

3 2 1 0
2 1 0 0
1 0 0 1

I have a working solution to the problem, which I posted here, asking for advice on performance improvement. In effort to implement @Jerry Coffin's solution (suggested in an answer to the question) I wrote the following code, which, unfortunately, produces garbage output. For example, the output for the input from the problem statement is

1   1  1 0 
11  11 0 0 
110 0  0 1

Why doesn't the code work?

#include <iostream>
#include <string>
#include <queue>

using namespace std;

const unsigned short MAX = 200;

typedef unsigned short coordinate;
typedef pair<coordinate,coordinate> coordinates;

//Converts char[] to unsigned int
coordinate atou(char* s) {
    coordinate x = 0;
    while(*s) x = x*10 + *(s++) - '0';
    return x;
}

//Returns array of immediate neighbours of pixel of coordinates c
coordinates* neighbours_of(coordinates c) {
    static coordinates neighbours[7];
    coordinate i = c.first;
    coordinate j = c.second;
    neighbours[0] = coordinates(i+1,j);
    neighbours[1] = coordinates(i+1,j+1);
    neighbours[2] = coordinates(i,j+1);
    neighbours[3] = coordinates(i-1,j+1);
    neighbours[4] = coordinates(i-1,j);
    neighbours[5] = coordinates(i-1,j-1);
    neighbours[6] = coordinates(i,j-1);
    neighbours[7] = coordinates(i+1,j-1);
    return neighbours;
}

int main() {

    unsigned short test_cases, wave_number, initial_wave_size;
    coordinate m, n, i, j;
    int A[MAX][MAX];
    coordinates* directions;
    string row;
    queue<coordinates> ones;
    queue<coordinates> wave;
    coordinates current_coordinates;
    bool found_some_zero;
    cin >> test_cases;

    while(test_cases--) {

        //Input
        cin >> n;
        cin >> m;
        for(i = 0; i < n; i++) {
            cin >> row;
            for(j = 0; j < m; j++) {
                if(row[j] == '1') {
                    A[i][j] = -1;
                    ones.push(coordinates(i,j));
                } else A[i][j] = atou(&row[j]);
            }
        }

        //Initilization
        wave = ones;
        wave_number = 1;
        found_some_zero = true;

        //Filling algorithm
        while(found_some_zero) {
            found_some_zero = false;
            initial_wave_size = wave.size();
            while(initial_wave_size--) {
                current_coordinates = wave.front();
                directions = neighbours_of(current_coordinates);
                //Try all directions
                for(int k = 0; k < 8; k++) {
                    i = directions[k].first;
                    j = directions[k].second;
                    //If on screen and not yet visited
                    if(i < n && j < m && A[i][j] == 0) {
                        //Mark visited
                        A[i][j] = wave_number;
                        //(i,j) will be part the next wave
                        wave.push(coordinates(i,j));
                        found_some_zero = true;
                    }
                }
                wave.pop();
            }
            wave_number++;
        }

        //-1 to 0
        while(!ones.empty()) {
            current_coordinates = ones.front();
            i = current_coordinates.first;
            j = current_coordinates.second;
            A[i][j] = 0;
            ones.pop();
        }

        //Output
        for(i = 0; i < n; i++) {
            for(j = 0; j < m; j++)
                cout << A[i][j] << " ";
            cout << endl;
        }
    }
    return 0;
}
1
You might find useful to check some links to implementations from en.wikipedia.org/wiki/Distance_transformMBo

1 Answers

1
votes

The input loop is buggy. Instead of

            if(row[j] == '1') {
                A[i][j] = -1;
                ones.push(coordinates(i,j));
            } else A[i][j] = atou(&row[j]);

you need to do

            if(row[j] == '1') {
                A[i][j] = -1;
                ones.push(coordinates(i,j));
            } else A[i][j] = 0;

Also, neighbors[7] should be neighbors[8], and you should use only the four cardinal directions if you want to match the specified output exactly.