2

Need to run though an image in python and essentially calculate the average location of all acceptable pixels within a certain boundary. The image is black and white. The acceptable pixels have a value of 255 and the unacceptable pixels have a value of zero. The image is something like 2592x1944 and takes maybe 15 seconds to run. This will need to be looped several times. Is there a faster way to do this?

goodcount = 0
sumx=0
sumy=0
xindex=0
yindex=0

for row in mask:
    yindex+=1
    xindex=0
    for n in row:
        xindex+=1
        if n == 255:
            goodcount += 1
            sumx += xindex
            sumy += yindex

if goodcount != 0:

    y = int(sumy / goodcount)
    x = int(sumx / goodcount)

2 Answers 2

4

np.where() will return arrays of the indices where a condition is true, which we can average (adding 1 to match your indexing) and cast to an integer:

if np.any(mask == 255):
    y, x = [int(np.mean(indices + 1)) for indices in np.where(mask == 255)]
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the answer. This works pretty good but at some point, I'm getting an error that says cannot convert float NaN to integer. I'm not too sure what this means. Do you know how to fix this? it may have something to do with no acceptable values but I don't know for sure.
I think that might happen if there are no acceptable pixels (goodcount == 0 in your original code). You can use np.any() to check for this.
If you need more speed and have OpenCV available, Mark Setchell's answer is several times faster on my machine (1ms vs 3.3ms, compared to 1.5s for the original code).
1

I think you are looking for the centroid of the white pixels, which OpenCV will find for you very fast:

enter image description here

#!/usr/bin/env python3

import cv2
import numpy as np

# Load image as greyscale
im = cv2.imread('star.png')

# Make greyscale version
grey = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

# Ensure binary
_, grey = cv2.threshold(grey,127,255,0)

# Calculate moments
M = cv2.moments(grey)

# Calculate x,y coordinates of centre
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])

# Mark centroid with circle, and tell user
cv2.circle(im, (cX, cY), 10, (0, 0, 255), -1)
print(f'Centroid at location: {cX},{cY}')

# Save output file
cv2.imwrite('result.png', im)

enter image description here

Sample Output

Centroid at location: 1224,344

That takes 381 microseconds on the 1692x816 image above, and rises to 1.38ms if I resize the image to the dimensions of your image... I'm calling that a 10,800x speedup 🤣

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.