Home > Back-end >  How to retrieve a list of indexes of white pixels whose neighbor are black using Python PIL and Nump
How to retrieve a list of indexes of white pixels whose neighbor are black using Python PIL and Nump

Time:03-22

I've been looking for hours to find a question similar, but nothing has satisfied me. My problem is: I've a PIL image (representing a canal) already converted into a Numpy array (using the "L" mode of PIL), and I'd like to retrieve the white pixels whose neighbor are black (their indexes in fact), without using for loops (the image is really huge). I thought of np.where but I don't know how I should use it to solve my problem, and I also don't know if it would be faster than using for loops (because my aim would be reaching this goal with the fastest solution).

I hope I'm clear enough, and I thank you in advance for your response!

EDIT: for example, with this image (a simple canal, it is already a black and white image, so the image.convert('L') isn't really useful here, but the code should be generic if possible), I'd do something like that:

import numpy as np
from PIL import Image

image = Image.open(canal)
image = image.convert("L")
array = np.asarray(image)
l = []
for i in range(1, len(array) - 1):
    for j in range(1, len(array[0]) - 1):
        if array[i][j] == 255 and (array[i 1][j] == 0 or array[i-1][j] == 0 or array[i][j 1] == 0 or array[i][j-1] == 0):
             l.append((i, j))

and I'd hope to obtain l as fast as possible :) I've colored the pixels I need in red in the next image: here.

CodePudding user response:

You could use the numba just-in-time compiler to speed up your loop.

from numba import njit

@njit
def find_highlow_pixels(img):
    pixels = []
    for j in range(1, img.shape[0]-1):
        for i in range(1, img.shape[1]-1):
            if (
                img[j, i] == 255 and (
                    img[j-1, i]==0 or img[j 1,i]==0 or 
                    img[j, i-1]==0 or img[j, i 1]==0
                )
            ):
                pixels.append((j, i))
    return pixels 

CodePudding user response:

Another possibility that came to my mind would be using the minimum filter. However, I would expect it to be slower than the first proposed solution, but could be useful to build more on top of it.

import numpy as np
from scipy.ndimage import minimum_filter

# create a footprint that only takes the neighbours into account
neighbours = (np.arange(9) % 2 == 1).reshape(3,3)

# create a mask of relevant pixels, img should be your image as array
mask = np.logical_and(
    img == 255,
    minimum_filter(img, footprint=neighbours) == 0
)

# get indexes
indexes = np.where(mask)

# as list
list(zip(*indexes))
  • Related