NMPhap commited on
Commit
1654acc
1 Parent(s): 7a0e77d

<Test>: Fix test]

Browse files
.coverage CHANGED
Binary files a/.coverage and b/.coverage differ
 
app/__init__.py CHANGED
@@ -27,14 +27,11 @@ key: str = os.environ.get("SUPABASE_KEY")
27
  supabase: Client = create_client(url, key)
28
 
29
  # LOAD FIREBASE ADMIN SDK
30
- if not firebase_admin._apps:
31
- firebase_app = initialize_app(
32
- credential=credentials.Certificate(
33
- json.loads(os.environ.get("FIREBASE_CREDENTIALS"))
34
- )
35
  )
36
- else:
37
- firebase_app = firebase_admin.get_app()
38
  db = firestore.client()
39
 
40
  # LOAD NEO4J DB
 
27
  supabase: Client = create_client(url, key)
28
 
29
  # LOAD FIREBASE ADMIN SDK
30
+ firebase_app = initialize_app(
31
+ credential=credentials.Certificate(
32
+ json.loads(os.environ.get("FIREBASE_CREDENTIALS"))
 
 
33
  )
34
+ )
 
35
  db = firestore.client()
36
 
37
  # LOAD NEO4J DB
app/custom_mmcv/__init__.py ADDED
File without changes
app/custom_mmcv/main.py CHANGED
@@ -17,7 +17,7 @@ def imshow_det_bboxes(
17
  img: Union[str, np.ndarray],
18
  bboxes: np.ndarray,
19
  labels: np.ndarray,
20
- class_names: List[str] = None,
21
  score_thr: float = 0,
22
  bbox_color: ColorType = "green",
23
  text_color: ColorType = "green",
@@ -67,7 +67,7 @@ def imshow_det_bboxes(
67
  bbox_int = bbox.astype(np.int32)
68
  left_top = (bbox_int[0], bbox_int[1])
69
  right_bottom = (bbox_int[2], bbox_int[3])
70
- if colors is not None:
71
  bbox_color = text_color = color_val(colors[label])
72
  cv2.rectangle(img, left_top, right_bottom, bbox_color, thickness=thickness)
73
  label_text = class_names[label] if class_names is not None else f"cls {label}"
 
17
  img: Union[str, np.ndarray],
18
  bboxes: np.ndarray,
19
  labels: np.ndarray,
20
+ class_names: List[str] = [],
21
  score_thr: float = 0,
22
  bbox_color: ColorType = "green",
23
  text_color: ColorType = "green",
 
67
  bbox_int = bbox.astype(np.int32)
68
  left_top = (bbox_int[0], bbox_int[1])
69
  right_bottom = (bbox_int[2], bbox_int[3])
70
+ if colors is not None and len(colors) > 0:
71
  bbox_color = text_color = color_val(colors[label])
72
  cv2.rectangle(img, left_top, right_bottom, bbox_color, thickness=thickness)
73
  label_text = class_names[label] if class_names is not None else f"cls {label}"
app/dependencies.py CHANGED
@@ -4,7 +4,6 @@ from firebase_admin import auth
4
  from firebase_admin.auth import ExpiredIdTokenError, InvalidIdTokenError
5
  from app import logger
6
  from . import db
7
-
8
  security = HTTPBearer()
9
 
10
 
 
4
  from firebase_admin.auth import ExpiredIdTokenError, InvalidIdTokenError
5
  from app import logger
6
  from . import db
 
7
  security = HTTPBearer()
8
 
9
 
app/graphdb/__init__.py ADDED
File without changes
app/main.py CHANGED
@@ -12,11 +12,6 @@ app.include_router(friend_request.router)
12
  app.include_router(me.router)
13
 
14
 
15
- @app.get("/", include_in_schema=False)
16
- def hello():
17
- response = RedirectResponse(url="/docs")
18
- return response
19
-
20
 
21
  @app.get("/test")
22
  async def test():
 
12
  app.include_router(me.router)
13
 
14
 
 
 
 
 
 
15
 
16
  @app.get("/test")
17
  async def test():
app/routers/__init__.py ADDED
File without changes
app/routers/image.py CHANGED
@@ -20,10 +20,9 @@ async def handleImageRequest(
20
  try:
21
  img = imfrombytes(file, cv2.IMREAD_COLOR)
22
  if raw:
23
- bboxes, labels = inferenceImage(img, threshold, True)
24
  return {"bboxes": bboxes.tolist(), "labels": labels.tolist()}
25
-
26
- img = inferenceImage(img, threshold, False)
27
  except Exception as e:
28
  logger.error(e)
29
  return Response(content="Failed to read image", status_code=400)
@@ -37,7 +36,7 @@ async def handleImageRequest(
37
  return Response(content=jpeg_bytes, media_type="image/jpeg")
38
 
39
 
40
- def inferenceImage(img, threshold: float, isRaw: bool = False):
41
  bboxes, labels, _ = detector(img)
42
  if isRaw:
43
  removeIndexs = []
 
20
  try:
21
  img = imfrombytes(file, cv2.IMREAD_COLOR)
22
  if raw:
23
+ bboxes, labels = inferenceImage(img, threshold, raw)
24
  return {"bboxes": bboxes.tolist(), "labels": labels.tolist()}
25
+ img = inferenceImage(img, threshold, raw)
 
26
  except Exception as e:
27
  logger.error(e)
28
  return Response(content="Failed to read image", status_code=400)
 
36
  return Response(content=jpeg_bytes, media_type="image/jpeg")
37
 
38
 
39
+ def inferenceImage(img, threshold: float, isRaw: bool):
40
  bboxes, labels, _ = detector(img)
41
  if isRaw:
42
  removeIndexs = []
app/routers/video.py CHANGED
@@ -62,50 +62,50 @@ async def handleVideoRequest(
62
  def now():
63
  return round(time.time() * 1000)
64
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
65
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
  async def inferenceVideo(artifactId: str, inputDir: str, threshold: float):
67
  try:
68
  Process(updateArtifact(artifactId, {"status": "processing"})).start()
69
- cap = cv2.VideoCapture(
70
- filename=os.path.join(inputDir, "input.mp4"), apiPreference=cv2.CAP_FFMPEG
71
- )
72
- fps = cap.get(cv2.CAP_PROP_FPS)
73
- size = (
74
- int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
75
- int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
76
- )
77
- result = cv2.VideoWriter(
78
- filename=os.path.join(inputDir, "out.mp4"),
79
- fourcc=cv2.VideoWriter_fourcc(*"mp4v"),
80
- fps=fps,
81
- frameSize=size,
82
- )
83
-
84
- isFirstFrame = True
85
- thumbnail = None
86
- while cap.isOpened():
87
- res, frame = cap.read()
88
- if isFirstFrame:
89
- isFirstFrame = False
90
- thumbnail = frame
91
-
92
- if res == False:
93
- break
94
-
95
- resFram = inferenceImage(frame, threshold)
96
- result.write(resFram)
97
-
98
- cap.release()
99
- result.release()
100
-
101
- def createThumbnail(thumbnail):
102
- thumbnail = cv2.resize(
103
- src=thumbnail, dsize=(160, 160), interpolation=cv2.INTER_AREA
104
- )
105
- cv2.imwrite(os.path.join(inputDir, "thumbnail.jpg"), thumbnail)
106
-
107
- createThumbnail(thumbnail)
108
-
109
  async def uploadVideo():
110
  async with aiofiles.open(os.path.join(inputDir, "out.mp4"), "rb") as f:
111
  supabase.storage.from_("video").upload(
@@ -149,7 +149,10 @@ async def inferenceVideo(artifactId: str, inputDir: str, threshold: float):
149
  )
150
  ).start()
151
  finally:
152
- shutil.rmtree(inputDir)
 
 
 
153
 
154
 
155
  def updateArtifact(artifactId: str, body):
 
62
  def now():
63
  return round(time.time() * 1000)
64
 
65
+ def createThumbnail(thumbnail, inputDir):
66
+ thumbnail = cv2.resize(
67
+ src=thumbnail, dsize=(160, 160), interpolation=cv2.INTER_AREA
68
+ )
69
+ cv2.imwrite(os.path.join(inputDir, "thumbnail.jpg"), thumbnail)
70
+ def inference_frame(inputDir, threshold:float=0.3):
71
+ cap = cv2.VideoCapture(
72
+ filename=os.path.join(inputDir, "input.mp4"), apiPreference=cv2.CAP_FFMPEG
73
+ )
74
+ fps = cap.get(cv2.CAP_PROP_FPS)
75
+ size = (
76
+ int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)),
77
+ int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)),
78
+ )
79
+ result = cv2.VideoWriter(
80
+ filename=os.path.join(inputDir, "out.mp4"),
81
+ fourcc=cv2.VideoWriter_fourcc(*"mp4v"),
82
+ fps=fps,
83
+ frameSize=size,
84
+ )
85
 
