Aastha Chowdhary
Aastha Chowdhary

Reputation: 13

Find the coordinates of red pixel in an image

Input picture

The picture above is the sample input I am taking and I want to find the coordinates of all the red pixels in this image and store it in a list and then, later on, iterate this list and draw circles around each of the coordinates which we found above in the image using OpenCV's cv2.circle function. I am doing the following:

coord = []

for i in range(img.shape[0]):
    for j in range(img.shape[1]):
        if img[i,j,0]!=0 and img[i,j,1]!=0 and img[i,j,2]!=255:
            img[i,j,0]=0
            img[i,j,1]=0
            img[i,j,2]=0
        else:
            img[i,j,0]=0
            img[i,j,1]=0
            img[i,j,2]=255  
            coord.append([i,j])

for l in range(len(coord)):
    px=coord[l][0]
    py=coord[l][1]
    cv2.circle(img,(px,py),5,(0,255,255),1)

But doing the above isn't making a circle on all the coordinates. I guess there is something wrong with the storing of coordinates and accessing them. Can anyone point out the error and help me please.

I am getting the following output which isn't correct

Upvotes: 1

Views: 3716

Answers (3)

Mark Setchell
Mark Setchell

Reputation: 207465

You'll be there all day with for loops! You'd be miles faster to do a morphological convolution with a ring kernel. I can show you how to do it quickly in ImageMagick but you can do it just the same with OpenCV.

Here is the basic command:

magick stars.png -morphology convolve ring:3.5,4.5 result.png

enter image description here

I'll run it again and this time, ask ImageMagick to show me the kernel - hopefully you can see the 1s forming a ring with inner radius 3.5 pixels and outer radius 4.5 pixels:

convert stars.png -define morphology:showkernel=1 -morphology convolve ring:3.5,4.5 result.png

Output

Kernel "Ring" of size 9x9+4+4 with values from 1 to 1
Forming a output range from 0 to 32 (Sum 32)
 0: nan     nan       1       1       1       1       1     nan     nan
 1: nan       1       1     nan     nan     nan       1       1     nan
 2:   1       1     nan     nan     nan     nan     nan       1       1
 3:   1     nan     nan     nan     nan     nan     nan     nan       1
 4:   1     nan     nan     nan     nan     nan     nan     nan       1
 5:   1     nan     nan     nan     nan     nan     nan     nan       1
 6:   1       1     nan     nan     nan     nan     nan       1       1
 7: nan       1       1     nan     nan     nan       1       1     nan
 8: nan     nan       1       1       1       1       1     nan     nan

There is an excellent description of the fascinating subject of morphology and how it works by Anthony Thyssen here.


Here is an OpenCV Python version of the same technique:

#!/usr/bin/env python3

import cv2
from skimage.draw import circle_perimeter
import numpy as np

# Load image
im = cv2.imread('stars.png')

# Ring shape structuring element 9x9 with a central circle radius 4 of 1s
selem = np.zeros((9, 9), dtype=np.uint8)
rr, cc = circle_perimeter(4, 4, 4)
selem[rr, cc] = 1

# Do the morphology just on red channel
dilated = cv2.dilate(im[...,2], selem, iterations=1)

# Put modified red channel back into original image and save
im[:,:,2] = dilated
cv2.imwrite('result.png', im)

The results are the same as above.

Upvotes: 2

Zabir Al Nazi Nabil
Zabir Al Nazi Nabil

Reputation: 11198

Your conditions are poorly written. First of all, it's better to search in a range, if the pixel value < 10 we can consider it black.

For red, we can check if B and G channels have value less than < 10 (you can change it), and R channel has value > 220.

coord = []

for i in range(img.shape[0]):
    for j in range(img.shape[1]):
        if img[i,j,0]<10 and img[i,j,1]<10 and img[i,j,2]>220:
            img[i,j,0]=0
            img[i,j,1]=0
            img[i,j,2]=255
            coord.append([i,j])
        else:
            img[i,j,0]=0
            img[i,j,1]=0
            img[i,j,2]=0

for l in range(len(coord)):
    px=coord[l][0]
    py=coord[l][1]
    cv2.circle(img,(py,px),5,(0,255,255),1)

Upvotes: 1

AKX
AKX

Reputation: 168967

The main problem here is that when writing those circles back out, your px and py are transposed. You'll have to do (py, px).

But, in addition, to make finding the red pixels much faster (135 times faster on my machine!), use a combination of

  • cv2.inRange (which generates a binary mask image, where the matching pixels are 1 and non-matching are 0)
  • np.argwhere (which returns the indexes of a matrix where the value is non-zero)
import cv2
import numpy as np

img = cv2.imread("RvegM.png")
red_pixels = np.argwhere(cv2.inRange(img, (0, 0, 250), (0, 0, 255)))
for px, py in red_pixels:
    cv2.circle(img, (py, px), 5, (0, 255, 255), 1)
cv2.imwrite("out.png", img)

out.png ends up looking like this:

enter image description here

Upvotes: 4

Related Questions