0

I'm looking for a way to generate the following sequence of numbers (which are the relative coordinates of a pixel's 8 neighbours, starting with the north-west pixel and ending with the west). The first number is the y-coordinate and the second is the x-coordinate:

 y,  x
 _____

 1, -1   // N-W
 1,  0   // N
 1,  1   // N-E
 0,  1   // E
-1,  1   // S-E
-1,  0   // S
-1, -1   // S-W
 0, -1   // W

I can dream up several ugly ways to accomplish this, such as just putting the coordinates in an array, but I'm wondering if there's a clean and efficient way I haven't thought of.

Edit: due to the way the algorithm I'm trying to implement is designed, the pixels must be iterated in that particular order (N-W to W).

user2398029
  • 6,699
  • 8
  • 48
  • 80
  • Can you explain whath you need that for, and why it has to be in that order? – AlexWien Nov 24 '12 at 01:47
  • It's for a watershed segmentation algorithm; the main reason I need it to be in that order is to be able to verify my solutions against a reference implementation. Using the same ordering ensures that tie-breaking behaviour is the same in both cases. – user2398029 Nov 24 '12 at 01:50

2 Answers2

3

Consider the following method of generating the Y-coords first only.

Starting from NW we want to achieve {1, 1, 1, 0, -1, -1, -1, 0}. This is a repeating pattern given by the loop:

for( int i = 0; i < 8; i++ )
{
    // You can combine into one ternary if you are adventurous
    int y = (i % 4 == 3) ? 0 : 1;
    y *= (i > 3) ? -1 : 1;
}

So this will generate the desired sequence for the y-values.

Now consider the sequence of x values starting from NE: {1, 1, 1, 0, -1, -1, -1, 0 }. You can see it is the same sequence.

So we can produce the desired sequence starting from NW using an offset of 2 to the previous loop and doctoring the last ternary to accommodate the wrapping at the end of the sequence:

for (int i = 2; i < 10; i++ )
{
    int x = (i % 4 == 3) ? 0 : 1;
    x *= (i % 8 > 3) ? 1 : -1;   
}

Now it is trivial to combine the two into a single loop:

for (int i = 0; i < 8; i++)
{
    int y = (i % 4 == 3) ? 0 : 1;
    y *= (i > 3) ? -1 : 1;

    int x = ( (i+2) % 4 == 3) ? 0 : 1;
    x *= ( (i+2) % 8 > 3) ? 1 : -1;
}
ose
  • 4,065
  • 2
  • 24
  • 40
  • upvoted: good, do you have created that now, or already knewn? – AlexWien Nov 23 '12 at 23:40
  • I created it based on the OP's question. I had not seen this problem before. It would be interesting to know what algorithm you (OP) are trying to produce with this sequence of values. – ose Nov 23 '12 at 23:41
  • Normaly such algos are used for so called convolution in image processing, but we will wait for the answer of the OP – AlexWien Nov 23 '12 at 23:42
  • Nice answer. And yes, this is for image processing - specifically GPU-based watershed segmentation. – user2398029 Nov 24 '12 at 01:43
1

Another readable alternative is to enumerate the sides explicitly, as follows:

int x = -1;
int y = 1;
for (int side = 0; side < 4; side++)
{
    for (int steps = 0; steps < 2; steps++)
    {
        // use coordinates here
        printf("%d, %d\n", y, x);

        if (side == 0) { x++; }
        else if (side == 1) { y--; }
        else if (side == 2) { x--; }
        else /* if side == 3) */ { y++; }
    }
}

/*
result:
1, -1
1, 0
1, 1
0, 1
-1, 1
-1, 0
-1, -1
0, -1
*/

This has the additional benefit that a neighborhood of any size can be traversed by changing the starting corner and the number of steps along a side.

@louism: "I can dream up several ugly ways to accomplish this, such as just putting the coordinates in an array" - I think that is the best way, actually. It is clear and readable (unlike the modulus arithmetic method described by @ose above), and possibly the fastest.

@louism: could you benchmark the three different methods (array lookups, enumerate sides, modulus arithmetic) and post the results? I would be rather interested in this since it is something I would use in code I am writing right now.

Alex I
  • 19,689
  • 9
  • 86
  • 158