86
+ isFirstFrame = True
87
+ thumbnail = None
88
+ while cap.isOpened():
89
+ res, frame = cap.read()
90
+ if isFirstFrame:
91
+ isFirstFrame = False
92
+ thumbnail = frame
93
+
94
+ if res == False:
95
+ break
96
+
97
+ resFram = inferenceImage(frame, threshold, False)
98
+ result.write(resFram)
99
+ cap.release()
100
+ result.release()
101
+ del cap
102
+ del result
103
+ return thumbnail
104
  async def inferenceVideo(artifactId: str, inputDir: str, threshold: float):
105
  try:
106
  Process(updateArtifact(artifactId, {"status": "processing"})).start()
107
+ thumbnail = inference_frame(inputDir, threshold=threshold)
108
+ createThumbnail(thumbnail, inputDir)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  async def uploadVideo():
110
  async with aiofiles.open(os.path.join(inputDir, "out.mp4"), "rb") as f:
111
  supabase.storage.from_("video").upload(
 
149
  )
150
  ).start()
151
  finally:
152
+ try:
153
+ shutil.rmtree(inputDir)
154
+ except PermissionError as e:
155
+ print(e)
156
 
157
 
158
  def updateArtifact(artifactId: str, body):
coverage.xml CHANGED
@@ -1,5 +1,5 @@
1
  <?xml version="1.0" ?>
