Reputation: 13
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.
Upvotes: 1
Views: 831
Reputation: 53089
Here is one way to do that in Python/OpenCV.
import cv2
import numpy as np
# read basemap image as grayscale
basemap = cv2.imread('basemap.png', cv2.COLOR_BGR2GRAY)
# threshold basemap and make single channel
thresh = cv2.threshold(basemap, 200, 255, cv2.THRESH_BINARY)[1]
thresh = thresh[:,:,0]
# read map
map = cv2.imread('map.png')
# define colors
red = (255,0,255)
green = (125,196,147)
blue = (232,197,159)
orange = (102,102,224)
# get contours
contours = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# print table header
print('{:^15}{:^15}{:^15}{:^15}{:^15}'.format("index", "red_count", "green_count", "blue_count", "orange_count"))
# initialize labeled map
map_labeled = map.copy()
# loop over index and corresponding contour (cntr)
for index, cntr in enumerate(contours):
# filter on area
area = cv2.contourArea(cntr)
if area > 1000 and area < 20000 :
# draw contours on black image
mask = np.zeros_like(basemap)
cv2.drawContours(mask, contours, index, (255,255,255), cv2.FILLED)
# copy map
map_masked = map.copy()
# do bitwise_and between copied map and mask for a given contour
map_masked = cv2.bitwise_and(map_masked, mask)
# get counts for given contour
red_count = np.sum(np.where((map_masked == red).all(axis=2)))
green_count = np.sum(np.where((map_masked == green).all(axis=2)))
blue_count = np.sum(np.where((map_masked == blue).all(axis=2)))
orange_count = np.sum(np.where((map_masked == orange).all(axis=2)))
# print index and counts
print('{:^15}{:^15}{:^15}{:^15}{:^15}'.format(index, red_count, green_count, blue_count, orange_count))
# get centroid of contour for label placement
M = cv2.moments(cntr)
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# label map with index
map_labeled = cv2.putText(map_labeled, str(index), (cx,cy), cv2.FONT_HERSHEY_PLAIN, 0.75, (0,0,0))
# view each state region from map isolated by mask from contour
# remove the following 3 lines if you do not want to hit the space key for each contour
cv2.imshow("index", map_masked)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save labeled map
cv2.imwrite('map_labeled.png', map_labeled)
Terminal Listing Output:
Upvotes: 1