为什么堆栈在这段代码中溢出?

时间:2009-05-24 12:12:10

标签: opengl

我写了这个函数用于填充闭环, pixvali 是全局声明的,用于存储第一次点击的像素的颜色值(在闭环内)。

但问题是,当第一个* fill(..,..)*结束时,这个递归不会终止,并且它表示堆栈溢出...

void fill(int x,int y)
{
    GLfloat pixval[3];
    glReadPixels(x,y,1,1,GL_RGB,GL_FLOAT,pixval);
    if(pixval[0]==pixvali[0] && pixval[1]==pixvali[1] && pixval[2]== pixvali[2])
    {
        glBegin(GL_POINTS);
            glVertex2i(x,y);
        glEnd();
        glFlush();
        fill(x-1,y);
        fill(x+1,y);
        fill(x,y-1);
        fill(x,y+1);
    }   
}

7 个答案:

答案 0 :(得分:4)

堆栈溢出是因为您正在使用递归,并且递归的深度与您填充的形状中的像素数呈线性关系。

也可能是您尝试使用与现有颜色相同的颜色填充形状。也就是说,当前的gl颜色与pixvali相同。在这种情况下,您将获得无限递归。

答案 1 :(得分:3)

这个问题很难说清楚,但我的猜测是,你会开始进行像素循环。

例如,假设您只有4个像素需要着色(0,0),(0,1),(1,0),(1,1)。

开始着色(0,0)。然后你的递归将输入(1,0),因为(-1,0)不需要着色。然后再次(0,0),因为它再次是(x-1,y)的像素,依此类推。

您需要添加一些方法来标记已经着色的像素。但这只是猜测,因为你无法真正看到那些功能之外发生了什么。

答案 2 :(得分:1)

不确定实现细节,但如果在堆栈上分配12字节本地数组(3个浮点数为4个字节),那么x和y参数各有4个字节,可能有4个字节用于退货地址。每次递归时至少有24个。这意味着你只需要超过40,000次调用就可以吹掉1MB的堆栈空间,如果没有别的东西,那就不行了。

从透视角度来看,43'690像素仅为800x600显示器的10%左右。

答案 3 :(得分:1)

您需要检查您正在编辑的像素。

e.g。如果你有一个从0,0到10,10的图像,并且你编辑了11,10,你就会得到内存。

所以你需要检查x,y是否在图像的边界之间。

x>=left&&x<=right&&y>=top&&y<=bottom 

答案 4 :(得分:0)

实现你自己的堆栈,不要使用递归进行泛洪填充,除非你用像素填充表面积相对较小的形状。

典型的实现是:

Stack stack; 
stack.push(firstPoint);

while(!stack.isEmpty()){
   Point currentPoint= stack.pop();
   //do what ever you want to do here, namely paint.
   //boundary check ur surrounding points and push them in the stack if they are inbounds
}

答案 5 :(得分:0)

乍一看,算法看起来不错。我有点担心“==”,因为它们与浮点值不兼容。我建议使用

abs(val1 - val2) < limit

代替(limit是&lt; 1和&gt; 0.例如,尝试0.0001。

为了追踪错误,我建议在函数的开头添加printf()。当您看到函数尝试填充的内容时,这将有所帮助。也许它被卡在某个地方并且用相同的坐标一次又一次地召唤自己?

此外,堆栈可能对于您尝试填充的区域来说太小。首先尝试使用一个小区域,比如说一个只有4 x 3像素的小矩形。不要试图用鼠标点击它,而是从内部已知的好点开始(只需在代码中调用fill())。

同时打印颜色值也有帮助。

答案 6 :(得分:0)

你为什么滥用OpenGL?你在那里做的是非常不稳定的。例如,如果使用精心选择的投影和模型视图矩阵的组合,则glReadPixels读取的像素将仅对应于顶点位置。此外,填充的每次迭代都将进行完整的往返。仅仅因为你使用的是OpenGL,它的速度并不快。

如果你想要填充帧缓冲区中的某些区域,请读出整个帧缓冲区,对其进行填充并将结果推回到OpenGL。此外,如果帧缓冲的某些部分被遮挡(通过窗口或类似物),那些部分将不会

现在要了解为什么你最终会进行无限递归。考虑一下:

fill(4, 4)将致电fill(5, 4)致电fill(5, 5)致电fill(4, 5)致电fill(4, 4) 热潮

现在你已经在那里进行了测试:

if( pixval[0] == pixvali[0] && 
    pixval[1] == pixvali[1] && 
    pixval[2] == pixvali[2] )

请注意,如果要设置的像素已经具有目标颜色,则此计算结果为true,再次以无限递归方式结束。您应该测试不等式

最后但并非最不重要:图片可能很容易包含数百万像素。通常的堆栈大小最多只允许1000个函数嵌套级别,因此您可以将尾递归转换为迭代。

TL; DR:不要为此使用OpenGL,在本地缓冲区上运行,使用正确的迭代条件测试并使用迭代而不是递归(或者使用函数式语言,然后编译器将处理尾递归)

http://en.wikipedia.org/wiki/Flood_fill