Skip to content

Commit 0b91139

Browse files
authored
Merge pull request #865 from roflcoopter/feature/face-recognition-import
Avoid importing dlib when it is not used
2 parents 3021569 + ae3d6de commit 0b91139

File tree

7 files changed

+54
-52
lines changed

7 files changed

+54
-52
lines changed

viseron/components/codeprojectai/face_recognition.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
import codeprojectai.core as cpai
99
import cv2
1010
import requests
11-
from face_recognition.face_recognition_cli import image_files_in_folder
1211

1312
from viseron.domains.camera.shared_frames import SharedFrame
1413
from viseron.domains.face_recognition import AbstractFaceRecognition
1514
from viseron.domains.face_recognition.const import CONFIG_FACE_RECOGNITION_PATH
16-
from viseron.helpers import calculate_absolute_coords, letterbox_resize
15+
from viseron.helpers import (
16+
calculate_absolute_coords,
17+
get_image_files_in_folder,
18+
letterbox_resize,
19+
)
1720

1821
from .const import (
1922
COMPONENT,
@@ -146,7 +149,7 @@ def train(self) -> None:
146149

147150
# Loop through each training image for the current person
148151
try:
149-
img_paths = image_files_in_folder(os.path.join(train_dir, face_dir))
152+
img_paths = get_image_files_in_folder(os.path.join(train_dir, face_dir))
150153
except NotADirectoryError as error:
151154
LOGGER.error(
152155
f"{train_dir} can only contain directories. "

viseron/components/compreface/face_recognition.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,12 @@
88
import cv2
99
from compreface import CompreFace
1010
from compreface.collections import FaceCollection, Subjects
11-
from face_recognition.face_recognition_cli import image_files_in_folder
1211

1312
from viseron.domains.camera.shared_frames import SharedFrame
1413
from viseron.domains.face_recognition import AbstractFaceRecognition
1514
from viseron.domains.face_recognition.binary_sensor import FaceDetectionBinarySensor
1615
from viseron.domains.face_recognition.const import CONFIG_FACE_RECOGNITION_PATH
17-
from viseron.helpers import calculate_absolute_coords
16+
from viseron.helpers import calculate_absolute_coords, get_image_files_in_folder
1817

1918
from .const import (
2019
COMPONENT,
@@ -218,7 +217,7 @@ def train(self) -> None:
218217

219218
# Loop through each training image for the current person
220219
try:
221-
img_paths = image_files_in_folder(os.path.join(train_dir, face_dir))
220+
img_paths = get_image_files_in_folder(os.path.join(train_dir, face_dir))
222221
except NotADirectoryError as error:
223222
LOGGER.error(
224223
f"{train_dir} can only contain directories. "

viseron/components/deepstack/face_recognition.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@
88
import cv2
99
import deepstack.core as ds
1010
import requests
11-
from face_recognition.face_recognition_cli import image_files_in_folder
1211

1312
from viseron.domains.camera.shared_frames import SharedFrame
1413
from viseron.domains.face_recognition import AbstractFaceRecognition
1514
from viseron.domains.face_recognition.const import CONFIG_FACE_RECOGNITION_PATH
16-
from viseron.helpers import calculate_absolute_coords
15+
from viseron.helpers import calculate_absolute_coords, get_image_files_in_folder
1716

1817
from .const import (
1918
COMPONENT,
@@ -141,7 +140,7 @@ def train(self) -> None:
141140

142141
# Loop through each training image for the current person
143142
try:
144-
img_paths = image_files_in_folder(os.path.join(train_dir, face_dir))
143+
img_paths = get_image_files_in_folder(os.path.join(train_dir, face_dir))
145144
except NotADirectoryError as error:
146145
LOGGER.error(
147146
f"{train_dir} can only contain directories. "

viseron/components/dlib/train.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55

66
import face_recognition
77
import PIL
8-
from face_recognition.face_recognition_cli import image_files_in_folder
98
from sklearn import neighbors
109

10+
from viseron.helpers import get_image_files_in_folder
11+
1112
LOGGER = logging.getLogger(__name__)
1213

1314

@@ -72,7 +73,7 @@ def train(
7273

7374
# Loop through each training image for the current person
7475
try:
75-
img_paths = image_files_in_folder(
76+
img_paths = get_image_files_in_folder(
7677
os.path.join(face_recognition_path, face_dir)
7778
)
7879
except NotADirectoryError as error:

viseron/components/webserver/api/__init__.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
import importlib
55
import logging
6+
from functools import cache
7+
from pathlib import Path
68
from typing import TYPE_CHECKING, Any
79

810
import tornado.routing
@@ -18,6 +20,33 @@
1820
LOGGER = logging.getLogger(__name__)
1921

2022

23+
@cache
24+
def get_handler(api_version: str, endpoint: str):
25+
"""Get handler for endpoint."""
26+
version_path = Path(__file__).parent / api_version
27+
28+
if not version_path.is_dir():
29+
return APINotFoundHandler
30+
31+
module_path = version_path / f"{endpoint}.py"
32+
if not module_path.is_file():
33+
return APINotFoundHandler
34+
35+
try:
36+
module = importlib.import_module(
37+
f"viseron.components.webserver.api.{api_version}.{endpoint}"
38+
)
39+
handler_name = f"{endpoint.title()}APIHandler"
40+
if hasattr(module, handler_name):
41+
return getattr(module, handler_name)
42+
except ImportError as error:
43+
LOGGER.warning(
44+
f"Error importing API handler {endpoint}: {error}",
45+
exc_info=True,
46+
)
47+
return APINotFoundHandler
48+
49+
2150
class APIRouter(tornado.routing.Router):
2251
"""Catch-all API Router."""
2352

@@ -46,29 +75,12 @@ def find_handler(
4675
target_kwargs={"vis": self._vis},
4776
)
4877

49-
endpoint_handler = f"{endpoint.title()}APIHandler"
50-
try:
51-
handler = getattr(
52-
importlib.import_module(
53-
f"viseron.components.webserver.api.{api_version}".format(
54-
api_version
55-
)
56-
),
57-
endpoint_handler,
58-
)
59-
except AttributeError:
78+
handler = get_handler(api_version, endpoint)
79+
if handler == APINotFoundHandler:
6080
LOGGER.warning(
6181
f"Unable to find handler for path: {request.path}",
62-
exc_info=True,
6382
)
64-
handler = APINotFoundHandler
65-
except ModuleNotFoundError as error:
66-
LOGGER.warning(
67-
f"Error importing API endpoint module: {error}", exc_info=True
68-
)
69-
handler = APINotFoundHandler
7083

71-
# Return handler
7284
return self._application.get_handler_delegate(
7385
request=request,
7486
target_class=handler,
Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1 @@
11
"""Viseron API."""
2-
3-
from viseron.components.webserver.api.v1.auth import AuthAPIHandler
4-
from viseron.components.webserver.api.v1.camera import CameraAPIHandler
5-
from viseron.components.webserver.api.v1.cameras import CamerasAPIHandler
6-
from viseron.components.webserver.api.v1.compreface import ComprefaceAPIHandler
7-
from viseron.components.webserver.api.v1.config import ConfigAPIHandler
8-
from viseron.components.webserver.api.v1.events import EventsAPIHandler
9-
from viseron.components.webserver.api.v1.hls import HlsAPIHandler
10-
from viseron.components.webserver.api.v1.onboarding import OnboardingAPIHandler
11-
from viseron.components.webserver.api.v1.recordings import RecordingsAPIHandler
12-
13-
__all__ = (
14-
"AuthAPIHandler",
15-
"CameraAPIHandler",
16-
"CamerasAPIHandler",
17-
"ComprefaceAPIHandler",
18-
"ConfigAPIHandler",
19-
"EventsAPIHandler",
20-
"HlsAPIHandler",
21-
"OnboardingAPIHandler",
22-
"RecordingsAPIHandler",
23-
)

viseron/helpers/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import math
88
import multiprocessing as mp
99
import os
10+
import re
1011
import socket
1112
import time
1213
import tracemalloc
@@ -670,6 +671,15 @@ def parse_size_to_bytes(size_str: str) -> int:
670671
)
671672

672673

674+
def get_image_files_in_folder(folder) -> list[str]:
675+
"""Return all files with JPG, JPEG or PNG extension."""
676+
return [
677+
os.path.join(folder, f)
678+
for f in os.listdir(folder)
679+
if re.match(r".*\.(jpg|jpeg|png)", f, flags=re.I)
680+
]
681+
682+
673683
def memory_usage_profiler(logger, key_type="lineno", limit=5) -> None:
674684
"""Print a table with the lines that are using the most memory."""
675685
snapshot = tracemalloc.take_snapshot()

0 commit comments

Comments
 (0)