我如何选择所有与PIL中图像边缘相邻的黑色像素?

2024-05-15 09:32:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一组图像petri培养皿,不幸的是不是最高质量的(下面的例子,轴不是图像的一部分)。 dish1 我正在尝试选择背景并使用以下内容计算其面积(以像素为单位):

image = Image.open(path)
black_image = 1 * (np.asarray(image.convert('L')) < 12)
black_region = black_image.sum()

结果如下:

enter image description here

如果我对黑色像素的选择更严格,我会错过其他图像中的像素,如果我更宽松,我最终会选择太多的培养皿本身。有没有办法我只能选择亮度值小于12且与边相邻的像素?我也对openCV解决方案持开放态度。在


Tags: path图像imagenp质量单位像素open
3条回答

如果你把图片的最上面的一行和最下面的那一行,再加上阈值,你就会得到这个图表,我把最上面的一行放在上面,把下面的一行放在最下面,就在原始图像的限制之外,你不需要这样做,我只是说明一下这个技巧。在

enter image description here

现在看看线条从黑色变为白色,然后从白色变为黑色(顶部用红色圈起来)。不幸的是,你的图像有注解和坐标轴,我不得不删掉,这样你的数字就不一样了。在第一行/行,我的图像在第319列从黑色变为白色,在第648列又变回黑色。如果我把它们加在一起得到966除以2,x轴上的图像中心在第483列。在

从底线/行看,过渡(红色圆圈)在第234列和第736列,加起来是970列,平均值为485,所以我们知道圆心在垂直图像列483-485或484上。在

然后你现在应该能够计算出图像的中心和半径,并对图像进行遮罩以精确计算出背景。在

希望我没有过分简化这个问题,但是从我的观点来看,使用OpenCV和简单的阈值、形态学操作和^{}就可以了。在

请参见以下代码:

import cv2
import numpy as np

# Input
input = cv2.imread('images/x0ziO.png', cv2.IMREAD_COLOR)

# Input to grayscale
gray = cv2.cvtColor(input, cv2.COLOR_BGR2GRAY)

# Binary threshold
_, gray = cv2.threshold(gray, 20, 255, cv2.THRESH_BINARY)

# Morphological improvements of the mask
gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)))
gray = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11)))

# Find contours
cnts, _ = cv2.findContours(gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# Filter large size contours; at the end, there should only be one left
largeCnts = []
for cnt in cnts:
    if (cv2.contourArea(cnt) > 10000):
        largeCnts.append(cnt)

# Draw (filled) contour(s)
gray = np.uint8(np.zeros(gray.shape))
gray = cv2.drawContours(gray, largeCnts, -1, 255, cv2.FILLED)

# Calculate background pixel area
bgArea = input.shape[0] * input.shape[1] - cv2.countNonZero(gray)

# Put result on input image
input = cv2.putText(input, 'Background area: ' + str(bgArea), (20, 30), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.0, (255, 255, 255))

cv2.imwrite('images/output.png', input)

中间的“遮罩”图像如下所示:

Mask

最终输出如下:

Output

Since you are open to OpenCV approaches you could use a SimpleBlobDetector

显然,我得到的结果也不是完美的,因为有很多超参数需要设置。超参数使它非常灵活,所以它是一个不错的起点。在

{这是探测器做什么的细节}:

  1. 阈值化:通过对源图像进行阈值化,将源图像转换为多个二进制图像。这些阈值递增thresholdStep,直到maxThreshold。所以第一个阈值是minThreshold,第二个阈值是minThreshold + thresholdStep,第三个阈值是{},依此类推。在
  2. 分组:在每个二值图像中,连接的白色像素被分组在一起。我们把这些二进制水滴称为。在
  3. 合并:计算二值图像中二进制斑点的中心,并合并位于minDistBetweenBlobs附近的斑点。

  4. 中心和半径计算:计算并返回新合并水滴的中心和半径。

找到图片下方的代码。

Output Image

# Standard imports
import cv2
import numpy as np

# Read image
im = cv2.imread("petri.png", cv2.IMREAD_COLOR)

# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()

# Change thresholds
params.minThreshold = 0
params.maxThreshold = 255

# Set edge gradient
params.thresholdStep = 5

# Filter by Area.
params.filterByArea = True
params.minArea = 10

# Set up the detector with default parameters.
detector = cv2.SimpleBlobDetector_create(params)

# Detect blobs.
keypoints = detector.detect(im)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(im, keypoints, np.array([]), (0, 0, 255),
                                      cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show keypoints
cv2.imshow("Keypoints", im_with_keypoints)
cv2.waitKey(0)

相关问题 更多 >