-2

For an image represented as a matrix, what is an efficient way to find all unique pairs of elements that touch within a 3x3 square?

Let A=
    1 1 2 2 3 3 3
    1 1 1 1 2 4 4
    1 1 2 2 5 5 5

Then we would return

(1,2),(1,3),(1,5),(2,3),(2,4),(2,5),(3,4),(4,5)
user135237
  • 389
  • 1
  • 3
  • 12
  • ```For an image represented as an image``` hmm... And what does ```touch``` (formally) mean in your case? – sascha Oct 18 '16 at 21:29
  • My mistake; 'touch' means it falls within a 3x3 square around every appearance of that element. – user135237 Oct 18 '16 at 23:06

2 Answers2

1

You may use itertools.combinations() to achieve this. Below is the sample code:

a = [[1, 1, 2, 2, 3, 3, 3,],
     [1, 1, 1, 1, 2, 4, 4,],
     [1, 1, 2, 2, 5, 5, 5,],
    ]

# Extract boundry values to list
boundary_vals = a[0] + a[-1] + [sub_list[0] for sub_list in a[1:-1]] + [sub_list[-1] for sub_list in a[1:-1]]

# Unique set of values
unique_vals = set(boundary_vals)

# Calculate combinations
from itertools import combinations
my_values = list(combinations(unique_vals, 2))

Here, my_values is a list of tuples having value as:

[(1, 2), (1, 3), (1, 4), (1, 5), 
 (2, 3), (2, 4), (2, 5), 
 (3, 4), (3, 5), 
 (4, 5)]

Explanation:

For calculating the boundary values:

# 1st list
>>> a[0]
[1, 1, 2, 2, 3, 3, 3]

# Last list
>>> a[-1]
[1, 1, 2, 2, 5, 5, 5]

# all the 0th elements of sub-lists excluding 1st and last list
>>> [sub_list[0] for sub_list in a[1:-1]]
[1]

# all the last elements of sub-lists excluding 1st and last list
>>> [sub_list[-1] for sub_list in a[1:-1]]
[4]

Adding all the above list, will give the boundary elements.

Moinuddin Quadri
  • 46,825
  • 13
  • 96
  • 126
  • These ```(1,4)``` and ```(3,5)``` do not look right, or are they (at least in a non-wrap scenario)? – sascha Oct 18 '16 at 22:34
1

Here is some partially-hardcoded easy-to-understand-approach.

  • Edit: faster version due to preprocessing
  • Edit 2: one more final speedup (symmetry-reduction in preprocessing)
  • Edit 3: okay; added one more symmetry-reduction step in preprocessing

Approach

  • get a block-view by skimage
  • preprocess the neighborhood-logic once:
    • create a list of all indices to look up pairs (which are connected) in the given window
    • some symmetry-reduction used
  • iterate all blocks; grab pairs
    • add if some symmetry-constraint is true

Code

import numpy as np
from skimage.util.shape import view_as_blocks, view_as_windows

img = np.array([[1,1,2,2,3,3,3],
                [1,1,1,1,2,4,4],
                [1,1,2,2,5,5,5]])
#img = np.random.random_integers(1, 10, size=(256,256))

WINDOW_SIZE = 3
img_windowed = view_as_windows(img, window_shape=(WINDOW_SIZE,WINDOW_SIZE))  # overlapping

# Preprocessing: generate valid index_pairs
index_pairs = []
for x in range(WINDOW_SIZE):
    for y in range(WINDOW_SIZE):
        if y>=x:  # remove symmetries
            if x>0:
                index_pairs.append(((x,y), (x-1,y)))
            if x<2:
                index_pairs.append(((x,y), (x+1,y)))
            if y>0:
                index_pairs.append(((x,y), (x,y-1)))
            if y<2:
                index_pairs.append(((x,y), (x,y+1)))
            if x>0 and y>0:
                index_pairs.append(((x,y), (x-1,y-1)))
            if x<2 and y<2:
                index_pairs.append(((x,y), (x+1,y+1)))
            if x>0 and y<2:
                index_pairs.append(((x,y), (x-1,y+1)))
            if x<2 and y>0:
                index_pairs.append(((x,y), (x+1,y-1)))

index_pairs = list(filter(lambda x: x[0] < x[1], index_pairs))  # remove symmetries

pairs = []
def reason_pair(a,b):  # remove symmetries
    if a<b:
        pairs.append((a,b))
    elif a>b:
        pairs.append((b,a))

for a in range(img_windowed.shape[0]):
    for b in range(img_windowed.shape[1]):
        block = img_windowed[a,b]
        for i in index_pairs:
            reason_pair(block[i[0]], block[i[1]])

print(set(pairs))

Output

set([(1, 2), (1, 3), (4, 5), (1, 5), (2, 3), (2, 5), (3, 4), (2, 4)])
sascha
  • 32,238
  • 6
  • 68
  • 110