您的光线不好,正如 HansHirse 建议的那样,请尝试使您拍摄照片的条件正常化。但是,有一种方法可以在一定程度上使照明正常化并使其尽可能均匀。该方法称为增益除法。这个想法是您尝试构建背景模型,然后通过该模型对每个输入像素进行加权。在图像的大部分时间里,输出增益应该是相对恒定的。让我们试一试:
# imports:
import cv2
import numpy as np
# Reading an image in default mode:
inputImage = cv2.imread(path + fileName)
# Deep copy for results:
inputImageCopy = inputImage.copy()
# Get local maximum:
kernelSize = 30
maxKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
localMax = cv2.morphologyEx(inputImage, cv2.MORPH_CLOSE, maxKernel, None, None, 1, cv2.BORDER_REFLECT101)
# Perform gain division
gainDivision = np.where(localMax == 0, 0, (inputImage/localMax))
# Clip the values to [0,255]
gainDivision = np.clip((255 * gainDivision), 0, 255)
# Convert the mat type from float to uint8:
gainDivision = gainDivision.astype("uint8")
必须小心这些数据类型及其转换。结果如下:
如你所见,大部分背景现在都是统一的,这很酷,因为现在我们可以应用一个简单的阈值方法。让我们尝试Otsu's Thresholding 来获得元素的漂亮二进制掩码:
# Convert RGB to grayscale:
grayscaleImage = cv2.cvtColor(gainDivision, cv2.COLOR_BGR2GRAY)
# Get binary image via Otsu:
_, binaryImage = cv2.threshold(grayscaleImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
产生这个二进制掩码:
可以通过应用morphology 来改进掩码,让我们尝试加入那些应用温和的closing 操作的blob:
# Set kernel (structuring element) size:
kernelSize = 3
# Set morph operation iterations:
opIterations = 2
# Get the structuring element:
morphKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernelSize, kernelSize))
# Perform closing:
binaryImage = cv2.morphologyEx( binaryImage, cv2.MORPH_CLOSE, morphKernel, None, None, opIterations, cv2.BORDER_REFLECT101 )
这是结果:
好的,现在,为了完整起见,让我们尝试计算所有元素的bounding rectangles。我们还可以过滤小区域的 blob,并将每个边界矩形存储在一个列表中:
# Find the blobs on the binary image:
contours, hierarchy = cv2.findContours(binaryImage, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Store the bounding rectangles here:
rectanglesList = []
# Look for the outer bounding boxes (no children):
for _, c in enumerate(contours):
# Get blob area:
currentArea = cv2.contourArea(c)
# Set a min area threshold:
minArea = 100
if currentArea > minArea:
# Approximate the contour to a polygon:
contoursPoly = cv2.approxPolyDP(c, 3, True)
# Get the polygon's bounding rectangle:
boundRect = cv2.boundingRect(contoursPoly)
# Store rectangles in list:
rectanglesList.append(boundRect)
# Get the dimensions of the bounding rect:
rectX = boundRect[0]
rectY = boundRect[1]
rectWidth = boundRect[2]
rectHeight = boundRect[3]
# Set bounding rect:
color = (0, 0, 255)
cv2.rectangle( inputImageCopy, (int(rectX), int(rectY)),
(int(rectX + rectWidth), int(rectY + rectHeight)), color, 2 )
cv2.imshow("Rectangles", inputImageCopy)
cv2.waitKey(0)
最终的图像是这样的:
这是检测到的元素总数:
print("Elements found: "+str(len(rectanglesList)))
Elements found: 37
如您所见,存在误报。谷物的一点阴影被检测为实际的谷物。也许调整最小面积会解决这个问题。或者,如果您无论如何都要对每个谷物进行分类,您可以过滤这种噪音。