Search code examples
pythonnumpyimage-processingpython-imaging-libraryastronomy

How to filter specific image coordinates from an image


I am reading an image, getting objects that have a certain brightness value, and then plotting the X and Y coords to the image.

Problem/outliers

But, there is a huge group of outliers, which are all located in a rectangular part of the image, Its X and Y coords are 1110-1977 (width) and 1069-1905 (height). From here, I'm looping through this little square portion of the image, and from my pre-created x and y arrays any values that have the same coords as shown there are removed.

However, this removes a lot more coords, which, for example, have X in the range 1110-1977. So the end result is a cross pattern filtering when I only want the square in the center to be filtered. How would I do this?

enter image description here

Code

from PIL import Image, ImageDraw
import numpy as np
from math import sqrt
imag = Image.open("Centaurus_A-DeNoiseAI-denoise.jpg")
imag = imag.convert ('RGB')
x=[]
y=[]
imag2=Image.open("Cen_A_cropped.jpg")
imag2=imag2.convert('RGB')
r=[]
g=[]
b=[]
width2, height2=imag2.size
for count2 in range(width2):
    for i2 in range(height2):
        X,Y=count2,i2
        (R,G,B)=imag2.getpixel((X,Y))
        r.append(R)
        g.append(G)
        b.append(B)
average_r=sum(r)/len(r)
average_g=sum(g)/len(g)
average_b=sum(b)/len(b)
brightness_average=sqrt(0.299*(average_r**2) + 0.587*(average_g**2) + 0.114*(average_b**2))
print("Avg. brightness "+str(brightness_average))
def calculate_brightness(galaxy,ref_clus,clus_mag):
    delta_b=(galaxy/ref_clus)
    bright=delta_b**2
    mag=np.log(bright)/np.log(2.512)
    return mag+clus_mag
count=0
X,Y = 1556,1568
(R,G,B) = imag.getpixel((X,Y))
width, height=imag.size
brightness = sqrt(0.299*(R**2) + 0.587*(G**2) + 0.114*(B**2))
print("Magnitude: "+str((calculate_brightness(13050, 15.79,3.7))))
reference=brightness_average/(calculate_brightness(13050, 15.79,3.7)/6.84)
print("Reference: "+str(reference))
for count in range(width):
    for i in range(height):
        X,Y = count,i
        (R,G,B) = imag.getpixel((X,Y))
        brightness = sqrt(0.299*(R**2) + 0.587*(G**2) + 0.114*(B**2))
        if(reference<=brightness<=reference+3):
            x.append(X)
            y.append(Y)

#post processing----------------------------------------------------------------------------------------------------
for x2 in range(1110, 1977):
    for y2 in range(1069, 1905):
        X,Y=x2,y2
        if(X in x and Y in y):
            x.remove(X)
            y.remove(Y)
#-------------------------------------------------------------------------------------------------------------------
with imag as im:
    delta = 19
    draw = ImageDraw.Draw(im)
    for i in range(len(x)):
        draw.rectangle([x[i-delta],y[i-delta],x[i-delta],y[i-delta]], fill=(0,255,0))

    im.save("your_image.png")

Centaurus_A-DeNoiseAI-denoise.jpg

Cen_A_cropped.jpg


Solution

  • Your post-processing logic is flawed. You remove a bunch of X values in the range 1110-1977, without checking whether its corresponding Y value is also in the range of the box. Remove this code section instead and add that logic the first time you loop to gather your x and y coords.

    for count in range(width):
        for i in range(height):
            X,Y = count,i
            if 1110 <= X < 1977 and 1069 <= Y < 1905:    # add these
                continue                                 # two lines
            (R,G,B) = imag.getpixel((X,Y))
    

    However, there is a better way of doing the exact same thing by using numpy arrays. Instead of writing explicit loops, you can vectorise a lot of your computations.

    import numpy as np
    from PIL import Image, ImageDraw
    
    image = Image.open('Centaurus_A-DeNoiseAI-denoise.jpg').convert('RGB')
    img1 = np.array(image)
    img2 = np.array(Image.open('Cen_A_cropped.jpg').convert('RGB'))
    
    coeffs = np.array([.299, .587, .114])
    average = img2.mean(axis=(0, 1))
    brightness_average = np.sqrt(np.sum(average**2 * coeffs))
    reference = brightness_average / (calculate_brightness(13050, 15.79,3.7) / 6.84)
    print(f'Avg. brightness: {brightness_average}')
    print(f'Reference: {reference}')
    
    brightness = np.sqrt(np.sum(img1.astype(int)**2 * coeffs, axis=-1))
    accepted_brightness = (brightness >= reference) * (brightness <= reference + 3)
    pixels_used = np.ones((img1.shape[:2]), dtype=bool)
    pixels_used[1069:1905,1110:1977] = False
    rows, cols = np.where(accepted_brightness * pixels_used)
    
    with image as im:
        draw = ImageDraw.Draw(im)
        draw.point(list(zip(cols, rows)), fill=(0, 255, 0))
        image.save('out.png')
    

    The main trick used here is in the line

    rows, cols = np.where(accepted_brightness * pixels_used)
    

    accepted_brightess is a 2d array of each pixel with a boolean value whether its brightness is within your preferred range. pixels_used is another 2d boolean array, where every pixel is True, except from the pixels in the box near the centre you want to ignore. The combination of those two gives you the pixel coordinates that have the correct brightness and are not in the square in the centre.