I am creating a flood fill
algorithm in C++
using Eclipse IDE
. The algorithm contains a vector of vectors called image
. A square is drawn in this image based on user input, segmenting the image to two regions (inside and outside the square). A clicked point
is taken in as input. If this point is inside the square, all points inside the square will be changed to a fill_value
(in this case, 25). If it is outside the square, all pixels outside the square changes to fill_value
. Here is the code:
#include <iostream>
#include <vector>
#include <queue>
#include <cstddef>
#include <stdexcept>
class Point
{
std::size_t x_cord;
std::size_t y_cord;
public:
Point(std::size_t x, std::size_t y):x_cord{x}, y_cord{y}
{
}
std::size_t x() const
{
return x_cord;
}
std::size_t y() const
{
return y_cord;
}
};
bool check_point(Point pt, std::size_t x_dim, std::size_t y_dim)
{
if(pt.x() >= 0 && pt.x() < x_dim && pt.y() >= 0 && pt.y() < y_dim)
{
return true;
}
return false;
}
void get_neighbors(Point& curr_point, std::queue<Point>& q, std::vector<std::vector<int>>& image, int old_val)
{
std::vector<Point> neighbors;
std::size_t x_dim = image.size();
std::size_t y_dim;
if(x_dim > 0)
{
y_dim = image[0].size();
}
if(check_point(Point{curr_point.x() - 1, curr_point.y()}, x_dim, y_dim) && image[curr_point.x() - 1][curr_point.y()] == old_val)
{
q.push(Point{curr_point.x() - 1, curr_point.y()});
}
if(check_point(Point{curr_point.x(), curr_point.y() - 1}, x_dim, y_dim) && image[curr_point.x()][curr_point.y() - 1] == old_val)
{
q.push(Point{curr_point.x(), curr_point.y() - 1});
}
if(check_point(Point{curr_point.x() + 1, curr_point.y()}, x_dim, y_dim) && image[curr_point.x() + 1][curr_point.y()] == old_val)
{
q.push(Point{curr_point.x() + 1, curr_point.y()});
}
if(check_point(Point{curr_point.x(), curr_point.y() + 1}, x_dim, y_dim) && image[curr_point.x()][curr_point.y() + 1] == old_val)
{
q.push(Point{curr_point.x(), curr_point.y() + 1});
}
}
void flood_fill(std::vector<std::vector<int>>& image, Point clicked, int new_val)
{
int old_val = image[clicked.x()][clicked.y()];
std::queue<Point> q;
q.push(clicked);
while(!q.empty())
{
Point curr_point = q.front();
get_neighbors(curr_point, q, image, old_val);
image[curr_point.x()][curr_point.y()] = new_val;
q.pop();
}
}
void draw_square(std::vector<std::vector<int>>& image, Point top_left_corner, int length)
{
std::size_t x_0 = top_left_corner.x();
std::size_t y_0 = top_left_corner.y();
std::size_t x;
std::size_t y;
for(x = x_0; x < x_0 + length; x++)
{
image[x][y_0] = 1;
image[x][y_0 + length - 1] = 1;
}
for(y = y_0; y < y_0 + length; y++)
{
image[x_0][y] = 1;
image[x_0 + length - 1][y] = 1;
}
}
void print_image(std::vector<std::vector<int>>& image, std::size_t x_dim, std::size_t y_dim)
{
for(std::size_t i = 0; i < x_dim; i++)
{
for(std::size_t j = 0; j < y_dim; j++)
{
std::cout << image[i][j] << "\t";
}
std::cout << "\n";
}
std::cout << "\n";
}
int main()
{
try
{
std::size_t x_dim, y_dim;
std::size_t x, y;
std::size_t c_x = 0;
std::size_t c_y = 0;
int length;
int fill_value = 25;
std::cout << "Enter the dimensions of the image: \n";
std::cin >> x_dim >> y_dim;
std::vector<std::vector<int>> image(x_dim, std::vector<int>(y_dim, 0));
std::cout << "Enter the top left point coordinates and length for the square: \n";
std::cin >> x >> y >> length;
Point top_left_corner{x, y};
if(!check_point(top_left_corner, x_dim, y_dim) || !check_point(Point{top_left_corner.x() + length - 1, top_left_corner.y() + length - 1}, x_dim, y_dim))
{
throw std::out_of_range{"Invalid Access"};
}
draw_square(image, top_left_corner, length);
std::cout << "Before Flood Fill: \n";
print_image(image, x_dim, y_dim);
std::cout << "Enter point to be clicked: \n";
std::cin >> c_x >> c_y;
Point clicked{c_x, c_y};
//std::cout << "here1\n";
if(!check_point(clicked, x_dim, y_dim))
{
throw std::out_of_range{"Invalid Access"};
}
std::cout << "here2\n";
flood_fill(image, clicked, fill_value);
std::cout << "After Flood Fill: \n";
print_image(image, x_dim, y_dim);
}
catch(std::out_of_range& e)
{
std::cerr << e.what() << "\n";
}
return 0;
}
It works fine for some input. However, consider the following input (The array after Before Flood Fill
is a program output, not an input):
Enter the dimensions of the image:
20 20
Enter the top left point coordinates and length for the square:
15 15 4
Before Flood Fill:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Enter point to be clicked:
1 2
The program takes up a lot of processing after this and does not proceed or does not get terminated. My idea was that this is due to inefficient implementation of flood_fill
function. When I use a debugger std::cout << "here2\n";
statement prints here2
on the console while it does not get printed while I simply run the program. So, I am not sure if it is flood_fill
which is causing this issue or is it something else.
Why is the behavior different while running and debugging?
Kindly provide suggestions to debug.
Note: My idea was that since the value is being changed, they will automatically fail the check for being an eligible neighbor. But I see the problem with my code. By the time, the value is changed, a particular neighbor might be added many times. Both answers helped me identify this. Thank you to both.