概念
请注意,在图像的列中,紫色线应该去的列是最黑的吗?我们可以通过首先检测具有至少一定量黑色的第一列和最后一列来检测 ROI(感兴趣区域)。然后检测检测到的 2 列之间的行,其中白色首先开始并首先结束于 2 列。
代码
import cv2
import numpy as np
files = [f"img{i}.jpg" for i in range(1, 6)]
for file in files:
img = cv2.imread(file)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
sum_cols = thresh.sum(0)
indices = np.where(sum_cols < sum_cols.min() + 40000)[0]
x1, x2 = indices[0] - 50, indices[-1] + 50
diff1, diff2 = np.diff(thresh[:, [x1, x2]].T, 1)
y1_1, y2_1 = np.where(diff1)[0][:2]
y1_2, y2_2 = np.where(diff2)[0][:2]
y1, y2 = min(y1_1, y1_2), max(y2_1, y2_2)
img_canny = cv2.Canny(thresh[y1: y2, x1: x2], 50, 50)
contours, _ = cv2.findContours(img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.line(img, (x1, y1_1), (x2, y1_2), (255, 0, 160), 5)
cv2.line(img, (x1, y2_1), (x2, y2_2), (255, 0, 160), 5)
cv2.drawContours(img[y1: y2, x1: x2], contours, -1, (0, 0, 255), 10)
cv2.imshow("Image", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
输出
以下是程序将为您提供的每个不同图像输出的内容:
解释
- 导入必要的库:
import cv2
import numpy as np
- 既然你有摄像头,显然你将使用
cv2.VideoCapture() 方法。由于我只有您提供的图像,因此我将让程序读取每张图像。因此,将每个图像文件名存储到一个列表中(我有img1.jpg、img1.jpg、...img5.jpg),遍历名称并读取每个图像:
files = [f"img{i}.jpg" for i in range(1, 6)]
for file in files:
img = cv2.imread(file)
- 将每张图片转换为灰度图,使用
cv2.threshold()方法将灰度图转换为只有2个值; 0 表示小于或等于127 的每个像素,255 表示每个大于127 的像素:
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
- 为了找到
0s 最多的列(这意味着黑色最多),我们需要找到每一列的总和,其中最小的总和来自0s 最多的列。通过每列的总和,我们可以使用np.where() 方法来查找阈值图像中每一列的索引,其总和接近于检测到的最小总和。然后,我们可以将检测到的列的第一个索引和最后一个索引分别作为我们 ROI 的x1 和x2(以及50 像素的填充)::李>
sum_cols = thresh.sum(0)
indices = np.where(sum_cols < sum_cols.min() + 40000)[0]
x1, x2 = indices[0] - 50, indices[-1] + 50
- 为了找到顶行的
y1 和y2,我们需要在检测到的第一个边缘检测从0 到255 的第一次变化的索引列和检测到的列的最后一个边缘。同样,为了找到底线的y1 和y2,我们需要在检测到的列的第一边缘检测从255 到0 的第一次变化的索引并在检测到的列的最后一个边缘。最后,通过我们的 4 个y 坐标,我们可以通过获取第一行中最小的y 坐标和最大的y 坐标来获得ROI 的y1 和y2第二行:
diff1, diff2 = np.diff(thresh[:, [x1, x2]].T, 1)
y1_1, y2_1 = np.where(diff1)[0][:2]
y1_2, y2_2 = np.where(diff2)[0][:2]
y1, y2 = min(y1_1, y1_2), max(y2_1, y2_2)
- 现在我们有了投资回报率。我们可以使用 Canny 边缘检测器检测出 ROI 内物体的边缘,并使用
cv2.findContours() 方法检测边缘的轮廓:
img_canny = cv2.Canny(thresh[y1: y2, x1: x2], 50, 50)
contours, _ = cv2.findContours(img_canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
- 最后,我们可以在非二值图像上绘制线条和轮廓,并显示图像:
cv2.line(img, (x1, y1_1), (x2, y1_2), (255, 0, 160), 5)
cv2.line(img, (x1, y2_1), (x2, y2_2), (255, 0, 160), 5)
cv2.drawContours(img[y1: y2, x1: x2], contours, -1, (0, 0, 255), 10)
cv2.imshow("Image", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break