【发布时间】:2019-02-01 19:10:22
【问题描述】:
我正在使用Eclipse IDE 在C++ 中创建flood fill 算法。该算法包含一个名为image 的向量。根据用户输入在此图像中绘制一个正方形,将图像分割为两个区域(正方形内部和外部)。 clicked point 被作为输入。如果此点在正方形内,则正方形内的所有点都将更改为fill_value(在本例中为 25)。如果在正方形外,正方形外的所有像素都变为fill_value。代码如下:
#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;
}
它适用于某些输入。但是,请考虑以下输入(Before Flood Fill 之后的数组是程序输出,而不是输入):
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
程序在此之后占用了大量处理,并且不会继续或不会终止。我的想法是这是由于flood_fill 函数的低效实现。当我使用调试器时,std::cout << "here2\n"; 语句会在控制台上打印here2,而我只是运行程序时不会打印它。所以,我不确定是flood_fill 导致了这个问题还是其他原因。
为什么运行和调试时行为不同?
请提供调试建议。
注意:我的想法是,由于值正在更改,它们将自动无法通过作为合格邻居的检查。但我看到我的代码存在问题。到值更改时,可能会多次添加特定的邻居。这两个答案都帮助我确定了这一点。谢谢你们。
【问题讨论】:
-
调试与以其他方式运行时的不同行为通常表明代码覆盖了它不应该覆盖的内存(调试或未经优化的编译会更改程序使用的内存中数据的布局,因此,不良操作的后果可能会改变)。您正在使用
vector<vector<int>>将变量用作与向量的实际大小无关的范围内的索引 - 可能您正在运行。除此之外,很难给出具体的建议,因为代码太多。阅读如何创建minimal reproducible example - 以增加获得有用建议的机会。 -
您对洪水填充算法的实现可能完全是假的。即使使用调试版本,如此小的图像上的洪水填充也应该是即时的。您需要使用调试器自行调试。尝试创建尽可能小的图像以进行调试。
-
是的,这是假的,因为点范围检查失败,正如我的回答中所解释的那样。
std::size_ts 正在下溢。 -
仅供参考,
y_diminget_neighbors不确定地使用,如果你曾经为image提供一个空向量向量的函数。在这种情况下,未初始化的y_dim在重复传递给check_point时仍然不确定。 Prolly 想为此做点什么。 -
floodfill 最终将通过 x==0 或 y==0 或两者都传递到 get_neighbors 点,这反过来将导致访问索引为 -1 的向量的元素,即 ub
标签: c++ eclipse debugging flood-fill