在线时间 480 小时 最后登录 2026-6-1 注册时间 2023-7-11 听众数 4 收听数 0 能力 0 分 体力 7823 点 威望 0 点 阅读权限 255 积分 2934 相册 0 日志 0 记录 0 帖子 1174 主题 1189 精华 0 分享 0 好友 1
该用户从未签到
背景
4 A) V. {8 d. U$ w1 j 由于我要做一个深度学习方向的计算机视觉项目,需要一些数据集来进行训练,我便想尝试捕获视频中的图片用来标注。# U$ v- A( b- {( H" n! N
( b$ s3 C, Q1 i9 M, M6 n
注意事项
( g: e, y/ ]3 X 如果视频中的场景单一,那么使用该视频获取的数据集训练出的模型面对其他场景的泛化能力就会受到限制。为了提高数据集的多样性,可以采用多种不同场景的视频,并且在获取数据时采用大间隔捕获的方式。采用大间隔捕获数据的方法可以捕获到的图片同质化降低,从而进一步增加数据集的多样性。import os
& H1 g; k( n- h# x; V2 t import sys
6 I: _! B3 ] u' b) X from concurrent.futures import ThreadPoolExecutor
6 M; E O) C2 s\" r import cv26 Y( t% j1 u) S! Q
% o4 m- J, z. |- j* c! h def output_img(video_path,img_dir):
7 B. x\" h2 y1 h. s8 x% s # 由视频逐帧输出图片$ X\" D U3 O6 u3 @
# video_path: 视频文件路径1 T2 P+ p$ P$ o& V6 H. N9 S% y7 {! J
# img_dir: 图片保存目录路径,路径不支持中文
# s$ U9 e& a2 c, x os.makedirs(img_dir,exist_ok=True)
. T2 o5 f' w) `6 {$ {4 X/ H # img_dir:表示要创建的目录路径。: F\" `; p6 @! r& G3 }% H& a* {: m$ t% @
# exist_ok=True:表示如果目录已经存在时不抛出异常。如果将 exist_ok 设置为 True,那么如果目录已经存在,os.makedirs() 函数也不会报错;如果设置为 False,则会抛出一个 FileExistsError 异常。$ t6 b7 k: _! r
cv = cv2.VideoCapture(video_path)
- K$ l0 K: J+ a( `3 \ frame_count = 0 + N$ ~# K2 p w* S0 B7 t9 D8 ~
# 计数器8 a& J, P2 h! q; Z. C) y2 \
n = 0
5 J6 w/ [& R, \' w # 命名计数器
\" P+ }9 F9 N4 ^& ^2 k k while True:
! n9 h! }$ U$ r+ g2 h ret,frame = cv.read()
2 H' y. n. C; k) I$ L8 W7 z if not ret:+ a- {\" Z7 I5 u3 w4 j% Q. Z
break
: r5 N4 w* V7 `9 W+ R frame_count += 1
8 D. u; x3 A3 i if frame_count % 30 ==0:
5 B2 [3 C- V4 Z5 h5 P( E# J) I( y # 每隔三十帧获取一次图片/ H6 T* B( b$ [) W2 t
n += 1
7 c\" |$ U. d0 k m img_name = "0000000{0}.jpg".format(n)7 L% M\" Z% T+ j, E! e% z
img_file_path = os.path.join(img_dir,img_name)
B ]* B# {3 z5 p- W if not os.path.exists(img_file_path):+ ]9 \4 B\" U. A4 U' w
sys.stdout.write("创建文件:"+ img_file_path + "\n")
3 J$ o0 ]3 Q; P2 e& ]( i # 标准输出流,将指定文本输出到控制台或其他输出设备中9 y2 u# u. E: `( a9 j* x9 n
cv2.imwrite(img_file_path,frame,[cv2.IMWRITE_JPEG_QUALITY,100])
3 n. I1 C* E/ E& S9 U# I' b # 将图像帧保存为jepg格式的图像,质量最高为100: x2 s5 ~. Z0 q+ v- s0 V
else:
8 h$ d i3 s* _7 l3 y u2 i6 ?0 a sys.stderr.write("跳过:" + img_file_path + "\n")
! z7 u. Y; D3 e: I z% _ ret,frame = cv.read()) m$ ~4 [% f8 s) J6 F, v
9 Z\" F) @: A\" [1 W% ~ def run(video_dir,img_dir):7 Q\" f) z! x, `: G2 C% d9 B
pool = ThreadPoolExecutor()
5 G, v, @& z: K: P\" ] w7 T6 U # 创建一个线程池对象,实例化ThreadPoolExecutor类,将任务提交给线程池,线程池会自动调度线程来执行这些任务
: U\" d$ y! ?1 u0 c for file in os.listdir(video_dir):$ ^. i1 H t0 Q6 |4 _
if file[-4:] == ".mp4":
: J5 Y4 J8 H3 N* ^8 j* v7 Q$ }$ w/ w video_file_path = os.path.join(video_dir,file)
3 Z9 h. S' K$ D. w( K$ R img_dir_name = os.path.join(img_dir,file[:-4])* ~, o3 G1 I6 M7 }5 e/ ?
os.makedirs(img_dir_name,exist_ok=True)) C% F/ T% C5 v/ y x* g
pool.submit(output_img,*(video_file_path,img_dir_name))/ [$ f% x9 Q\" i6 p: V( V
#* 和 ** 是用于解包参数的操作符。在这种情况下,*(video_file_path, img_dir_name)
, y9 V; ?: j0 Z9 n- Y7 Q3 @* [' r3 q # 的作用是将元组 (video_file_path, img_dir_name) 中的元素解包并作为单独的参数传递给函数。
3 \# ~5 ~- ]# |% |: I7 c7 P$ o6 j' q 3 W5 G. G4 o1 o C/ P$ x+ Q
if __name__ == '__main__':
* [0 {8 r, b& q8 u% q run(r"E:\video",r"E:\image")
1 f( ^6 Z6 [0 Y+ v0 e% \ 复制代码 , ?: p- n5 p6 V
zan