Skip to content

Commit

Permalink
WIP rotate video support
Browse files Browse the repository at this point in the history
  • Loading branch information
Timozen committed Feb 20, 2024
1 parent 5995b2b commit bc3f832
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 14 deletions.
35 changes: 31 additions & 4 deletions frontend/ui_facial_feature_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,22 @@ def __init__(self, parent):
self.use_bbox = QtWidgets.QCheckBox("Use Bounding Box")
self.use_bbox.setToolTip("Use the bounding box to extract the landmarks.")

self.rotate_group = QtWidgets.QGroupBox("Rotate")
self.rotate_group.setLayout(QtWidgets.QVBoxLayout())

self.rotate_none = QtWidgets.QRadioButton("None")
self.rotate_90 = QtWidgets.QRadioButton("90")
self.rotate_m90 = QtWidgets.QRadioButton("-90")

self.rotate_none.setChecked(True)
self.rotate_group.layout().addWidget(self.rotate_none)
self.rotate_group.layout().addWidget(self.rotate_90)
self.rotate_group.layout().addWidget(self.rotate_m90)
self.current_rotation = "None"
self.rotate_none.toggled.connect(self.set_rotation)
self.rotate_90.toggled.connect(self.set_rotation)
self.rotate_m90.toggled.connect(self.set_rotation)

self.add_handler("auto_save", self.auto_save, default=True)
self.add_handler("use_bbox", self.use_bbox, default=True)
self.add_handler("auto_find_face", self.widget_frame.cb_auto_find, default=True)
Expand All @@ -191,6 +207,8 @@ def __init__(self, parent):
self.flayout_se.addRow(self.button_start)
self.flayout_se.addRow(self.bt_pause_resume)
self.flayout_se.addRow(self.button_stop)
self.flayout_se.addRow(self.rotate_group)

self.flayout_se.addRow(self.feature_group)
self.flayout_se.addRow(self.blends_shape_group)
self.flayout_se.addRow("Graph Update Delay:", self.skip_frame)
Expand Down Expand Up @@ -240,6 +258,15 @@ def __init__(self, parent):
self.jefapato_signal_thread.sig_finished.connect(self.sig_finished)
self.jefapato_signal_thread.start()

def set_rotation(self):
if self.rotate_none.isChecked():
self.current_rotation = "None"
elif self.rotate_90.isChecked():
self.current_rotation = "90"
elif self.rotate_m90.isChecked():
self.current_rotation = "-90"
self.set_resource(self.video_resource)

def setup_graph(self) -> None:
logger.info("Setup graph for all features to plot", features=len(self.used_features_classes))
self.widget_graph.clear()
Expand Down Expand Up @@ -285,7 +312,7 @@ def start(self) -> None:
self.ea.set_features(self.used_features_classes)

rect = self.widget_frame.get_roi_rect() if self.use_bbox.isChecked() else None
self.ea.clean_start(rect)
self.ea.clean_start(rect, self.current_rotation)

def stop(self) -> None:
self.ea.stop()
Expand Down Expand Up @@ -371,7 +398,7 @@ def load_video(self):
parent=self,
caption="Select video file",
directory=".",
filter="Video Files (*.mp4 *.flv *.ts *.mts *.avi *.mov)",
filter="Video Files (*.mp4 *.flv *.ts *.mts *.avi *.mov *.wmv)",
)

if fileName == "":
Expand Down Expand Up @@ -404,7 +431,7 @@ def set_resource(self, resource: Path | int) -> bool:
else:
self.la_current_file.setText("File: Live Webcam Feed")

success, frame = self.ea.prepare_video_resource(self.video_resource)
success, frame = self.ea.prepare_video_resource(self.video_resource, self.current_rotation)
if success:
logger.info("Image was set", parent=self)
self.widget_frame.set_selection_image(frame)
Expand Down Expand Up @@ -454,7 +481,7 @@ def dropEvent(self, event: QtGui.QDropEvent):
file = files[0]

file = Path(file)
if file.suffix.lower() not in [".mp4", ".flv", ".ts", ".mts", ".avi", ".mov"]:
if file.suffix.lower() not in [".mp4", ".flv", ".ts", ".mts", ".avi", ".mov", ".wmv"]:
logger.info("User dropped invalid file", widget=self)
return
self.set_resource(Path(file))
23 changes: 16 additions & 7 deletions src/jefapato/facial_features/landmark_analyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def __init__(self, max_ram_size: int = 4<<28):

self.pm = pluggy.PluginManager("analyser")

