如何在 OpenCV 中找到多个框的中心?
How do I find the center of multiple boxes in OpenCV?
我正在编写一个 python 脚本,该脚本接收视频输入并且应该找到滴答钉板的中心。截至目前,我能够找到电路板的轮廓,但我不知道如何找到每个小盒子的中心。
代码如下:
import numpy as np
import cv2
video = cv2.VideoCapture(0)
while 1:
ret, frame = video.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray, 3)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
c = 0
for i in contours:
area = cv2.contourArea(i)
if area > 200:
cv2.drawContours(frame, contours, c, (0, 255, 0), 3)
c+=1
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
这是我得到的帧:
我不认为这是正确的方法,因为 tick-tack-toe 板线是连接的,我们很可能总是以单个大轮廓结束。为了使事情变得简单,您可以尝试检测图像中的线条。
您可以尝试cv2.HoughLinesP
或cv2.createLineSegmentDetector
来识别行。我相信你可以在检测到线路后继续前进。
此外,您可以查看 How to detect lines in OpenCV?,不同的用例也可以根据您的目的进行扩展。
概念
使用基本算法找到图像中的轮廓。在这种情况下,只会检测到 2 个轮廓;整个 tic-tac-toe 网格,以及 tic-tac-toe 网格的中心框。
得到2个等高线的convex hull,排序,这样我们就知道从哪里访问top-left点,top-right点等2组点中的每一个:
- 根据这些信息,我们可以计算出每个盒子的中心。对于 4 个角框中的每一个,只需找到构成框的 2 条线的尖端的中心。对于其余的方框,只需找到组成方框的4个点的中心:
代码
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 0)
img_canny = cv2.Canny(img_blur, 0, 100)
kernel = np.ones((2, 2))
img_dilate = cv2.dilate(img_canny, kernel, iterations=8)
return cv2.erode(img_dilate, kernel, iterations=2)
def convex_hull(cnt):
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, peri * 0.02, True)
return cv2.convexHull(approx).squeeze()
def centers(inner, outer):
c = inner[..., 0].argsort()
top_lef2, top_rit2 = sorted(inner[c][:2], key=list)
bot_lef2, bot_rit2 = sorted(inner[c][-2:], key=list)
c1 = outer[..., 0].argsort()
c2 = outer[..., 1].argsort()
top_lef, top_rit = sorted(outer[c1][:2], key=list)
bot_lef, bot_rit = sorted(outer[c1][-2:], key=list)
lef_top, lef_bot = sorted(outer[c2][:2], key=list)
rit_top, rit_bot = sorted(outer[c2][-2:], key=list)
yield inner.mean(0)
yield np.mean([top_lef, top_rit, top_lef2, top_rit2], 0)
yield np.mean([bot_lef, bot_rit, bot_lef2, bot_rit2], 0)
yield np.mean([lef_top, lef_bot, top_lef2, bot_lef2], 0)
yield np.mean([rit_top, rit_bot, top_rit2, bot_rit2], 0)
yield np.mean([top_lef, lef_top], 0)
yield np.mean([bot_lef, lef_bot], 0)
yield np.mean([top_rit, rit_top], 0)
yield np.mean([bot_rit, rit_bot], 0)
img = cv2.imread(r"D:/OpenCV Projects/TicTacToe centers/tictactoe.png")
contours, _ = cv2.findContours(process(img), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
inner, outer = sorted(map(convex_hull, contours), key=len)
for x, y in centers(inner, outer):
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
cv2.imshow("result", img)
cv2.waitKey(0)
输出
解释
- 导入必要的库:
import cv2
import numpy as np
- 定义一个函数,
process()
,接受一个图像数组和 return 一个二值图像 (即图像的处理版本)这将允许稍后在图像上进行适当的轮廓检测:
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 0)
img_canny = cv2.Canny(img_blur, 0, 100)
kernel = np.ones((2, 2))
img_dilate = cv2.dilate(img_canny, kernel, iterations=8)
return cv2.erode(img_dilate, kernel, iterations=2)
- 定义一个函数,
convex_hull()
,接受一个轮廓数组和 returns 一个数组,该数组是轮廓的近似版本的凸包:
def convex_hull(cnt):
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, peri * 0.02, True)
return cv2.convexHull(approx).squeeze()
- 定义一个函数,
centers()
,接受两个数组; tic-tac-toe 网格内框的凸包,以及整个 tic-tac-toe 网格的凸包。在函数中,进行必要的排序,使每个单独的点都在一个变量中,变量名与该点的位置相对应;这将允许轻松计算每个中心点:
def centers(inner, outer):
c = inner[..., 0].argsort()
top_lef2, top_rit2 = sorted(inner[c1][:2], key=list)
bot_lef2, bot_rit2 = sorted(inner[c1][-2:], key=list)
c1 = outer[..., 0].argsort()
c2 = outer[..., 1].argsort()
top_lef, top_rit = sorted(outer[c1][:2], key=list)
bot_lef, bot_rit = sorted(outer[c1][-2:], key=list)
lef_top, lef_bot = sorted(outer[c2][:2], key=list)
rit_top, rit_bot = sorted(outer[c2][-2:], key=list)
- 仍在
center()
函数中,生成框的中心。 np.mean()
方法,当使用 0
作为 axis
关键字参数时,将 return 传递给方法的坐标数组中坐标的中心:
yield inner.mean(0)
yield np.mean([top_lef, top_rit, top_lef2, top_rit2], 0)
yield np.mean([bot_lef, bot_rit, bot_lef2, bot_rit2], 0)
yield np.mean([lef_top, lef_bot, top_lef2, bot_lef2], 0)
yield np.mean([rit_top, rit_bot, top_rit2, bot_rit2], 0)
yield np.mean([top_lef, lef_top], 0)
yield np.mean([bot_lef, lef_bot], 0)
yield np.mean([top_rit, rit_top], 0)
yield np.mean([bot_rit, rit_bot], 0)
- 读入图像,求其轮廓(也是先用
process()
函数对图像进行处理),求轮廓的凸包,绘制在具有 centers()
函数的框的中心:
img = cv2.imread(r"D:/OpenCV Projects/TicTacToe centers/tictactoe.png")
contours, _ = cv2.findContours(process(img), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
inner, outer = sorted(map(convex_hull, contours), key=len)
for x, y in centers(inner, outer):
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
- 最后,显示生成的图像:
cv2.imshow("result", img)
cv2.waitKey(0)
我正在编写一个 python 脚本,该脚本接收视频输入并且应该找到滴答钉板的中心。截至目前,我能够找到电路板的轮廓,但我不知道如何找到每个小盒子的中心。
代码如下:
import numpy as np
import cv2
video = cv2.VideoCapture(0)
while 1:
ret, frame = video.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray, 3)
thresh = cv2.adaptiveThreshold(blur, 255, 1, 1, 11, 2)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
c = 0
for i in contours:
area = cv2.contourArea(i)
if area > 200:
cv2.drawContours(frame, contours, c, (0, 255, 0), 3)
c+=1
cv2.imshow("frame", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
这是我得到的帧:
我不认为这是正确的方法,因为 tick-tack-toe 板线是连接的,我们很可能总是以单个大轮廓结束。为了使事情变得简单,您可以尝试检测图像中的线条。
您可以尝试cv2.HoughLinesP
或cv2.createLineSegmentDetector
来识别行。我相信你可以在检测到线路后继续前进。
此外,您可以查看 How to detect lines in OpenCV?,不同的用例也可以根据您的目的进行扩展。
概念
使用基本算法找到图像中的轮廓。在这种情况下,只会检测到 2 个轮廓;整个 tic-tac-toe 网格,以及 tic-tac-toe 网格的中心框。
得到2个等高线的convex hull,排序,这样我们就知道从哪里访问top-left点,top-right点等2组点中的每一个:
- 根据这些信息,我们可以计算出每个盒子的中心。对于 4 个角框中的每一个,只需找到构成框的 2 条线的尖端的中心。对于其余的方框,只需找到组成方框的4个点的中心:
代码
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 0)
img_canny = cv2.Canny(img_blur, 0, 100)
kernel = np.ones((2, 2))
img_dilate = cv2.dilate(img_canny, kernel, iterations=8)
return cv2.erode(img_dilate, kernel, iterations=2)
def convex_hull(cnt):
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, peri * 0.02, True)
return cv2.convexHull(approx).squeeze()
def centers(inner, outer):
c = inner[..., 0].argsort()
top_lef2, top_rit2 = sorted(inner[c][:2], key=list)
bot_lef2, bot_rit2 = sorted(inner[c][-2:], key=list)
c1 = outer[..., 0].argsort()
c2 = outer[..., 1].argsort()
top_lef, top_rit = sorted(outer[c1][:2], key=list)
bot_lef, bot_rit = sorted(outer[c1][-2:], key=list)
lef_top, lef_bot = sorted(outer[c2][:2], key=list)
rit_top, rit_bot = sorted(outer[c2][-2:], key=list)
yield inner.mean(0)
yield np.mean([top_lef, top_rit, top_lef2, top_rit2], 0)
yield np.mean([bot_lef, bot_rit, bot_lef2, bot_rit2], 0)
yield np.mean([lef_top, lef_bot, top_lef2, bot_lef2], 0)
yield np.mean([rit_top, rit_bot, top_rit2, bot_rit2], 0)
yield np.mean([top_lef, lef_top], 0)
yield np.mean([bot_lef, lef_bot], 0)
yield np.mean([top_rit, rit_top], 0)
yield np.mean([bot_rit, rit_bot], 0)
img = cv2.imread(r"D:/OpenCV Projects/TicTacToe centers/tictactoe.png")
contours, _ = cv2.findContours(process(img), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
inner, outer = sorted(map(convex_hull, contours), key=len)
for x, y in centers(inner, outer):
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
cv2.imshow("result", img)
cv2.waitKey(0)
输出
解释
- 导入必要的库:
import cv2
import numpy as np
- 定义一个函数,
process()
,接受一个图像数组和 return 一个二值图像 (即图像的处理版本)这将允许稍后在图像上进行适当的轮廓检测:
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img_blur = cv2.GaussianBlur(img_gray, (5, 5), 0)
img_canny = cv2.Canny(img_blur, 0, 100)
kernel = np.ones((2, 2))
img_dilate = cv2.dilate(img_canny, kernel, iterations=8)
return cv2.erode(img_dilate, kernel, iterations=2)
- 定义一个函数,
convex_hull()
,接受一个轮廓数组和 returns 一个数组,该数组是轮廓的近似版本的凸包:
def convex_hull(cnt):
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, peri * 0.02, True)
return cv2.convexHull(approx).squeeze()
- 定义一个函数,
centers()
,接受两个数组; tic-tac-toe 网格内框的凸包,以及整个 tic-tac-toe 网格的凸包。在函数中,进行必要的排序,使每个单独的点都在一个变量中,变量名与该点的位置相对应;这将允许轻松计算每个中心点:
def centers(inner, outer):
c = inner[..., 0].argsort()
top_lef2, top_rit2 = sorted(inner[c1][:2], key=list)
bot_lef2, bot_rit2 = sorted(inner[c1][-2:], key=list)
c1 = outer[..., 0].argsort()
c2 = outer[..., 1].argsort()
top_lef, top_rit = sorted(outer[c1][:2], key=list)
bot_lef, bot_rit = sorted(outer[c1][-2:], key=list)
lef_top, lef_bot = sorted(outer[c2][:2], key=list)
rit_top, rit_bot = sorted(outer[c2][-2:], key=list)
- 仍在
center()
函数中,生成框的中心。np.mean()
方法,当使用0
作为axis
关键字参数时,将 return 传递给方法的坐标数组中坐标的中心:
yield inner.mean(0)
yield np.mean([top_lef, top_rit, top_lef2, top_rit2], 0)
yield np.mean([bot_lef, bot_rit, bot_lef2, bot_rit2], 0)
yield np.mean([lef_top, lef_bot, top_lef2, bot_lef2], 0)
yield np.mean([rit_top, rit_bot, top_rit2, bot_rit2], 0)
yield np.mean([top_lef, lef_top], 0)
yield np.mean([bot_lef, lef_bot], 0)
yield np.mean([top_rit, rit_top], 0)
yield np.mean([bot_rit, rit_bot], 0)
- 读入图像,求其轮廓(也是先用
process()
函数对图像进行处理),求轮廓的凸包,绘制在具有centers()
函数的框的中心:
img = cv2.imread(r"D:/OpenCV Projects/TicTacToe centers/tictactoe.png")
contours, _ = cv2.findContours(process(img), cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
inner, outer = sorted(map(convex_hull, contours), key=len)
for x, y in centers(inner, outer):
cv2.circle(img, (int(x), int(y)), 5, (0, 0, 255), -1)
- 最后,显示生成的图像:
cv2.imshow("result", img)
cv2.waitKey(0)