使用 OpenCV 计算图像中所有不同区域的像素

Posted

技术标签:

【中文标题】使用 OpenCV 计算图像中所有不同区域的像素【英文标题】:Counting pixels in all distinct regions in an image using OpenCV 【发布时间】:2022-01-14 20:18:16 【问题描述】:

我有一个看起来像这样的图像(它被炸毁了,但每个正方形只有一个像素)。我希望能够计算每个黑色区域中的像素数。区域被定义为水平和垂直相邻的像素(不是对角线)。所以在这张图片中有四个。输出可以是某种字典,其中区域标识符(可能像质心)映射到该区域中的像素数,或者只是一个总数列表,如 [14,3,9,9]。

我似乎无法弄清楚如何让实际的像素数起作用。

另外,我能找到的最接近的是this question,但我仍然无法让它为我工作。

我在谷歌上搜索了 connectedComponentsWithStats 函数的工作原理。我尝试模糊图像和一切以摆脱可能的“对角线”连接。我假设它是我应该使用的那个,但也许有更好的东西?

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import cv2

d = [[9, 8, 9, 3, 2], [8, 7, 8, 9, 1], [9, 6, 5, 8, 9], [9, 7, 6, 7, 9], [9, 8, 7, 8, 9], [6, 9, 8, 9, 4], [5, 6, 9, 4, 3], [6, 7, 8, 9, 2], [7, 8, 9, 2, 1], [8, 9, 2, 1, 0]]
a = np.array([[0 if x < 9 else 255 for x in z] for z in d])

img = Image.fromarray(a.astype(np.uint8), mode='L')
img.save("test.jpg")

img = cv2.imread('test.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (1, 1), 0)

components, output, stats, centroids = cv2.connectedComponentsWithStats(blurred, connectivity = 4)
output

【问题讨论】:

不要模糊图像。只需将 connectivity In connectedComponentsWithStats() 更改为 4 而不是 8。 【参考方案1】:

如果你想计算像素,OpenCV 有一个函数:cv2.countNonZero。这会计算白色像素,但您可以通过首先计算图像的高度和宽度来轻松计算黑色像素。

您的代码将被修改如下:

import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import cv2


d = [[9, 8, 9, 3, 2], [8, 7, 8, 9, 1], [9, 6, 5, 8, 9], [9, 7, 6, 7, 9], [9, 8, 7, 8, 9], [6, 9, 8, 9, 4],
     [5, 6, 9, 4, 3], [6, 7, 8, 9, 2], [7, 8, 9, 2, 1], [8, 9, 2, 1, 0]]
a = np.array([[0 if x < 9 else 255 for x in z] for z in d])

img = Image.fromarray(a.astype(np.uint8), mode='L')
img.save("test.jpg")

img = cv2.imread('test.jpg')

# Display image:
cv2.imshow("Image", img)
cv2.waitKey(0)

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (1, 1), 0)

# Count number of white pixels:
whitePixels = cv2.countNonZero(blurred)

print("White pixels: "+str(whitePixels))

# Get image height and width:
(imgHeight, imgWidth) = blurred.shape[:2]

# Get number of black pixels:
blackPixels = imgHeight*imgWidth - whitePixels

print("Black pixels: "+str(blackPixels))

哪些打印:

White pixels: 34
Black pixels: 16

专业提示:不要使用jpg 图像格式,它是有损的 - 它会压缩和修改像素值。使用无损格式,例如png

【讨论】:

哦,对不起,我的意思是我想分别为每个区域计算它们。就像有某种映射,比如从质心到该区域的像素数。【参考方案2】:

connectedComponentsWithStats已经为您提供area,这是图片中每个组件(非零像素)中的像素数。

a = np.where(np.array(d) < 9, 255, 0).astype(np.uint8)

(nlabels, labels, stats, centroids) = cv.connectedComponentsWithStats(a, connectivity=4)

assert nlabels == 5 # = background + 4 components
print(stats[:, cv.CC_STAT_AREA]) # area: array([15, 14,  3,  9,  9], dtype=int32)

注意Beaker的评论:CC有一个连接参数,你应该设置为“4-way”。

您应该反转您的图片,使黑色像素变为白色。 white is non-zero 是真的,它总是前景,这是 CC 调用期望使用的。

文档: https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gac7099124c0390051c6970a987e7dc5c5

高斯模糊会将任何二值化图像变成灰度值汤。 不要应用高斯模糊,或者如果你需要它,然后再阈值。 connectedComponents* 将威胁任何非零(!)像素作为前景

【讨论】:

我的连接一直设置为 4,正如您在我的代码中看到的那样。我想我不明白这个区域参数在哪里。我看到是cv CC_STAT_AREA,但我将其应用于哪个元素? components.CC_STAT_AREA 不起作用。我改变了它,使段由白色像素组成,但 connectedComponentsWithStats 中的第一个组件返回“3”,应该有 4 个段。所以整个事情让我很困惑:( 你能举个例子吗? 您需要先提供准确的数据。该插图并非完全黑白,并且在边框附近有单像素黑线。我必须纠正这些事情。 生成图片的代码就在我的帖子里.....图片看起来不一样,因为我不得不把它炸毁并截屏............我可以上传5x10 像素的图片,当我回到家时。我只是认为发布 sn-p 会更容易。 “管理”?我什至没有尝试,因为毫无疑问它会起作用。你是否?交互提示:a = np.where(np.array(d) &lt; 9, 255, 0).astype(np.uint8); (nlabels, labels, stats, centroids) = cv.connectedComponentsWithStats(a, connectivity=4); nlabels; labels; stats; centroids,区域为第4列(最后一列),第一行为背景

以上是关于使用 OpenCV 计算图像中所有不同区域的像素的主要内容,如果未能解决你的问题,请参考以下文章

youcans 的 OpenCV 例程200篇171.SLIC 超像素区域分割

youcans 的 OpenCV 例程200篇175.超像素区域分割方法比较

opencv如何读取多边形区域内的像素值?

OpenCV C++ 手势识别

OpenCV实战(10)——积分图像详解

opencv进阶1