OpenCV:不规则形状区域中每种颜色的像素数?

2022-05-26 00:00:00 python opencv pixel

问题描述

假设我有一个multicolored map of the United States,我想知道某个州(例如内华达州)有多少像素是紫色的,有多少是绿色的,有多少是白色的。我可以使用OpenCV执行此操作吗?

我已尝试解决此问题,方法是使用cv2.drawContours将未着色的basemap上的每个状态转换为其自己的轮廓,然后将两个图像重叠(这是开始感觉不对劲的地方)。

我知道然后我可以使用以下内容:

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

打印给定状态/轮廓中的总像素数,但我不知道是否有类似的函数可以显示该状态/轮廓中特定颜色的像素数。有办法做到这一点吗?如有任何指导,我们将不胜感激。


解决方案

这里有一种方法可以在PythonOpenCV中实现。

  • 将底图图像作为灰度和阈值读取
  • 阅读地图图像
  • 从带阈值的垒手图像中获取所有轮廓
  • 定义颜色
  • 在轮廓上循环并在指定区域范围内选择轮廓(调整下限以获得更多状态轮廓)
  • 对于每个可接受的轮廓,在黑色图像上用白色填充
  • 屏蔽地图图像以仅显示给定的等高线
  • 使用NumPy对蒙版地图图像中的所有彩色像素求和
  • 打印索引和颜色计数
  • 可以选择查看每个蒙版地图区域
  • 获取轮廓的质心
  • 在地图图像质心处绘制索引号
  • 在循环结束后,保存已标注的地图图像


底图:

映射:

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)


已标记的地图:

终端列表输出:

相关文章