Skip to content

Commit f83bfe6

Browse files
authored
Merge pull request #328 from fourMs/copilot/fix-optical-flow-export-issue
Fix optical flow video export failing with FFprobeError on frame count
2 parents 06dfd70 + 6eb9876 commit f83bfe6

2 files changed

Lines changed: 100 additions & 12 deletions

File tree

musicalgestures/_flow.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from scipy.stats import entropy
88

99
import musicalgestures
10-
from musicalgestures._utils import MgFigure, extract_wav, embed_audio_in_video, MgProgressbar, convert_to_avi, generate_outfilename
10+
from musicalgestures._utils import MgFigure, extract_wav, embed_audio_in_video, MgProgressbar, convert_to_avi, generate_outfilename, ffmpeg_cmd
1111

1212

1313
class Flow:
@@ -111,8 +111,10 @@ def dense(
111111
if not overwrite:
112112
target_name = generate_outfilename(target_name)
113113

114-
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
115-
out = cv2.VideoWriter(target_name, fourcc, fps, (width, height))
114+
cmd = ['ffmpeg', '-y', '-s', '{}x{}'.format(width, height),
115+
'-r', str(fps), '-f', 'rawvideo', '-pix_fmt', 'bgr24', '-vcodec', 'rawvideo',
116+
'-i', '-', '-vcodec', 'mjpeg', '-q:v', '3', target_name]
117+
out = ffmpeg_cmd(cmd, total_time=length, pipe='write')
116118

117119
ret, frame1 = vidcap.read()
118120
prev_frame = cv2.cvtColor(cv2.resize(frame1, size), cv2.COLOR_BGR2GRAY)
@@ -154,14 +156,14 @@ def dense(
154156

155157
if skip_empty:
156158
if np.sum(rgb) > 0:
157-
out.write(rgb.astype(np.uint8))
159+
out.stdin.write(rgb.astype(np.uint8))
158160
else:
159161
if ii == 0:
160-
out.write(rgb.astype(np.uint8))
162+
out.stdin.write(rgb.astype(np.uint8))
161163
else:
162-
out.write(prev_rgb.astype(np.uint8))
164+
out.stdin.write(prev_rgb.astype(np.uint8))
163165
else:
164-
out.write(rgb.astype(np.uint8))
166+
out.stdin.write(rgb.astype(np.uint8))
165167

166168
if skip_empty:
167169
if np.sum(rgb) > 0 or ii == 0:
@@ -232,7 +234,8 @@ def dense(
232234
return mgf
233235

234236
else:
235-
out.release()
237+
out.stdin.close()
238+
out.wait()
236239
destination_video = target_name
237240

238241
if self.has_audio:
@@ -321,7 +324,6 @@ def sparse(
321324

322325
vidcap = cv2.VideoCapture(filename)
323326
ret, frame = vidcap.read()
324-
fourcc = cv2.VideoWriter_fourcc(*'MJPG')
325327

326328
fps = int(vidcap.get(cv2.CAP_PROP_FPS))
327329
width = int(vidcap.get(cv2.CAP_PROP_FRAME_WIDTH))
@@ -336,7 +338,10 @@ def sparse(
336338
if not overwrite:
337339
target_name = generate_outfilename(target_name)
338340

339-
out = cv2.VideoWriter(target_name, fourcc, fps, (width, height))
341+
cmd = ['ffmpeg', '-y', '-s', '{}x{}'.format(width, height),
342+
'-r', str(fps), '-f', 'rawvideo', '-pix_fmt', 'bgr24', '-vcodec', 'rawvideo',
343+
'-i', '-', '-vcodec', 'mjpeg', '-q:v', '3', target_name]
344+
out = ffmpeg_cmd(cmd, total_time=length, pipe='write')
340345

341346
# params for ShiTomasi corner detection
342347
feature_params = dict(maxCorners=corner_max_corners,
@@ -390,7 +395,7 @@ def sparse(
390395

391396
img = cv2.add(frame, mask)
392397

393-
out.write(img.astype(np.uint8))
398+
out.stdin.write(img.astype(np.uint8))
394399

395400
# Now update the previous frame and previous points
396401
old_gray = frame_gray.copy()
@@ -403,7 +408,8 @@ def sparse(
403408
pb.progress(ii)
404409
ii += 1
405410

406-
out.release()
411+
out.stdin.close()
412+
out.wait()
407413

408414
destination_video = target_name
409415

tests/test_flow.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import musicalgestures
2+
import os
3+
import pytest
4+
5+
6+
@pytest.fixture(scope="class")
7+
def testvideo_avi(tmp_path_factory):
8+
target_name = str(tmp_path_factory.mktemp("data")).replace("\\", "/") + "/testvideo.avi"
9+
testvideo_avi = musicalgestures._utils.extract_subclip(
10+
musicalgestures.examples.dance, 5, 6, target_name=target_name)
11+
return testvideo_avi
12+
13+
14+
@pytest.fixture(scope="class")
15+
def testvideo_mp4(tmp_path_factory):
16+
target_name = str(tmp_path_factory.mktemp("data")).replace(
17+
"\\", "/") + "/testvideo.avi"
18+
testvideo_avi = musicalgestures._utils.extract_subclip(
19+
musicalgestures.examples.dance, 5, 6, target_name=target_name)
20+
testvideo_mp4 = musicalgestures._utils.convert_to_mp4(testvideo_avi)
21+
os.remove(testvideo_avi)
22+
return testvideo_mp4
23+
24+
25+
class Test_flow_dense:
26+
def test_normal_case(self, testvideo_avi):
27+
mg = musicalgestures.MgVideo(testvideo_avi)
28+
result = mg.flow.dense()
29+
assert type(result) == musicalgestures.MgVideo
30+
assert os.path.isfile(result.filename) == True
31+
32+
def test_overwrite(self, testvideo_avi):
33+
mg = musicalgestures.MgVideo(testvideo_avi)
34+
result = mg.flow.dense(overwrite=True)
35+
assert type(result) == musicalgestures.MgVideo
36+
assert os.path.isfile(result.filename) == True
37+
38+
def test_not_avi(self, testvideo_mp4):
39+
mg = musicalgestures.MgVideo(testvideo_mp4)
40+
result = mg.flow.dense()
41+
assert type(result) == musicalgestures.MgVideo
42+
assert os.path.isfile(result.filename) == True
43+
44+
def test_skip_empty(self, testvideo_avi):
45+
mg = musicalgestures.MgVideo(testvideo_avi)
46+
result = mg.flow.dense(skip_empty=True)
47+
assert type(result) == musicalgestures.MgVideo
48+
assert os.path.isfile(result.filename) == True
49+
50+
def test_with_target_name(self, testvideo_avi):
51+
target_name = os.path.dirname(testvideo_avi) + "/result_dense.avi"
52+
mg = musicalgestures.MgVideo(testvideo_avi)
53+
result = mg.flow.dense(target_name=target_name, overwrite=True)
54+
assert type(result) == musicalgestures.MgVideo
55+
assert os.path.isfile(result.filename) == True
56+
57+
58+
class Test_flow_sparse:
59+
def test_normal_case(self, testvideo_avi):
60+
mg = musicalgestures.MgVideo(testvideo_avi)
61+
result = mg.flow.sparse()
62+
assert type(result) == musicalgestures.MgVideo
63+
assert os.path.isfile(result.filename) == True
64+
65+
def test_overwrite(self, testvideo_avi):
66+
mg = musicalgestures.MgVideo(testvideo_avi)
67+
result = mg.flow.sparse(overwrite=True)
68+
assert type(result) == musicalgestures.MgVideo
69+
assert os.path.isfile(result.filename) == True
70+
71+
def test_not_avi(self, testvideo_mp4):
72+
mg = musicalgestures.MgVideo(testvideo_mp4)
73+
result = mg.flow.sparse()
74+
assert type(result) == musicalgestures.MgVideo
75+
assert os.path.isfile(result.filename) == True
76+
77+
def test_with_target_name(self, testvideo_avi):
78+
target_name = os.path.dirname(testvideo_avi) + "/result_sparse.avi"
79+
mg = musicalgestures.MgVideo(testvideo_avi)
80+
result = mg.flow.sparse(target_name=target_name, overwrite=True)
81+
assert type(result) == musicalgestures.MgVideo
82+
assert os.path.isfile(result.filename) == True

0 commit comments

Comments
 (0)