如何通过识别 Python 中的单条或多条水平线来分割图像?
How to split an image by identifying single or multiple horizontal lines in Python?
我想根据问题之间的淡灰线使用 Python 将图像分成多个部分。(如下图所示)。有办法吗?
您可以创建水平线的掩码,然后使用cv2.reduce
将图像缩小为列 使用 MAX
值。通过检测轮廓,您可以计算缩小蒙版中线条的起始 垂直 坐标,最后使用此信息 crop
图像。像这样:
# Set image path
imagePath = "D://opencvImages//"
imageName = "zlSGu.jpg"
# Read image:
inputImage = cv2.imread(imagePath + imageName)
# Store a copy for results:
inputCopy = inputImage.copy()
# Convert BGR to grayscale:
grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Set a lower and upper range for the threshold:
lowerThresh = 230
upperThresh = 235
# Get the lines mask:
mask = cv2.inRange(grayInput, lowerThresh, upperThresh)
这为您提供了线条掩码:
有点吵,你的图片被压缩了。让我们应用一个最小面积为 50 的 areaFilter
来滤除此噪声:
# Set a filter area on the mask:
minArea = 50
mask = areaFilter(minArea, mask)
这是过滤后的掩码:
现在,使用 MAX (255)
强度值将图像缩小为一列:
# Reduce matrix to a n row x 1 columns matrix:
reducedImage = cv2.reduce(mask, 1, cv2.REDUCE_MAX)
这是缩小后的图像,这里有点难看,但只显示灰色线条(缩小为一列)。现在,让我们检测这些线的起点和终点——它们实际上只是一个垂直坐标。我们可以从直线的边界框计算出这个坐标:
# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# Store the lines here:
separatingLines = []
# We need some dimensions of the original image:
imageHeight = inputCopy.shape[0]
imageWidth = inputCopy.shape[1]
# Look for the outer bounding boxes:
for _, c in enumerate(contours):
# Approximate the contour to a polygon:
contoursPoly = cv2.approxPolyDP(c, 3, True)
# Convert the polygon to a bounding rectangle:
boundRect = cv2.boundingRect(contoursPoly)
# Get the bounding rect's data:
[x, y, w, h] = boundRect
# Start point and end point:
lineCenter = y + (0.5 * h)
startPoint = (0,int(lineCenter))
endPoint = (int(imageWidth), int(lineCenter))
# Store the end point in list:
separatingLines.append( endPoint )
# Draw the line using the start and end points:
color = (0, 255, 0)
cv2.line(inputCopy, startPoint, endPoint, color, 2)
# Show the image:
cv2.imshow("inputCopy", inputCopy)
cv2.waitKey(0)
我另外将行的数据存储在 separatingLines
列表中。此外,仅出于显示目的,我在原始输入上绘制了线条。这是已识别行的图像:
现在,这些行未排序。让我们 sort
他们基于他们的垂直坐标。行正确排序后,我们可以 crop
每个部分,因为我们循环遍历行列表。像这样:
# Sort the list based on ascending Y values:
separatingLines = sorted(separatingLines, key=lambda x: x[1])
# The past processed vertical coordinate:
pastY = 0
# Crop the sections:
for i in range(len(separatingLines)):
# Get the current line width and starting y:
(sectionWidth, sectionHeight) = separatingLines[i]
# Set the ROI:
x = 0
y = pastY
cropWidth = sectionWidth
cropHeight = sectionHeight - y
# Crop the ROI:
currentCrop = inputImage[y:y + cropHeight, x:x + cropWidth]
cv2.imshow("Current Crop", currentCrop)
cv2.waitKey(0)
# Set the next starting vertical coordinate:
pastY = sectionHeight
这些是图像的裁剪部分。请注意,这些是单独的图片:
这是areaFilter
函数的定义和实现:
def areaFilter(minArea, inputImage):
# Perform an area filter on the binary blobs:
componentsNumber, labeledImage, componentStats, componentCentroids = \
cv2.connectedComponentsWithStats(inputImage, connectivity=4)
# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]
# Filter the labeled pixels based on the remaining labels,
# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')
return filteredImage
我想根据问题之间的淡灰线使用 Python 将图像分成多个部分。(如下图所示)。有办法吗?
您可以创建水平线的掩码,然后使用cv2.reduce
将图像缩小为列 使用 MAX
值。通过检测轮廓,您可以计算缩小蒙版中线条的起始 垂直 坐标,最后使用此信息 crop
图像。像这样:
# Set image path
imagePath = "D://opencvImages//"
imageName = "zlSGu.jpg"
# Read image:
inputImage = cv2.imread(imagePath + imageName)
# Store a copy for results:
inputCopy = inputImage.copy()
# Convert BGR to grayscale:
grayInput = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY)
# Set a lower and upper range for the threshold:
lowerThresh = 230
upperThresh = 235
# Get the lines mask:
mask = cv2.inRange(grayInput, lowerThresh, upperThresh)
这为您提供了线条掩码:
有点吵,你的图片被压缩了。让我们应用一个最小面积为 50 的 areaFilter
来滤除此噪声:
# Set a filter area on the mask:
minArea = 50
mask = areaFilter(minArea, mask)
这是过滤后的掩码:
现在,使用 MAX (255)
强度值将图像缩小为一列:
# Reduce matrix to a n row x 1 columns matrix:
reducedImage = cv2.reduce(mask, 1, cv2.REDUCE_MAX)
这是缩小后的图像,这里有点难看,但只显示灰色线条(缩小为一列)。现在,让我们检测这些线的起点和终点——它们实际上只是一个垂直坐标。我们可以从直线的边界框计算出这个坐标:
# Find the big contours/blobs on the filtered image:
contours, hierarchy = cv2.findContours(mask, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
# Store the lines here:
separatingLines = []
# We need some dimensions of the original image:
imageHeight = inputCopy.shape[0]
imageWidth = inputCopy.shape[1]
# Look for the outer bounding boxes:
for _, c in enumerate(contours):
# Approximate the contour to a polygon:
contoursPoly = cv2.approxPolyDP(c, 3, True)
# Convert the polygon to a bounding rectangle:
boundRect = cv2.boundingRect(contoursPoly)
# Get the bounding rect's data:
[x, y, w, h] = boundRect
# Start point and end point:
lineCenter = y + (0.5 * h)
startPoint = (0,int(lineCenter))
endPoint = (int(imageWidth), int(lineCenter))
# Store the end point in list:
separatingLines.append( endPoint )
# Draw the line using the start and end points:
color = (0, 255, 0)
cv2.line(inputCopy, startPoint, endPoint, color, 2)
# Show the image:
cv2.imshow("inputCopy", inputCopy)
cv2.waitKey(0)
我另外将行的数据存储在 separatingLines
列表中。此外,仅出于显示目的,我在原始输入上绘制了线条。这是已识别行的图像:
现在,这些行未排序。让我们 sort
他们基于他们的垂直坐标。行正确排序后,我们可以 crop
每个部分,因为我们循环遍历行列表。像这样:
# Sort the list based on ascending Y values:
separatingLines = sorted(separatingLines, key=lambda x: x[1])
# The past processed vertical coordinate:
pastY = 0
# Crop the sections:
for i in range(len(separatingLines)):
# Get the current line width and starting y:
(sectionWidth, sectionHeight) = separatingLines[i]
# Set the ROI:
x = 0
y = pastY
cropWidth = sectionWidth
cropHeight = sectionHeight - y
# Crop the ROI:
currentCrop = inputImage[y:y + cropHeight, x:x + cropWidth]
cv2.imshow("Current Crop", currentCrop)
cv2.waitKey(0)
# Set the next starting vertical coordinate:
pastY = sectionHeight
这些是图像的裁剪部分。请注意,这些是单独的图片:
这是areaFilter
函数的定义和实现:
def areaFilter(minArea, inputImage):
# Perform an area filter on the binary blobs:
componentsNumber, labeledImage, componentStats, componentCentroids = \
cv2.connectedComponentsWithStats(inputImage, connectivity=4)
# Get the indices/labels of the remaining components based on the area stat
# (skip the background component at index 0)
remainingComponentLabels = [i for i in range(1, componentsNumber) if componentStats[i][4] >= minArea]
# Filter the labeled pixels based on the remaining labels,
# assign pixel intensity to 255 (uint8) for the remaining pixels
filteredImage = np.where(np.isin(labeledImage, remainingComponentLabels) == True, 255, 0).astype('uint8')
return filteredImage