用于检测视频中矩形形状(文本光标)的对象检测模型?
Object detection model for detecting rectangular shape (text cursor) in a video?
我目前正在进行一些研究,以检测和定位屏幕录制视频中的文本光标(您知道,闪烁的矩形形状指示您在计算机上键入时字符的位置)。为此,我使用自定义对象数据集训练了 YOLOv4 模型(我从 here 中获取了参考)并计划还实施 DeepSORT 来跟踪移动光标。
下面是我用来训练YOLOv4的训练数据示例:
这是我想要实现的目标:
您是否认为使用 YOLOv4 + DeepSORT 对这项任务来说被认为是矫枉过正?我问是因为到目前为止,模型只能成功检测到包含文本光标的视频帧的 70%-80%。如果它毕竟是矫枉过正,你知道可以为这个任务实施的任何其他方法吗?
无论如何,我计划不仅从 Visual Studio 代码 window,而且从浏览器(例如,Google Chrome)检测文本光标和文本处理器(例如 Microsoft Word)。像这样:
我正在考虑将滑动 Window 方法作为替代方法,但根据我的阅读,该方法可能会消耗大量资源并且执行速度较慢。我也在考虑从 OpenCV (like this) 进行模板匹配,但我认为它不会比 YOLOv4 表现得更好更快。
约束是关于性能速度(即,给定时间量可以处理多少帧)和检测精度(即,我想避免将字母 'l' 或“1”检测为文本光标,因为这些字符在某些字体中是相似的)。但我认为更高的精度和更慢的 FPS 是可以接受的。
我目前正在为此使用 Python、Tensorflow 和 OpenCV。
非常感谢!
如果光标是屏幕上唯一的移动对象,这将起作用。这是之前和之后:
之前:
之后:
代码:
import cv2
import numpy as np
BOX_WIDTH = 10
BOX_HEIGHT = 20
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = np.ones((5, 5))
img_canny = cv2.Canny(img_gray, 50, 50)
return img_canny
def get_contour(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
return max(contours, key=cv2.contourArea)
def get_line_tip(cnt1, cnt2):
x1, y1, w1, h1 = cv2.boundingRect(cnt1)
if h1 > BOX_HEIGHT / 2:
if np.any(cnt2):
x2, y2, w2, h2 = cv2.boundingRect(cnt2)
if x1 < x2:
return x1, y1
return x1 + w1, y1
def get_rect(x, y):
half_width = BOX_WIDTH // 2
lift_height = BOX_HEIGHT // 6
return (x - half_width, y - lift_height), (x + half_width, y + BOX_HEIGHT - lift_height)
cap = cv2.VideoCapture("screen_record.mkv")
success, img_past = cap.read()
cnt_past = np.array([])
line_tip_past = 0, 0
while True:
success, img_live = cap.read()
if not success:
break
img_live_processed = process_img(img_live)
img_past_processed = process_img(img_past)
img_diff = cv2.bitwise_xor(img_live_processed, img_past_processed)
cnt = get_contour(img_diff)
line_tip = get_line_tip(cnt, cnt_past)
if line_tip:
cnt_past = cnt
line_tip_past = line_tip
else:
line_tip = line_tip_past
rect = get_rect(*line_tip)
img_past = img_live.copy()
cv2.rectangle(img_live, *rect, (0, 0, 255), 2)
cv2.imshow("Cursor", img_live)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
分解:
- 导入必要的库:
import cv2
import numpy as np
- 根据光标的大小定义跟踪框的大小:
BOX_WIDTH = 10
BOX_HEIGHT = 20
- 定义一个函数来将帧处理成边缘:
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = np.ones((5, 5))
img_canny = cv2.Canny(img_gray, 50, 50)
return img_canny
- 定义一个函数来检索图像中面积最大的轮廓(光标不需要很大也能正常工作,如果需要可以很小):
def get_contour(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
return max(contours, key=cv2.contourArea)
- 定义一个包含 2 个轮廓的函数,一个是光标的轮廓 + 当前帧的一些文本,另一个是轮廓 + 光标轮廓的一些杂散文本 + 来自帧之前。通过这两个轮廓,我们可以识别光标是向左移动还是向右移动:
def get_line_tip(cnt1, cnt2):
x1, y1, w1, h1 = cv2.boundingRect(cnt1)
if h1 > BOX_HEIGHT / 2:
if np.any(cnt2):
x2, y2, w2, h2 = cv2.boundingRect(cnt2)
if x1 < x2:
return x1, y1
return x1 + w1, y1
- 定义一个接受光标尖端点的函数,return 一个基于之前定义的
BOX_WIDTH
和 BOX_HEIGHT
常量的框:
def get_rect(x, y):
half_width = BOX_WIDTH // 2
lift_height = BOX_HEIGHT // 6
return (x - half_width, y - lift_height), (x + half_width, y + BOX_HEIGHT - lift_height)
- 为视频定义一个捕获设备,并从视频的开头删除一帧并将其存储在一个变量中,该变量将用作每帧之前的帧。还为过去的轮廓和过去的线尖定义临时值:
cap = cv2.VideoCapture("screen_record.mkv")
success, img_past = cap.read()
cnt_past = np.array([])
line_tip_past = 0, 0
- 使用
while
循环,并从视频中读取。处理视频中的帧和该帧之前的帧:
while True:
success, img_live = cap.read()
if not success:
break
img_live_processed = process_img(img_live)
img_past_processed = process_img(img_past)
- 对于处理后的帧,我们可以使用
cv2.bitwise_xor
方法找到帧之间的差异,以获取屏幕上的移动位置。然后,我们可以使用定义的 get_contour
函数找到 2 帧之间的运动轮廓:
img_diff = cv2.bitwise_xor(img_live_processed, img_past_processed)
cnt = get_contour(img_diff)
- 有了轮廓,我们就可以利用定义的
get_line_tip
函数来找到光标的尖端。如果找到提示,将其保存到 line_tip_past
变量中以供下一次迭代使用,如果未找到提示,我们可以将之前保存的提示用作当前提示:
line_tip = get_line_tip(cnt, cnt_past)
if line_tip:
cnt_past = cnt
line_tip_past = line_tip
else:
line_tip = line_tip_past
- 现在我们使用光标提示和我们之前定义的
get_rect
函数定义一个矩形,并将其绘制到当前帧上。但是在绘制之前,我们将帧保存为下一次迭代当前帧之前的帧:
rect = get_rect(*line_tip)
img_past = img_live.copy()
cv2.rectangle(img_live, *rect, (0, 0, 255), 2)
- 最后,我们显示框架:
cv2.imshow("Cursor", img_live)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
我目前正在进行一些研究,以检测和定位屏幕录制视频中的文本光标(您知道,闪烁的矩形形状指示您在计算机上键入时字符的位置)。为此,我使用自定义对象数据集训练了 YOLOv4 模型(我从 here 中获取了参考)并计划还实施 DeepSORT 来跟踪移动光标。
下面是我用来训练YOLOv4的训练数据示例:
这是我想要实现的目标:
您是否认为使用 YOLOv4 + DeepSORT 对这项任务来说被认为是矫枉过正?我问是因为到目前为止,模型只能成功检测到包含文本光标的视频帧的 70%-80%。如果它毕竟是矫枉过正,你知道可以为这个任务实施的任何其他方法吗?
无论如何,我计划不仅从 Visual Studio 代码 window,而且从浏览器(例如,Google Chrome)检测文本光标和文本处理器(例如 Microsoft Word)。像这样:
我正在考虑将滑动 Window 方法作为替代方法,但根据我的阅读,该方法可能会消耗大量资源并且执行速度较慢。我也在考虑从 OpenCV (like this) 进行模板匹配,但我认为它不会比 YOLOv4 表现得更好更快。
约束是关于性能速度(即,给定时间量可以处理多少帧)和检测精度(即,我想避免将字母 'l' 或“1”检测为文本光标,因为这些字符在某些字体中是相似的)。但我认为更高的精度和更慢的 FPS 是可以接受的。
我目前正在为此使用 Python、Tensorflow 和 OpenCV。 非常感谢!
如果光标是屏幕上唯一的移动对象,这将起作用。这是之前和之后:
之前:
之后:
代码:
import cv2
import numpy as np
BOX_WIDTH = 10
BOX_HEIGHT = 20
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = np.ones((5, 5))
img_canny = cv2.Canny(img_gray, 50, 50)
return img_canny
def get_contour(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
return max(contours, key=cv2.contourArea)
def get_line_tip(cnt1, cnt2):
x1, y1, w1, h1 = cv2.boundingRect(cnt1)
if h1 > BOX_HEIGHT / 2:
if np.any(cnt2):
x2, y2, w2, h2 = cv2.boundingRect(cnt2)
if x1 < x2:
return x1, y1
return x1 + w1, y1
def get_rect(x, y):
half_width = BOX_WIDTH // 2
lift_height = BOX_HEIGHT // 6
return (x - half_width, y - lift_height), (x + half_width, y + BOX_HEIGHT - lift_height)
cap = cv2.VideoCapture("screen_record.mkv")
success, img_past = cap.read()
cnt_past = np.array([])
line_tip_past = 0, 0
while True:
success, img_live = cap.read()
if not success:
break
img_live_processed = process_img(img_live)
img_past_processed = process_img(img_past)
img_diff = cv2.bitwise_xor(img_live_processed, img_past_processed)
cnt = get_contour(img_diff)
line_tip = get_line_tip(cnt, cnt_past)
if line_tip:
cnt_past = cnt
line_tip_past = line_tip
else:
line_tip = line_tip_past
rect = get_rect(*line_tip)
img_past = img_live.copy()
cv2.rectangle(img_live, *rect, (0, 0, 255), 2)
cv2.imshow("Cursor", img_live)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
分解:
- 导入必要的库:
import cv2
import numpy as np
- 根据光标的大小定义跟踪框的大小:
BOX_WIDTH = 10
BOX_HEIGHT = 20
- 定义一个函数来将帧处理成边缘:
def process_img(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
kernel = np.ones((5, 5))
img_canny = cv2.Canny(img_gray, 50, 50)
return img_canny
- 定义一个函数来检索图像中面积最大的轮廓(光标不需要很大也能正常工作,如果需要可以很小):
def get_contour(img):
contours, hierarchies = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
if contours:
return max(contours, key=cv2.contourArea)
- 定义一个包含 2 个轮廓的函数,一个是光标的轮廓 + 当前帧的一些文本,另一个是轮廓 + 光标轮廓的一些杂散文本 + 来自帧之前。通过这两个轮廓,我们可以识别光标是向左移动还是向右移动:
def get_line_tip(cnt1, cnt2):
x1, y1, w1, h1 = cv2.boundingRect(cnt1)
if h1 > BOX_HEIGHT / 2:
if np.any(cnt2):
x2, y2, w2, h2 = cv2.boundingRect(cnt2)
if x1 < x2:
return x1, y1
return x1 + w1, y1
- 定义一个接受光标尖端点的函数,return 一个基于之前定义的
BOX_WIDTH
和BOX_HEIGHT
常量的框:
def get_rect(x, y):
half_width = BOX_WIDTH // 2
lift_height = BOX_HEIGHT // 6
return (x - half_width, y - lift_height), (x + half_width, y + BOX_HEIGHT - lift_height)
- 为视频定义一个捕获设备,并从视频的开头删除一帧并将其存储在一个变量中,该变量将用作每帧之前的帧。还为过去的轮廓和过去的线尖定义临时值:
cap = cv2.VideoCapture("screen_record.mkv")
success, img_past = cap.read()
cnt_past = np.array([])
line_tip_past = 0, 0
- 使用
while
循环,并从视频中读取。处理视频中的帧和该帧之前的帧:
while True:
success, img_live = cap.read()
if not success:
break
img_live_processed = process_img(img_live)
img_past_processed = process_img(img_past)
- 对于处理后的帧,我们可以使用
cv2.bitwise_xor
方法找到帧之间的差异,以获取屏幕上的移动位置。然后,我们可以使用定义的get_contour
函数找到 2 帧之间的运动轮廓:
img_diff = cv2.bitwise_xor(img_live_processed, img_past_processed)
cnt = get_contour(img_diff)
- 有了轮廓,我们就可以利用定义的
get_line_tip
函数来找到光标的尖端。如果找到提示,将其保存到line_tip_past
变量中以供下一次迭代使用,如果未找到提示,我们可以将之前保存的提示用作当前提示:
line_tip = get_line_tip(cnt, cnt_past)
if line_tip:
cnt_past = cnt
line_tip_past = line_tip
else:
line_tip = line_tip_past
- 现在我们使用光标提示和我们之前定义的
get_rect
函数定义一个矩形,并将其绘制到当前帧上。但是在绘制之前,我们将帧保存为下一次迭代当前帧之前的帧:
rect = get_rect(*line_tip)
img_past = img_live.copy()
cv2.rectangle(img_live, *rect, (0, 0, 255), 2)
- 最后,我们显示框架:
cv2.imshow("Cursor", img_live)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()