I need an algorithm to travel through all the cells, in an area which has been defined, in a 2 dimenional map (i.e. image)

Currently i have made and am using a wall following routine with a stack of "Multiple Direction Fork" co-ordinates. My problem is that the stack can get very large.

basically the algorithm is:

0. set current pixel to (0,0)1. set Direction to none.2. if pixel above current is empty and pixel left is non-empty, Direction = UP3. else if pixel left of current is empty and pixel below is non-empty, Direction = LEFT4. else if pixel below current is empty and pixel right is non-empty, Direction = DOWN5. else if pixel right of current is empty and pixel above is non-empty, Direction = RIGHT6. if Direction is UP6a. if pixels (-1,-1) and (+1,-1) or (+1,+1) are non empty then add current pixel to stack.7. if Direction is DOWN7a. if pixels (-1,+1) and (+1,+1) or (-1,-1) are non empty then add current pixel to stack.8. if Direction is RIGHT8a. if pixels (+1,-1) and (+1,+1) or (-1,+1) are non empty then add current pixel to stack.9. if Direction is LEFT9a. if pixels (-1,-1) and (-1,+1) or (+1,-1) are non empty then add current pixel to stack.10. if direction is not none process current pixel10a. else if there are pixels in the stack then pop a pixel and set the current pixel to it.10b. else exit algorithm11. move current pixel in direction specified.12. repeat 1

Does anyone know how to reduce the number of pixels entering the stack or indeed know of a better algorithm to traverse all the pixels in a shape.

(shaded pixels are the pixels that have been processed, solid blue pixels are pixels that had been in the stack at some point, grey/white pixels define the bounding areas and black pixels are unprocessed pixels)

(you will have to change the file extension after you have downloaded it)

As you can see there are long diagonal lines of (blue) pixels that have been in the stack.

hmm, it is looking like my wall following fill is the best peformer on average. (i.e. linear Flood Fill easily beats it on small areas, however fails on larger areas, while my wall following fill seems to work very well on large areas)

It looks like i will be staying with the wall following fill unless anyone knows of another filling aglorithm.

I think your queue is hurting your performance. All that new'ing of Items adds up. There is no need to use a queue as a stack will do the same. You can implement you own stack using an int array. It's easy, just double the stack size when it needs to grow.

This way you will hopefully get the same execution time as the recursive versions, but without the stack overflows.

A person suggested filling by rows, i.e. go left->right filling pixels until you hit a boundary pixel. then look above you to see if there are row(s) that are to be filled and then fill each of those.

This is exactly what the linear flood fill does.

Quote

I think your queue is hurting your performance.

I take that back. I implemented a non recursive linear flood fill using an array of int stack and a linked list. The linked list version was much faster.

As a conclusion I will suggest to get the linear flood fill working. It should not be as heavy on the stack as other methods. However, here is a non recursive version, if you still get stack overflows:

for all the tests i run with both my wall following and your linear flood fill, your flood fill consistantly peformed faster than my wall following in some cases 50% or more. This is the same as for the original linear flood fill earlier in this post.

The good news is that your version is superior to the previous version as you implement your own stack and thus have no overflow errors. (worked on images 2360x2100)

because the algorithm iterates over the x variable. Then it will touch only memory in the same area, and that can give better cach performance. But I haven't tested it so the gain is probably to smal to notice. But the other thing is that you can do this: "boolean line[] = checkedPixels[y]". This will reduce array lookups.

I've also come up with a new optimazation that increased the speed with ~25%. It's where the new LineSeg is added to the stack. X is set to newLineSeg.right, because it's known that all pixels to the left is set. Also the loop is getting so tight that an int[] stack implementation is another 20% faster than the LinkedList. Here is the latest int[] stack version. It's hopefully twice as fast as the last one I posted

The only extra overhead with the new method is the int stack. You could try reusing it, if thats acceptable. Make it a static member, and reset it at the start of the flood fill. Reducing the inital stack size on smal images might also help.

Not sure why the old method slows downs that much.

There is one more improvement that can be made to the algorithm. It is to not check for "collision" from where it come from. Here's an extract from the code:

if (y>0) { fromTop=1;// fill the line above this line //int topLine[] = image[y-1]; topChecked = pixelsChecked[y-1]; // did we come from there? if (nextLine.fromTop==1) { // check on the left side of where we come from intmin = Math.min(nextLine.prevLeft, right); for (intx=nextLine.left; x<=min; x++) { if (!topChecked[x]) { x = addLineSegToStack(x, y-1,width,height,frame,pixelsChecked,fcount,RColour,GColour,BColour,stack,fromTop); } } // check on the right side of where we come from for (intx=Math.max(nextLine.prevRight, nextLine.left); x<=right; x++) { if (!topChecked[x]) { x = addLineSegToStack(x, y-1,width,height,frame,pixelsChecked,fcount,RColour,GColour,BColour,stack,fromTop); } } } else { // did not come from this side: check the whole line for (intx=nextLine.left; x<=right; x++) { if (!topChecked[x]) { x = addLineSegToStack(x, y-1,width,height,frame,pixelsChecked,fcount,RColour,GColour,BColour,stack,fromTop); } } } }

The previous left and right values are the values of left and right of the last stack push operation, So i set the current previous left and previous right to the left and right of the last push operation.

If the current push operation is the beginning of the stack then i just add dummy prev left and prev right, just like you do.

With "previous" I mean the LineSeg that was prosessed when this LineSeg was added to the stack. It makes more sense in the recursive version. Where the previously prosessed lineSeg is the caller, or previous function.

A better name would be "callerLeft, callerRight and callerY", representing the line segment prosessed by the caller of this "function". Here a function is the code inside the "while (stack.size > 0)" loop. You do a function call by pushing a lineSeg onto the stack. It will be processed by the while loop later.

Thats why you can't use the previous lineSeg on the stack, as it is probably not the caller.

fromTop can also be made more clear. It's a boolean representing from what direction the current function was called. The caller is known to be on a neighbouring side, so it is used to find out if the caller is the top or bottom line. It would be better to just store the callers y, and check against that.

I am currently on my home computer due to being sick today, so i cannot compare the filling times now with the previous times i have given, however as an indicator, that 2482x2688 image is being fillled in 469 msec on my Athlon XP2100. And the 512x512 is done in 47 msec.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org