Toggle navigation
Toggle navigation
This project
Loading...
Sign in
lizhengwei
/
aigc-embedding-service
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
lizhengwei
2026-05-14 13:16:05 +0800
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
784c8effb9211d075ce6ea641718dcccb11ae660
784c8eff
1 parent
1396dd74
jira:NYJ-1540 desc: add video_event_for_contents func
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
96 additions
and
53 deletions
src/football_replay_match/core/llm_image.py
src/football_replay_match/util/football_replay_video_event_by_llm.py
src/football_replay_match/core/llm_image.py
View file @
784c8ef
...
...
@@ -122,7 +122,7 @@ class Video2Frame:
if
not
cap
.
isOpened
():
raise
ValueError
(
f
"无法打开视频片段: {clip_path}"
)
total_frames
=
int
(
cap
.
get
(
cv2
.
CAP_PROP_FRAME_COUNT
))
#
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
video_fps
=
cap
.
get
(
cv2
.
CAP_PROP_FPS
)
if
video_fps
<=
0
:
video_fps
=
25.0
...
...
@@ -132,10 +132,9 @@ class Video2Frame:
if
frame_interval
<
1
:
frame_interval
=
1
frame
_path
s
=
[]
frames
=
[]
saved_count
=
0
frame_count
=
0
while
True
:
ret
,
frame
=
cap
.
read
()
if
not
ret
:
...
...
@@ -145,27 +144,26 @@ class Video2Frame:
if
roi
is
not
None
:
x
,
y
,
w
,
h
=
roi
frame
=
frame
[
y
:
y
+
h
,
x
:
x
+
w
]
if
max_px_area
is
not
None
:
h
,
w
=
frame
.
shape
[:
2
]
area
=
h
*
w
if
area
>
max_px_area
:
scale
=
(
max_px_area
/
area
)
**
0.5
new_w
=
int
(
w
*
scale
)
new_h
=
int
(
h
*
scale
)
frame
=
cv2
.
resize
(
frame
,
(
new_w
,
new_h
),
interpolation
=
cv2
.
INTER_AREA
)
frame
=
self
.
reisize_frame
(
frame
,
max_px_area
)
frames
.
append
(
frame
)
frame_path
=
frame_output_dir
/
f
"frame_{saved_count:06d}.jpg"
cv2
.
imwrite
(
str
(
frame_path
),
frame
)
frame_paths
.
append
(
str
(
frame_path
))
saved_count
+=
1
frame_count
+=
1
cap
.
release
()
return
frame_paths
return
frames
def
reisize_frame
(
self
,
frame
,
max_px_area
):
if
max_px_area
is
None
:
return
frame
h
,
w
=
frame
.
shape
[:
2
]
area
=
h
*
w
if
area
>
max_px_area
:
scale
=
(
max_px_area
/
area
)
**
0.5
new_w
=
int
(
w
*
scale
)
new_h
=
int
(
h
*
scale
)
frame
=
cv2
.
resize
(
frame
,
(
new_w
,
new_h
),
interpolation
=
cv2
.
INTER_AREA
)
return
frame
@staticmethod
def
_find_frame_idx
(
utc_list
,
target_utc
,
find_last_not_greater
=
True
):
...
...
@@ -241,28 +239,34 @@ def frames2content(frames):
将帧列表(图片路径列表)转为 LLM content 列表。
格式与 llm_video_content.py 中的 contents 返回的 content 一致。
"""
content
=
[]
content
s
=
[]
video_prompt
=
(
f
"以下是从视频中按时间顺序提取的 {len(frames)} 帧画面,"
f
"请将它们视为一个连续的视频进行分析。"
)
content
.
append
({
"type"
:
"text"
,
"text"
:
video_prompt
})
content
s
.
append
({
"type"
:
"text"
,
"text"
:
video_prompt
})
for
frame_path
in
frames
:
with
open
(
frame_path
,
"rb"
)
as
f
:
img_bytes
=
f
.
read
()
b64_str
=
base64
.
b64encode
(
img_bytes
)
.
decode
(
"utf-8"
)
content
.
append
({
for
frame
in
frames
:
_
,
buffer
=
cv2
.
imencode
(
'.jpg'
,
frame
)
b64_str
=
base64
.
b64encode
(
buffer
)
.
decode
(
'utf-8'
)
# with open(frame_path, "rb") as f:
# img_bytes = f.read()
# b64_str = base64.b64encode(img_bytes).decode("utf-8")
contents
.
append
({
"type"
:
"image_url"
,
"image_url"
:
{
"url"
:
f
"data:image/jpeg;base64,{b64_str}"
}
})
return
content
return
content
s
if
__name__
==
"__main__"
:
# pass
import
argparse
import
sys
import
tempfile
...
...
@@ -275,7 +279,7 @@ if __name__ == "__main__":
)
parser
.
add_argument
(
"--cache-dir"
,
default
=
None
,
default
=
r"C:
\
Users
\
lzw
\
AppData
\
Local
\
Temp
\
video2frame_test_mn26tcy_my"
,
help
=
"缓存目录(默认自动创建临时目录)"
,
)
parser
.
add_argument
(
...
...
@@ -293,7 +297,7 @@ if __name__ == "__main__":
parser
.
add_argument
(
"--fps"
,
type
=
float
,
default
=
1
.0
,
default
=
2
.0
,
help
=
"抽帧帧率(默认 1.0)"
,
)
args
=
parser
.
parse_args
()
...
...
@@ -314,11 +318,13 @@ if __name__ == "__main__":
start_utc
=
args
.
start
,
end_utc
=
args
.
end
,
fps
=
args
.
fps
,
roi
=
None
,
max_px_area
=
200
_000
,
)
print
(
f
" 成功提取 {len(frames)} 帧"
)
if
frames
:
print
(
f
" 首帧: {frames[0]}"
)
print
(
f
" 末帧: {frames[-1]}"
)
print
(
f
" 首帧: {frames[0].shape}"
)
print
(
f
" 末帧: {frames[-1].shape}"
)
except
Exception
as
e
:
print
(
f
" 失败: {e}"
)
import
traceback
...
...
@@ -340,27 +346,46 @@ if __name__ == "__main__":
if
len
(
content
)
>
3
:
print
(
f
" ... 还有 {len(content) - 3} 个元素"
)
# TEST 3: ROI + max_px_area
print
(
"
\n
[TEST 3] to_frames with roi + max_px_area"
)
try
:
end_crop
=
min
(
args
.
start
+
5
,
args
.
end
)
frames_cropped
=
v2f
.
to_frames
(
url
=
args
.
url
,
start_utc
=
args
.
start
,
end_utc
=
end_crop
,
fps
=
1.0
,
roi
=
None
,
max_px_area
=
200
_000
,
)
print
(
f
" 成功提取 {len(frames_cropped)} 帧(带 ROI 裁剪和面积缩放)"
)
except
Exception
as
e
:
print
(
f
" 跳过/失败: {e}"
)
print
(
"
\n
[TEST 3] FootballReplayVideoEvent 进球识别(可选)"
)
# TEST 4: SEI UTC 模式说明(仅演示,需真实含 SEI 的流才能运行)
print
(
"
\n
[TEST 4] SEI UTC 绝对时间戳模式(示例代码,需替换为含 SEI 的流)"
)
print
(
" # 示例:假设视频第一帧 UTC 为 1715600000,提取 10 秒片段
\n
"
" # frames = v2f.to_frames(url, start_utc=1715600000, end_utc=1715600010, fps=1)"
try
:
from
..util.football_replay_video_event_by_llm
import
FootballReplayVideoEvent
except
ImportError
:
import
sys
from
pathlib
import
Path
sys
.
path
.
insert
(
0
,
str
(
Path
(
__file__
)
.
parent
.
parent
))
from
util.football_replay_video_event_by_llm
import
FootballReplayVideoEvent
fbrv
=
FootballReplayVideoEvent
(
base_url
=
"http://192.168.1.59:11434"
,
model
=
"qwen3.6:35b-a3b-q8_0"
,
temperature
=
0.7
,
)
print
(
"
\n
[INFO] 所有测试完成"
)
event_json
=
fbrv
.
video_event_for_contents
(
content
,
None
)
print
(
f
" 识别结果: {event_json}"
)
# # TEST 3: ROI + max_px_area
# print("\n[TEST 3] to_frames with roi + max_px_area")
# try:
# end_crop = min(args.start + 5, args.end)
# frames_cropped = v2f.to_frames(
# url=args.url,
# start_utc=args.start,
# end_utc=end_crop,
# fps=1.0,
# roi=None,
# max_px_area=200_000,
# )
# print(f" 成功提取 {len(frames_cropped)} 帧(带 ROI 裁剪和面积缩放)")
# except Exception as e:
# print(f" 跳过/失败: {e}")
#
# # TEST 4: SEI UTC 模式说明(仅演示,需真实含 SEI 的流才能运行)
# print("\n[TEST 4] SEI UTC 绝对时间戳模式(示例代码,需替换为含 SEI 的流)")
# print(
# " # 示例:假设视频第一帧 UTC 为 1715600000,提取 10 秒片段\n"
# " # frames = v2f.to_frames(url, start_utc=1715600000, end_utc=1715600010, fps=1)"
# )
#
# print("\n[INFO] 所有测试完成")
...
...
src/football_replay_match/util/football_replay_video_event_by_llm.py
View file @
784c8ef
...
...
@@ -96,3 +96,21 @@ class FootballReplayVideoEvent:
with
open
(
cache_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
result
)
return
result_json
def
video_event_for_contents
(
self
,
contents
:
list
,
asr_text
:
str
=
'无'
,
cache_path
=
None
):
if
cache_path
is
not
None
and
os
.
path
.
exists
(
cache_path
):
with
open
(
cache_path
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
return
json
.
loads
(
f
.
read
())
system_message
=
SystemMessage
(
content
=
req_prompt
)
video_message
=
HumanMessage
(
content
=
contents
)
asr_message
=
HumanMessage
(
content
=
f
"解说内容:{asr_text}"
)
result
=
self
.
model
.
invoke
([
system_message
,
video_message
,
asr_message
])
.
content
try
:
result_json
=
json
.
loads
(
result
)
except
json
.
JSONDecodeError
:
result_json
=
json
.
loads
(
result
.
replace
(
"```json"
,
""
)
.
replace
(
"```"
,
""
))
if
cache_path
is
not
None
:
os
.
makedirs
(
Path
(
cache_path
)
.
parent
,
exist_ok
=
True
)
with
open
(
cache_path
,
'w'
,
encoding
=
'utf-8'
)
as
f
:
f
.
write
(
result
)
return
result_json
...
...
Please
register
or
login
to post a comment