2
- <coverage version="7.2.2" timestamp="1699431759592" lines-valid="371" lines-covered="301" line-rate="0.8113" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
3
  <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.2.2 -->
4
  <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5
  <sources>
@@ -102,6 +102,10 @@
102
  </package>
103
  <package name="custom_mmcv" line-rate="0.9697" branch-rate="0" complexity="0">
104
  <classes>
 
 
 
 
105
  <class name="color.py" filename="custom_mmcv/color.py" complexity="0" line-rate="0.9688" branch-rate="0">
106
  <methods/>
107
  <lines>
@@ -182,6 +186,10 @@
182
  </package>
183
  <package name="graphdb" line-rate="0.3636" branch-rate="0" complexity="0">
184
  <classes>
 
 
 
 
185
  <class name="main.py" filename="graphdb/main.py" complexity="0" line-rate="0.3636" branch-rate="0">
186
  <methods/>
187
  <lines>
@@ -200,8 +208,12 @@
200
  </class>
201
  </classes>
202
  </package>
203
- <package name="routers" line-rate="0.7679" branch-rate="0" complexity="0">
204
  <classes>
 
 
 
 
205
  <class name="friend_request.py" filename="routers/friend_request.py" complexity="0" line-rate="0.6548" branch-rate="0">
206
  <methods/>
207
  <lines>
@@ -346,7 +358,7 @@
346
  <line number="16" hits="0"/>
347
  </lines>
348
  </class>
349
- <class name="video.py" filename="routers/video.py" complexity="0" line-rate="0.7895" branch-rate="0">
350
  <methods/>
351
  <lines>
352
  <line number="1" hits="1"/>
@@ -367,65 +379,65 @@
367
  <line number="25" hits="1"/>
368
  <line number="28" hits="1"/>
369
  <line number="29" hits="1"/>
370
- <line number="35" hits="1"/>
371
- <line number="36" hits="1"/>
372
- <line number="41" hits="1"/>
373
- <line number="42" hits="1"/>
374
  <line number="43" hits="0"/>
375
- <line number="46" hits="1"/>
376
- <line number="47" hits="1"/>
377
- <line number="50" hits="1"/>
378
- <line number="51" hits="1"/>
379
- <line number="52" hits="1"/>
380
- <line number="53" hits="1"/>
381
- <line number="54" hits="1"/>
382
- <line number="55" hits="1"/>
383
  <line number="56" hits="0"/>
