Skip to content

Commit 04d990b

Browse files
authored
Merge pull request #54 from LukasBommes/add_end_to_end_tests
Add end to end tests
2 parents 56a783d + bacfb73 commit 04d990b

File tree

1,357 files changed

+1485
-60
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,357 files changed

+1485
-60
lines changed

.github/workflows/ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ jobs:
7070
run: |
7171
docker run -v ${{ github.workspace }}:/home/video_cap \
7272
mv-extractor:local \
73-
python3.10 tests/tests.py
73+
python3.10 -m unittest discover -s tests -p "*tests.py"
7474
7575
build_and_test_wheels:
7676
name: Build wheels for cp${{ matrix.python }}-${{ matrix.platform_id }}
@@ -133,7 +133,7 @@ jobs:
133133
CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux_image }}
134134
#CIBW_MANYLINUX_I686_IMAGE: ${{ matrix.manylinux_image }}
135135
CIBW_BUILD_FRONTEND: build
136-
CIBW_TEST_COMMAND: VIDEO_URL={project}/vid_h264.mp4 python3 {project}/tests/tests.py
136+
CIBW_TEST_COMMAND: PROJECT_ROOT={project} python3 -m unittest discover -s {project}/tests -p "*tests.py"
137137
CIBW_BUILD_VERBOSITY: 1
138138

139139
- uses: actions/upload-artifact@v4

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ For example, to store extracted frames and motion vectors to disk without showin
6666
```
6767
extract_mvs vid_h264.mp4 --dump
6868
```
69+
The `--dump` parameter also takes an optional destination directory.
6970

7071

7172
## Advanced Usage
@@ -79,13 +80,13 @@ git clone https://github.com/LukasBommes/mv-extractor.git mv_extractor
7980

8081
Now, to run the tests from the `mv_extractor` directory with
8182
```
82-
python3 tests/tests.py
83+
python3 -m unittest discover -s tests -p "*tests.py"
8384
```
8485
Confirm that all tests pass.
8586

8687
If you are using the Docker image instead of the PyPI package as explained below, you can invoke the tests with
8788
```
88-
sudo ./run.sh python3.10 tests/tests.py
89+
sudo ./run.sh python3.10 -m unittest discover -s tests -p "*tests.py"
8990
```
9091

9192
### Importing mvextractor into Your Own Scripts

src/mvextractor/__main__.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,20 @@ def main(args=None):
2525
args = sys.argv[1:]
2626

2727
parser = argparse.ArgumentParser(description='Extract motion vectors from video.')
28-
parser.add_argument('video_url', type=str, nargs='?', help='File path or url of the video stream')
29-
parser.add_argument('-p', '--preview', action='store_true', help='Show a preview video with overlaid motion vectors')
30-
parser.add_argument('-v', '--verbose', action='store_true', help='Show detailled text output')
31-
parser.add_argument('-d', '--dump', action='store_true', help='Dump frames, motion vectors, frame types, and timestamps to output directory')
28+
parser.add_argument('video_url', type=str, nargs='?', help='file path or url of the video stream')
29+
parser.add_argument('-p', '--preview', action='store_true', help='show a preview video with overlaid motion vectors')
30+
parser.add_argument('-v', '--verbose', action='store_true', help='show detailled text output')
31+
parser.add_argument('-d', '--dump', nargs='?', const=True,
32+
help='dump frames, motion vectors, frame types, and timestamps to optionally specified output directory')
3233
args = parser.parse_args()
3334

3435
if args.dump:
35-
now = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
36+
if isinstance(args.dump, str):
37+
dumpdir = args.dump
38+
else:
39+
dumpdir = f"out-{datetime.now().strftime('%Y-%m-%dT%H:%M:%S')}"
3640
for child in ["frames", "motion_vectors"]:
37-
os.makedirs(os.path.join(f"out-{now}", child), exist_ok=True)
41+
os.makedirs(os.path.join(dumpdir, child), exist_ok=True)
3842

3943
cap = VideoCap()
4044

@@ -83,14 +87,13 @@ def main(args=None):
8387

