OpenCV: per color pixel count in irregularly shaped area?

ghz 12hours ago ⋅ 1 views

Say I have a multicolored map of the United States, and I want to know how many pixels in a particular state (Nevada, for example) are purple, how many are green, and how many are white. Can I do this with OpenCV?

I've tried to tackle this by turning each state on an uncolored "basemap" into its own contour using cv2.drawContours, then overlaying the two images (This is where things start to feel wrong).

I know that I can then use the following:

Nevada = contours[21]
area = cv2.contourArea(Nevada)
print(area) 

to print the total number of pixels in a given state/contour, but I have no idea whether a similar function exists that will show me the number of pixels of a certain color within that state/contour. Is there a way to do this? Any guidance would be much appreciated.

Answer

Yes, you can absolutely use OpenCV to count the number of pixels of a certain color within a specific region (contour) in an image, such as the pixels of specific colors in Nevada on the map. Here’s how you can go about it:

Step-by-step process:

  1. Identify the contour of the state: Since you've already used cv2.drawContours to find contours of the states, you can use the contour of Nevada (or any other state you're interested in) to mask the region of interest (ROI).

  2. Mask the region using the contour: Once you have the contour, you can create a binary mask for the region corresponding to the state (e.g., Nevada). This will help isolate the area where you want to count the pixels of a specific color.

  3. Define the color range you’re interested in: You’ll need to specify the color range for the colors you're interested in (purple, green, white, etc.). Since OpenCV works in BGR format, you will need to define color ranges using cv2.inRange().

  4. Mask the colors inside the contour: Use the color mask to find only the pixels within the specific color range, and then combine that mask with the state contour mask to isolate the pixels of a given color inside the state.

  5. Count the pixels: Finally, you can count the number of pixels inside the mask for each color by using the cv2.countNonZero() function.

Example code:

Here’s a complete example based on your description:

import cv2
import numpy as np

# Load your map image
map_image = cv2.imread('path_to_map_image.png')

# Load the contours (assuming you've already computed them using cv2.findContours)
# Example: contours is a list of contours, where each element is a list of points for a state
# If contours are not yet computed, you'll need to find contours first
# Example: contours, _ = cv2.findContours(...)

# Select the contour of Nevada (assumed to be the 21st contour here)
nevada_contour = contours[21]

# Create a blank mask for the state of Nevada
mask = np.zeros(map_image.shape[:2], dtype=np.uint8)

# Draw the contour of Nevada on the mask (white color)
cv2.drawContours(mask, [nevada_contour], -1, 255, thickness=cv2.FILLED)

# Now you have a mask where only the pixels of Nevada are white (255) and all others are black (0)

# Create color masks for purple, green, and white
# For example, let's say you have a range for each color in BGR format
# You can fine-tune the values based on your specific color ranges

# Define the BGR color ranges (tune these based on the colors in your image)
# Example color ranges for purple, green, and white
lower_purple = np.array([128, 0, 128])  # Lower bound for purple
upper_purple = np.array([255, 50, 255])  # Upper bound for purple

lower_green = np.array([35, 50, 50])  # Lower bound for green
upper_green = np.array([85, 255, 85])  # Upper bound for green

lower_white = np.array([200, 200, 200])  # Lower bound for white
upper_white = np.array([255, 255, 255])  # Upper bound for white

# Apply inRange for each color
mask_purple = cv2.inRange(map_image, lower_purple, upper_purple)
mask_green = cv2.inRange(map_image, lower_green, upper_green)
mask_white = cv2.inRange(map_image, lower_white, upper_white)

# Now we need to combine the masks for the state (Nevada) and the color masks

# Mask the color regions inside Nevada's contour
mask_purple_nevada = cv2.bitwise_and(mask_purple, mask)
mask_green_nevada = cv2.bitwise_and(mask_green, mask)
mask_white_nevada = cv2.bitwise_and(mask_white, mask)

# Count the number of pixels in each color within the state of Nevada
purple_pixels = cv2.countNonZero(mask_purple_nevada)
green_pixels = cv2.countNonZero(mask_green_nevada)
white_pixels = cv2.countNonZero(mask_white_nevada)

# Print the results
print(f"Purple pixels in Nevada: {purple_pixels}")
print(f"Green pixels in Nevada: {green_pixels}")
print(f"White pixels in Nevada: {white_pixels}")

Explanation:

  1. Contour extraction (cv2.drawContours):

    • nevada_contour is the 21st element in the list of contours. We assume you’ve already identified this contour for Nevada.
  2. Color Range:

    • cv2.inRange is used to create binary masks for each color (purple, green, and white). You need to adjust the color ranges based on your map's specific color shades.
  3. Bitwise Operations:

    • We use cv2.bitwise_and to combine the color masks with the mask for Nevada. This ensures that only the pixels inside Nevada's contour and with the specified color are counted.
  4. Counting Pixels:

    • cv2.countNonZero(mask) counts the number of non-zero pixels in the mask, which gives the number of pixels of the target color inside Nevada.

Notes:

  • Fine-tuning color ranges: You may need to adjust the color ranges (lower_purple, upper_purple, etc.) to match the actual colors in your image more closely. Use a tool like cv2.imshow() to visualize the color masks and fine-tune the values.

  • Performance: For large images, this approach could be computationally intensive. If performance is a concern, consider resizing the image or using a more efficient method for contour detection.

Conclusion:

By using OpenCV’s contour drawing, color masking, and bitwise operations, you can isolate the pixels of specific colors inside a state on the map and count them. This should work well for identifying how many pixels in Nevada are purple, green, or white, as long as you adjust the color ranges appropriately for your map image.