计算图像中白色背景上蓝调线的数量
Posted
技术标签:
【中文标题】计算图像中白色背景上蓝调线的数量【英文标题】:Count number of the blues lines on white background in the image 【发布时间】:2020-06-09 00:12:17 【问题描述】:我有 1000 张这样的图片
我通过tutorial 尝试了 cv2 库和霍夫线变换,但我不明白这是我的情况吗?我有 1000 张图像,也就是说,我几乎无法手动输入任何数据(如宽度或坐标)。
按照逻辑,我必须找到图像中的每个蓝色像素并检查邻居的像素是否为白色
所以为此我必须知道PNG图像的像素格式。我必须如何读取图像,例如公共文件open (path, 'r') as file_object
或者它必须是带有库的一些特殊方法?
【问题讨论】:
【参考方案1】:你可以数行数并除以二...
#!/usr/bin/env python3
import numpy as np
from PIL import Image
from scipy.ndimage import generic_filter
# Line ends filter
def lineEnds(P):
global ends
# Central pixel and one other must be 255 for line end
if (P[4]==255) and np.sum(P)==510:
ends += 1
return 255
return 0
# Global count of line ends
ends = 0
# Open image and make into Numpy array
im = Image.open('lines.png').convert('L')
im = np.array(im)
# Invert and threshold for white lines on black
im = 255 - im
im[im>0] = 255
# Save result, just for debug
Image.fromarray(im).save('intermediate.png')
# Find line ends
result = generic_filter(im, lineEnds, (3, 3))
print(f'Line ends: ends')
# Save result, just for debug
Image.fromarray(result).save('result.png')
输出
Line ends: 16
请注意,这不是生产质量代码。您应该添加额外的检查,例如线端的总数是偶数,并在边缘周围添加 1 像素宽的黑色边框以防线接触边缘等等。
【讨论】:
除以二的好方法【参考方案2】:乍一看,问题看起来很简单——转换为二进制图像,使用Hough Line Transform,并计算行数,但它不起作用......
注意:我找到的解决方案是基于寻找和合并轮廓,但使用霍夫变换可能更稳健。相反在合并轮廓的过程中,你可能会发现很多短线,然后根据角度和边缘的接近度将它们合并成长线。
以下解决方案使用以下阶段:
将图像转换为黑底白线的二值图像。 分割线之间的交叉点(用黑色填充交叉点)。 在二值图像中查找轮廓(并删除小轮廓)。 合并具有闭合角度和闭合边缘的轮廓。这是一个工作代码示例:
import cv2
import numpy as np
def box2line(box):
"""Convert rotated rectangle box into two array of two points that defines a line"""
b = box.copy()
for i in range(2):
p0 = b[0]
dif0 = (b[1:, 0] - p0[0])**2 + (b[1:, 1] - p0[1])**2
min_idx = np.argmin(dif0, 0)
b = np.delete(b, min_idx+1, 0)
return b
def minlinesdist(line, line2):
"""Finds minimum distance between any two edges of two lines"""
a0 = line[0, :]
a1 = line[1, :]
b0 = line2[0, :]
b1 = line2[1, :]
d00 = np.linalg.norm(a0 - b0)
d01 = np.linalg.norm(a0 - b1)
d10 = np.linalg.norm(a1 - b0)
d11 = np.linalg.norm(a1 - b1)
min_dist = np.min((d00, d01, d10, d11))
return min_dist
def get_rect_box_line_and_angle(c):
"""Return minAreaRect, boxPoints, line and angle of contour"""
rect = cv2.minAreaRect(c)
box = cv2.boxPoints(rect)
line = box2line(box)
angle = rect[2]
return rect, box, line, angle
(cv_major_ver, cv_minor_ver, cv_subminor_ver) = (cv2.__version__).split('.') # Get version of OpenCV
im = cv2.imread('BlueLines.png') # Read input image
# Convert image to binary image with white lines on black background
################################################################################
gray = im[:, :, 1] # Get only the green color channel (the blue lines should be black).
# Apply threshold
ret, thresh_gray = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)
# Invert polarity
thresh_gray = 255 - thresh_gray
################################################################################
# Split intersection points between lines (fill crossing points with black).
################################################################################
thresh_float = thresh_gray.astype(float) / 255 # Convert to float with range [0, 1]
thresh_float = cv2.filter2D(thresh_float, -1, np.ones((3, 3))) # Filter with ones 5x5
# Find pixels with "many" neighbors
thresh_intersect = np.zeros_like(thresh_gray)
thresh_intersect[(thresh_float > 3)] = 255; # Image of intersection points only.
thresh_gray[(thresh_float > 3)] = 0;
################################################################################
# Find contours in thresh_gray, and remove small contours.
################################################################################
if int(cv_major_ver) < 4:
_, contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
else:
contours, _ = cv2.findContours(thresh_gray, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# Remove small contours, because their angle is not well defined
fcontours = []
for i in range(len(contours)):
c = contours[i]
if c.shape[0] > 6: # Why 6?
fcontours.append(c)
contours = fcontours
# Starting value.
n_lines = len(contours)
################################################################################
# Merge contours with close angles, and close edges
# Loop decreases n_lines when two lines are merged.
# Note: The solution is kind of "brute force" solution, and can be better.
################################################################################
# https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_contours/py_contour_features/py_contour_features.html
# Fitting a Line
rows,cols = im.shape[:2]
for i in range(len(contours)):
c = contours[i]
rect, box, line, angle = get_rect_box_line_and_angle(c)
for j in range(i+1, len(contours)):
c2 = contours[j]
rect2 = cv2.minAreaRect(c2)
box2 = cv2.boxPoints(rect2)
line2 = box2line(box2)
angle2 = rect2[2]
angle_diff = (angle - angle2 + 720) % 180 # Angle difference in degrees (force it to be positive number in range [0, 180].
angle_diff = np.minimum(angle_diff, 180 - angle_diff)
min_dist = minlinesdist(line, line2) # Minimum distance between any two edges of line and line2
if (angle_diff < 3) and (min_dist < 20):
color = (int((i+3)*100 % 255),int((i+3)*50 % 255), int((i+3)*70 % 255))
# https://***.com/questions/22801545/opencv-merge-contours-together
# Merge contours together
tmp = np.vstack((c, c2))
c = cv2.convexHull(tmp)
# Draw merged contour (for testing)
im = cv2.drawContours(im, [c], 0, color, 2)
# Replace contour with merged one.
contours[j] = c
n_lines -= 1 # Subtract lines counter
break
################################################################################
print('Number of lines = '.format(n_lines))
# Display result (for testing):
cv2.imshow('thresh_gray', thresh_gray)
cv2.imshow('im', im)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
Number of lines = 8
thresh_gray
(拆分前):
thresh_gray
(拆分后):
im
:
注意: 我知道解决方案并不完美,并且不会在所有 1000 张图像上找到完美的结果。 我认为使用霍夫变换和合并线会有一个更好的变化,这将产生完美的结果。
【讨论】:
但它是关于计算行数的吗?与相交的线如何计算? @YunusTemurlenk 是的,我相信在二进制图像上计算行数有一个已知的解决方案(使用霍夫线变换)。以上是关于计算图像中白色背景上蓝调线的数量的主要内容,如果未能解决你的问题,请参考以下文章