def analysis_setup(self, bbox_slice: tuple[int, int, int, int] | None = None) -> bool:
def analysis_setup(self, bbox_slice: tuple[int, int, int, int] | None = None, rotation:str="None") -> bool:
"""
Sets up the analysis by initializing necessary components and calculating available resources.
Expand Down Expand Up @@ -73,7 +73,7 @@ def analysis_setup(self, bbox_slice: tuple[int, int, int, int] | None = None) ->
if bbox_slice is not None:
logger.info("Bounding box slice", bbox=bbox_slice)

self.loader = VideoDataLoader(self.resource_interface.read, data_amount=self.data_amount, queue_maxsize=items_to_place)
self.loader = VideoDataLoader(self.resource_interface.read, data_amount=self.data_amount, queue_maxsize=items_to_place, rotation=rotation)
self.extractor = MediapipeLandmarkExtractor(data_queue=self.loader.data_queue, data_amount=self.data_amount, bbox_slice=bbox_slice)
self.extractor.register(self)
return True
Expand Down Expand Up @@ -200,7 +200,7 @@ def toggle_pause(self) -> None:

self.extractor.toggle_pause()

def clean_start(self, bbox_slice: tuple[int, int, int, int] | None = None) -> None:
def clean_start(self, bbox_slice: tuple[int, int, int, int] | None = None, rotation:str = "None") -> None:
"""
Starts the landmark analysis process.
Expand All @@ -217,7 +217,7 @@ def clean_start(self, bbox_slice: tuple[int, int, int, int] | None = None) -> No
for m_name in self.feature_classes:
self.feature_data[m_name].clear()

self.analysis_setup(bbox_slice=bbox_slice)
self.analysis_setup(bbox_slice=bbox_slice, rotation=rotation)
self.analysis_start()

def get_header(self) -> list[str]:
Expand Down Expand Up @@ -247,7 +247,7 @@ def __next__(self):

return row

def prepare_video_resource(self, value: Path) -> tuple[bool, np.ndarray]:
def prepare_video_resource(self, value: Path, rotation: str = "None") -> tuple[bool, np.ndarray]:
self.video_resource = value

if not isinstance(self.video_resource, Path):
Expand All @@ -259,14 +259,23 @@ def prepare_video_resource(self, value: Path) -> tuple[bool, np.ndarray]:
if not self.video_resource.is_file():
raise ValueError(f"File {self.video_resource} is not a file.")

if self.video_resource.suffix.lower() not in [".mp4", ".flv", ".ts", ".mts", ".avi", ".mov"]:
if self.video_resource.suffix.lower() not in [".mp4", ".flv", ".ts", ".mts", ".avi", ".mov", ".wmv"]:
raise ValueError(f"File {self.video_resource} is not a video file.")

self.resource_interface = cv2.VideoCapture(str(self.video_resource.absolute()))
self.data_amount = self.resource_interface.get(cv2.CAP_PROP_FRAME_COUNT)

success, image = self.resource_interface.read()
return success, cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

if not success:
return success, None

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
if rotation == "90":
image = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
elif rotation == "-90":
image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
return success, image

def get_fps(self) -> float:
"""
Expand Down
12 changes: 9 additions & 3 deletions src/jefapato/facial_features/video_data_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(
self,
next_item_func: Callable,
data_amount: int | None = None,
queue_maxsize: int = (1 << 10)
queue_maxsize: int = (1 << 10),
rotation: str = "None",
) -> None:
super().__init__(daemon=True)
assert data_amount is not None, "data_amount must be set"
Expand All @@ -28,9 +29,13 @@ def __init__(
self.data_queue: queue.Queue[InputQueueItem] = queue.Queue(maxsize=queue_maxsize)
self.stopped = False
self.data_amount = data_amount

self.processing_per_second: int = 0

if rotation == "None":
self.rotation_fc = lambda x: x
elif rotation == "90":
self.rotation_fc = lambda x: cv2.rotate(x, cv2.ROTATE_90_CLOCKWISE)
elif rotation == "-90":
self.rotation_fc = lambda x: cv2.rotate(x, cv2.ROTATE_90_COUNTERCLOCKWISE)

def run(self):
logger.info("Loader Thread", state="starting", object=self)
Expand Down Expand Up @@ -66,6 +71,7 @@ def run(self):
self.stopped = True
break
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frame = self.rotation_fc(frame)
self.data_queue.put(InputQueueItem(frame=frame, timestamp=c_time))
processed_p_sec += 1
else:
Expand Down

0 comments on commit bc3f832

Please sign in to comment.