384
  <line number="57" hits="0"/>
385
  <line number="58" hits="0"/>
386
  <line number="59" hits="0"/>
387
  <line number="62" hits="1"/>
388
- <line number="63" hits="1"/>
389
  <line number="66" hits="1"/>
390
- <line number="67" hits="1"/>
391
- <line number="68" hits="1"/>
392
- <line number="69" hits="1"/>
393
- <line number="72" hits="1"/>
394
- <line number="73" hits="1"/>
395
- <line number="77" hits="1"/>
396
- <line number="84" hits="1"/>
397
- <line number="85" hits="1"/>
398
- <line number="86" hits="1"/>
399
- <line number="87" hits="1"/>
400
- <line number="88" hits="1"/>
401
- <line number="89" hits="1"/>
402
- <line number="90" hits="1"/>
403
- <line number="92" hits="1"/>
404
- <line number="93" hits="1"/>
405
- <line number="95" hits="1"/>
406
- <line number="96" hits="1"/>
407
- <line number="98" hits="1"/>
408
- <line number="99" hits="1"/>
409
- <line number="101" hits="1"/>
410
- <line number="102" hits="1"/>
411
- <line number="105" hits="1"/>
412
- <line number="107" hits="1"/>
413
- <line number="109" hits="1"/>
414
- <line number="110" hits="1"/>
415
- <line number="111" hits="1"/>
416
- <line number="115" hits="1"/>
417
- <line number="116" hits="1"/>
418
- <line number="119" hits="1"/>
419
- <line number="123" hits="1"/>
420
- <line number="124" hits="1"/>
421
- <line number="125" hits="1"/>
422
  <line number="126" hits="0"/>
423
- <line number="127" hits="1"/>
424
- <line number="128" hits="1"/>
425
- <line number="130" hits="1"/>
426
  <line number="142" hits="0"/>
427
  <line number="143" hits="0"/>
428
- <line number="152" hits="1"/>
429
  <line number="155" hits="1"/>
430
  <line number="156" hits="1"/>
431
  <line number="157" hits="1"/>
 
1
  <?xml version="1.0" ?>
2
+ <coverage version="7.2.2" timestamp="1699459188469" lines-valid="371" lines-covered="252" line-rate="0.6792" branches-covered="0" branches-valid="0" branch-rate="0" complexity="0">
3
  <!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.2.2 -->
4
  <!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
5
  <sources>
 
102
  </package>
103
  <package name="custom_mmcv" line-rate="0.9697" branch-rate="0" complexity="0">
104
  <classes>
105
+ <class name="__init__.py" filename="custom_mmcv/__init__.py" complexity="0" line-rate="1" branch-rate="0">
106
+ <methods/>
107
+ <lines/>
108
+ </class>
109
  <class name="color.py" filename="custom_mmcv/color.py" complexity="0" line-rate="0.9688" branch-rate="0">
110
  <methods/>
111
  <lines>
 
186
  </package>
187
  <package name="graphdb" line-rate="0.3636" branch-rate="0" complexity="0">
188
  <classes>
189
+ <class name="__init__.py" filename="graphdb/__init__.py" complexity="0" line-rate="1" branch-rate="0">
190
+ <methods/>
191
+ <lines/>
192
+ </class>
193
  <class name="main.py" filename="graphdb/main.py" complexity="0" line-rate="0.3636" branch-rate="0">
194
  <methods/>
195
  <lines>
 
208
  </class>
209
  </classes>
210
  </package>
211
+ <package name="routers" line-rate="0.5491" branch-rate="0" complexity="0">
212
  <classes>
213
+ <class name="__init__.py" filename="routers/__init__.py" complexity="0" line-rate="1" branch-rate="0">
214
+ <methods/>
215
+ <lines/>
216
+ </class>
217
  <class name="friend_request.py" filename="routers/friend_request.py" complexity="0" line-rate="0.6548" branch-rate="0">
218
  <methods/>
219
  <lines>
 
358
  <line number="16" hits="0"/>
359
  </lines>
360
  </class>
361
+ <class name="video.py" filename="routers/video.py" complexity="0" line-rate="0.2737" branch-rate="0">
362
  <methods/>
363
  <lines>
364
  <line number="1" hits="1"/>
 
379
  <line number="25" hits="1"/>
