Image processing 为什么递归函数抛出堆栈溢出异常?

Image processing 为什么递归函数抛出堆栈溢出异常?,image-processing,f#,stack-overflow,Image Processing,F#,Stack Overflow,我正在实现一个填充孔算法,字节0的值用作背景,因为我需要首先实现一个泛光填充算法。 如果我对当前像素的左上角、左上角或左下角的位置使用CHECK 8,则代码会给我一个堆栈溢出异常。 但是如果我只使用顶部、顶部、右侧、底部和底部,那就好了。 我使用仪器分配工具进行了检查,是的,我接到了很多malloc电话。 但我不知道它为什么会这样 let FillHoles (img : Bitmap) = let bd = img.LockBits(Rectangle(0,0,img.Width,im

我正在实现一个填充孔算法,字节0的值用作背景,因为我需要首先实现一个泛光填充算法。
如果我对当前像素的左上角、左上角或左下角的位置使用CHECK 8,则代码会给我一个堆栈溢出异常。
但是如果我只使用顶部、顶部、右侧、底部和底部,那就好了。
我使用仪器分配工具进行了检查,是的,我接到了很多malloc电话。 但我不知道它为什么会这样

let FillHoles (img : Bitmap) =
    let bd = img.LockBits(Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)
    let mutable (p:nativeptr<byte>) = NativePtr.ofNativeInt (bd.Scan0)

    let rec check8 (point:nativeptr<byte>) x y =
        try 
            if (x>=0 && x<=img.Width-1 && y>=0 && y<=img.Height-1)then
                if((NativePtr.get point 0)=(byte 0)) then
                    NativePtr.set point 0 (byte 255)
//                  if (x<>0) then
//                      check8 (NativePtr.add point -4) (x-1) y //Left -4
//                  if (x<>0 && y<>0) then
//                      check8 (NativePtr.add point -(bd.Stride + 4)) (x-1) (y-1) //TopLeft
                    if (y<>0) then
                        check8 (NativePtr.add point -(bd.Stride)) x (y-1) //Top
                    if (x<>img.Width-1 && y<>0) then
                        check8 (NativePtr.add point -(bd.Stride - 4)) (x+1) (y-1)  //TopRight
                    if (x<img.Width-1) then
                        check8 (NativePtr.add point 4) (x+1) y //Right
                    if (x<>img.Width-1 && y<>img.Height-1) then
                        check8 (NativePtr.add point (bd.Stride + 4)) (x+1) (y+1) //BotRight
                    if (y<>img.Height-1) then
                        check8 (NativePtr.add point (bd.Stride)) x (y+1) //Bot
//                    if (x<>0 && y<>img.Height-1) then
//                        check8 (NativePtr.add point (bd.Stride - 4)) (x-1) (y+1) //BotLeft
        with
            | :? System.NullReferenceException as ex -> (printfn "%A" ex.Message)
            | :? System.StackOverflowException as ex -> (printfn "%A" ex.Message)

    for row in 0 .. img.Height-1 do
        for col in 0 .. img.Width-1 do
            if (row=0 || row=img.Height-1 || col=0 || col=img.Width-1) then
                check8 p row col
            p <- NativePtr.add p 4
        done
    done
    img.UnlockBits(bd)
    img
let FillHoles(img:Bitmap)=
设bd=img.LockBits(矩形(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)
设可变(p:nativeptr)=nativeptr.ofnativeinit(bd.Scan0)
让记录检查8(点:nativeptr)x y=
尝试
如果(x>=0&&x=0&&y)(打印“%A”例如消息)
对于0..img.Height-1 do中的行
对于0..img.Width-1 do中的列
如果(行=0 | |行=img.Height-1 | | col=0 | | col=img.Width-1),则
检查8p行列

p我试图进行尾部递归洪水填充,但时间不够,所以我用堆栈实现了它。
递归的一个我无法正确调试它,我的IDE表现得很奇怪,所有的堆栈帧可能都会弄乱它。
可能有一种更实用的方法

let FloodFill (img : Bitmap) =
    let bd = img.LockBits(Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)

    let check4 x y =
        let (checkPointer:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*y+x*4)
        if(NativePtr.get checkPointer 0 = byte 0) then
            let st = Stack()
            st.Push(x,y)
            while(st.Count > 0) do
                let current = st.Pop()
                let xx = fst current
                let yy = snd current
                let (pointer:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+xx*4)
                NativePtr.set pointer 0 (byte 255)

                if(xx+1<=img.Width-1)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+(xx+1)*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx+1,yy)
                if(xx-1>=0)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+(xx-1)*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx-1,yy)
                if(yy+1<=img.Height-1)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*(yy+1)+xx*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx,yy+1)
                if(yy-1>=0)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*(yy-1)+xx*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx,yy-1)

    for row in 0 .. img.Height-1 do
        for col in 0 .. img.Width-1 do
            if (row=0 || row=img.Height-1 || col=0 || col=img.Width-1) then
                check4 row col
        done
    done
    img.UnlockBits(bd)
    img
let FloodFill(img:Bitmap)=
设bd=img.LockBits(矩形(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)
让我们检查一下4 x y=
let(checkPointer:nativeptr)=nativeptr.add(nativeptr.ofnativeeint(bd.Scan0))(bd.Stride*y+x*4)
如果(NativePtr.get checkPointer 0=字节0),则
设st=Stack()
圣推(x,y)
而(st.Count>0)做什么
让电流=圣波普()
设xx=fst电流
设yy=snd电流
let(指针:nativeptr)=nativeptr.add(nativeptr.ofnativeinit(bd.Scan0))(bd.Stride*yy+xx*4)
NativePtr.set指针0(字节255)
如果(xx+1=0),则
let(point:nativeptr)=nativeptr.add(nativeptr.ofnativeeint(bd.Scan0))(bd.Stride*yy+(xx-1)*4)
如果(NativePtr.get point 0=字节0),则
圣推(xx-1,yy)
如果(yy+1=0),则
let(point:nativeptr)=nativeptr.add(nativeptr.ofnativeinit(bd.Scan0))(bd.Stride*(yy-1)+xx*4)
如果(NativePtr.get point 0=字节0),则
圣推(xx,yy-1)
对于0..img.Height-1 do中的行
对于0..img.Width-1 do中的列
如果(行=0 | |行=img.Height-1 | | col=0 | | col=img.Width-1),则
检查4行列
完成
完成
img.解锁位(bd)
img

在try-catch中不能发生尾部调用。请参见如何编译尾部调用?在本章中。

通常,在递归函数中,当您没有正确的退出条件时,会发生堆栈溢出异常。这会导致递归函数被无限调用,直到堆栈内存耗尽。我怀疑您正在进行相同的操作两次。如果您已经检查了一个点,则需要退出该函数-例如左->右->左->右等将创建一个无穷多个点loop@Niroshan我认为你是对的,正在检查尾部递归和这篇文章@JohnPalmer我相信条件(NativePtr.get point 0)=(byte 0))正在检查这一点。我将尝试将其更改为尾部递归,并尝试更实用的方法。@EinSL我对f#知之甚少。您可以在每个
if..then
之后打印一些内容,然后查看哪一个永远不会完成。其次,如果退出条件是
(NativePtr.get point 0)=(字节0)
,则在这之后再次检查set语句。