8488
# store motion vectors, frames, etc. in output directory
8589
if args.dump:
86-
cv2.imwrite(os.path.join(f"out-{now}", "frames", f"frame-{step}.jpg"), frame)
87-
np.save(os.path.join(f"out-{now}", "motion_vectors", f"mvs-{step}.npy"), motion_vectors)
88-
with open(os.path.join(f"out-{now}", "timestamps.txt"), "a") as f:
90+
cv2.imwrite(os.path.join(dumpdir, "frames", f"frame-{step}.jpg"), frame)
91+
np.save(os.path.join(dumpdir, "motion_vectors", f"mvs-{step}.npy"), motion_vectors)
92+
with open(os.path.join(dumpdir, "timestamps.txt"), "a") as f:
8993
f.write(str(timestamp)+"\n")
90-
with open(os.path.join(f"out-{now}", "frame_types.txt"), "a") as f:
94+
with open(os.path.join(dumpdir, "frame_types.txt"), "a") as f:
9195
f.write(frame_type+"\n")
9296

93-
9497
step += 1
9598

9699
if args.preview:

tests/end_to_end_tests.py

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import os
2+
import tempfile
3+
import unittest
4+
import subprocess
5+
6+
import cv2
7+
import numpy as np
8+
9+
10+
PROJECT_ROOT = os.getenv("PROJECT_ROOT", "")
11+
12+
13+
class TestEndToEnd(unittest.TestCase):
14+
15+
def motions_vectors_valid(self, outdir, refdir):
16+
equal = []
17+
for i in range(336):
18+
mvs = np.load(os.path.join(outdir, "motion_vectors", f"mvs-{i}.npy"))
19+
mvs_ref = np.load(os.path.join(refdir, "motion_vectors", f"mvs-{i}.npy"))
20+
equal.append(np.all(mvs == mvs_ref))
21+
return all(equal)
22+
23+
24+
def frame_types_valid(self, outdir, refdir):
25+
with open(os.path.join(outdir, "frame_types.txt"), "r") as file:
26+
frame_types = [line.strip() for line in file]
27+
with open(os.path.join(refdir, "frame_types.txt"), "r") as file:
28+
frame_types_ref = [line.strip() for line in file]
29+
return frame_types == frame_types_ref
30+
31+
32+
def frames_valid(self, outdir, refdir):
33+
equal = []
34+
for i in range(336):
35+
frame = cv2.imread(os.path.join(outdir, "frames", f"frame-{i}.jpg"))
36+
frame_ref = cv2.imread(os.path.join(refdir, "frames", f"frame-{i}.jpg"))
37+
equal.append(np.all(frame == frame_ref))
38+
return all(equal)
39+
40+
41+
def test_end_to_end_h264(self):
42+
with tempfile.TemporaryDirectory() as outdir:
43+
print("Running extraction for H.264")
44+
subprocess.run(f"extract_mvs {os.path.join(PROJECT_ROOT, 'vid_h264.mp4')} --dump {outdir}", shell=True, check=True)
45+
refdir = os.path.join(PROJECT_ROOT, "tests/reference/h264")
46+
47+
self.assertTrue(self.motions_vectors_valid(outdir, refdir), msg="motion vectors are invalid")
48+
self.assertTrue(self.frame_types_valid(outdir, refdir), msg="frame types are invalid")
49+
self.assertTrue(self.frames_valid(outdir, refdir), msg="frames are invalid")
50+
51+
52+
def test_end_to_end_mpeg4_part2(self):
53+
with tempfile.TemporaryDirectory() as outdir:
54+
print("Running extraction for MPEG-4 Part 2")
55+
subprocess.run(f"extract_mvs {os.path.join(PROJECT_ROOT, 'vid_mpeg4_part2.mp4')} --dump {outdir}", shell=True, check=True)
56+
refdir = os.path.join(PROJECT_ROOT, "tests/reference/mpeg4_part2")
57+
58+
self.assertTrue(self.motions_vectors_valid(outdir, refdir), msg="motion vectors are invalid")
59+
self.assertTrue(self.frame_types_valid(outdir, refdir), msg="frame types are invalid")
60+
self.assertTrue(self.frames_valid(outdir, refdir), msg="frames are invalid")
61+
62+
63+
if __name__ == '__main__':
64+
unittest.main()
65+

0 commit comments

Comments
 (0)