380
  <line number="28" hits="1"/>
381
  <line number="29" hits="1"/>
382
+ <line number="35" hits="0"/>
383
+ <line number="36" hits="0"/>
384
+ <line number="41" hits="0"/>
385
+ <line number="42" hits="0"/>
386
  <line number="43" hits="0"/>
387
+ <line number="46" hits="0"/>
388
+ <line number="47" hits="0"/>
389
+ <line number="50" hits="0"/>
390
+ <line number="51" hits="0"/>
391
+ <line number="52" hits="0"/>
392
+ <line number="53" hits="0"/>
393
+ <line number="54" hits="0"/>
394
+ <line number="55" hits="0"/>
395
  <line number="56" hits="0"/>
396
  <line number="57" hits="0"/>
397
  <line number="58" hits="0"/>
398
  <line number="59" hits="0"/>
399
  <line number="62" hits="1"/>
400
+ <line number="63" hits="0"/>
401
  <line number="66" hits="1"/>
402
+ <line number="67" hits="0"/>
403
+ <line number="68" hits="0"/>
404
+ <line number="69" hits="0"/>
405
+ <line number="72" hits="0"/>
406
+ <line number="73" hits="0"/>
407
+ <line number="77" hits="0"/>
408
+ <line number="84" hits="0"/>
409
+ <line number="85" hits="0"/>
410
+ <line number="86" hits="0"/>
411
+ <line number="87" hits="0"/>
412
+ <line number="88" hits="0"/>
413
+ <line number="89" hits="0"/>
414
+ <line number="90" hits="0"/>
415
+ <line number="92" hits="0"/>
416
+ <line number="93" hits="0"/>
417
+ <line number="95" hits="0"/>
418
+ <line number="96" hits="0"/>
419
+ <line number="98" hits="0"/>
420
+ <line number="99" hits="0"/>
421
+ <line number="101" hits="0"/>
422
+ <line number="102" hits="0"/>
423
+ <line number="105" hits="0"/>
424
+ <line number="107" hits="0"/>
425
+ <line number="109" hits="0"/>
426
+ <line number="110" hits="0"/>
427
+ <line number="111" hits="0"/>
428
+ <line number="115" hits="0"/>
429
+ <line number="116" hits="0"/>
430
+ <line number="119" hits="0"/>
431
+ <line number="123" hits="0"/>
432
+ <line number="124" hits="0"/>
433
+ <line number="125" hits="0"/>
434
  <line number="126" hits="0"/>
435
+ <line number="127" hits="0"/>
436
+ <line number="128" hits="0"/>
437
+ <line number="130" hits="0"/>
438
  <line number="142" hits="0"/>
439
  <line number="143" hits="0"/>
440
+ <line number="152" hits="0"/>
441
  <line number="155" hits="1"/>
442
  <line number="156" hits="1"/>
443
  <line number="157" hits="1"/>
tests/test_image.py CHANGED
@@ -24,7 +24,7 @@ class TestImageRoute():
24
  def test_inferenceImage(self):
25
  bboxes, labels = inferenceImage(mmcv.imread('demo.jpg'), 0.3, True)
26
  assert len(bboxes.tolist()) > 0 and len(labels.tolist()) > 0 and len(bboxes.tolist()) == len(labels.tolist())
27
- result = inferenceImage(self.img, 0.3)
28
  assert type(result) is np.ndarray and result.shape == self.img.shape
29
  def test_ImageAPI(self, client):
30
  payload = {}
 
24
  def test_inferenceImage(self):
25
  bboxes, labels = inferenceImage(mmcv.imread('demo.jpg'), 0.3, True)
26
  assert len(bboxes.tolist()) > 0 and len(labels.tolist()) > 0 and len(bboxes.tolist()) == len(labels.tolist())
27
+ result = inferenceImage(self.img, 0.3, False)
28
  assert type(result) is np.ndarray and result.shape == self.img.shape
29
  def test_ImageAPI(self, client):
30
  payload = {}
tests/test_video.py CHANGED
@@ -1,13 +1,17 @@
1
  from fastapi.testclient import TestClient
2
  from fastapi.routing import APIRoute
3
- from app.routers.video import updateArtifact
4
  from app.main import app
5
  from app.constants import deviceId
6
  from app import db
 
 
7
  import os
8
  import pytest
9
  import requests
10
  import json
 
 
11
  from google.cloud.firestore_v1.base_query import FieldFilter
12
 
13
  def endpoints():
@@ -35,6 +39,7 @@ def user():
35
  response = requests.request("POST", url, headers=headers, data=payload)
36
  data = response.json()
37
  user = {"id": data['localId'], "token": data["idToken"]}
 
38
  yield user
39
  db.collection("user").document(user['id']).delete()
40
  class TestVideoAPI:
@@ -60,7 +65,6 @@ class TestVideoAPI:
60
  response = client.request("POST", 'video', headers=headers, data=payload, files=files)
61
  assert response.status_code == 401
62
  # Test when sent file is not a video
63
- db.collection("user").document(user['id']).set({"deviceId": deviceId})
64
  payload = {}
65
  files=[
66
  ('file',('demo.jpg',open('demo.jpg','rb'),'application/octet-stream'))
@@ -68,7 +72,10 @@ class TestVideoAPI:
68
  headers = {
69
  'Authorization': "Bearer " + user['token']
70
  }
71
- response = client.request("POST", 'video', headers=headers, data=payload, files=files)
 
 
 
72
  assert response.status_code == 400
73
  # Test when all requirements have been fulfilled
74
  payload = {}
@@ -104,5 +111,23 @@ class TestVideoAPI:
104
  assert db.collection("artifacts").document('test').get().to_dict()['status'] == 'test_done'
105
  #Delete data for next time test
106
  test_artifact.delete()
107
-
108
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi.testclient import TestClient
2
  from fastapi.routing import APIRoute
3
+ from app.routers.video import updateArtifact, createThumbnail, inference_frame
4
  from app.main import app
5
  from app.constants import deviceId
6
  from app import db
7
+ import platform
8
+ import mmcv
9
  import os
10
  import pytest
11
  import requests
12
  import json
13
+ import cv2
14
+ import shutil
15
  from google.cloud.firestore_v1.base_query import FieldFilter
16
 
17
  def endpoints():
 
39
  response = requests.request("POST", url, headers=headers, data=payload)
40
  data = response.json()
41
  user = {"id": data['localId'], "token": data["idToken"]}
42
+ db.collection("user").document(user['id']).set({"deviceId": deviceId})
43
  yield user
44
  db.collection("user").document(user['id']).delete()
45
  class TestVideoAPI:
 
65
  response = client.request("POST", 'video', headers=headers, data=payload, files=files)
66
  assert response.status_code == 401
67
  # Test when sent file is not a video
 
68
  payload = {}
69
  files=[
70
  ('file',('demo.jpg',open('demo.jpg','rb'),'application/octet-stream'))
 
72
  headers = {
73
  'Authorization': "Bearer " + user['token']
74
  }
75
+ while True:
76
+ response = client.request("POST", 'video', headers=headers, data=payload, files=files)
77
+ if response.status_code != 401:
78
+ break
79
  assert response.status_code == 400
80
  # Test when all requirements have been fulfilled
81
  payload = {}
 
111
  assert db.collection("artifacts").document('test').get().to_dict()['status'] == 'test_done'
112
  #Delete data for next time test
113
  test_artifact.delete()
114
+ def test_inference_frame(self):
115
+ if not os.path.exists('test_vid'):
116
+ os.mkdir('test_vid')
117
+ shutil.copyfile('demo.mp4', 'test_vid/input.mp4')
118
+ thumbnail = inference_frame('test_vid')
119
+ assert os.path.exists("test_vid/out.mp4") and os.path.isfile('test_vid/out.mp4')
120
+ vidcap = cv2.VideoCapture('test_vid/input.mp4')
121
+ success, image = vidcap.read()
122
+ if success:
123
+ assert (image.shape == thumbnail.shape)
124
+ vidcap.release()
125
+ del vidcap
126
+ shutil.rmtree('test_vid')
127
+ def test_create_thumbnal(self):
128
+ vidcap = cv2.VideoCapture('demo.mp4')
129
+ success, image = vidcap.read()
130
+ if success:
131
+ createThumbnail(image, "")
132
+ result = mmcv.imread('thumbnail.jpg', channel_order='rgb')
133
+ assert (result.shape == (160, 160, 3))