Skip to content

Commit

Permalink
Merge pull request #35 from locaal-ai/roy.image_rotation
Browse files Browse the repository at this point in the history
feat: Add rotation functionality for camera view
  • Loading branch information
royshil authored Sep 18, 2024
2 parents 2ad1849 + 90deb53 commit 40da39e
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 83 deletions.
34 changes: 31 additions & 3 deletions camera_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,18 +81,20 @@ def set_camera_highest_resolution(cap):
set_resolution(cap, *highest_res)


class FrameCrop:
class FrameCropAndRotation:
def __init__(self):
self.isCropSet = fetch_data("scoresight.json", "crop_mode", False)
self.cropTop = fetch_data("scoresight.json", "top_crop", 0)
self.cropBottom = fetch_data("scoresight.json", "bottom_crop", 0)
self.cropLeft = fetch_data("scoresight.json", "left_crop", 0)
self.cropRight = fetch_data("scoresight.json", "right_crop", 0)
self.rotation = fetch_data("scoresight.json", "rotation", 0)
subscribe_to_data("scoresight.json", "crop_mode", self.setCropMode)
subscribe_to_data("scoresight.json", "top_crop", self.setCropTop)
subscribe_to_data("scoresight.json", "bottom_crop", self.setCropBottom)
subscribe_to_data("scoresight.json", "left_crop", self.setCropLeft)
subscribe_to_data("scoresight.json", "right_crop", self.setCropRight)
subscribe_to_data("scoresight.json", "rotation", self.setRotation)

def setCropMode(self, crop_mode):
self.isCropSet = crop_mode
Expand All @@ -109,6 +111,9 @@ def setCropLeft(self, crop_left):
def setCropRight(self, crop_right):
self.cropRight = crop_right

def setRotation(self, rotation):
self.rotation = rotation


class TimerThread(QThread):
update_signal = Signal(object)
Expand All @@ -134,14 +139,22 @@ def __init__(
self.video_capture = None
self.should_stop = False
self.frame_interval = 30
self.update_frame_interval = 200
self.update_frame_interval = 1000 / fetch_data(
"scoresight.json", "detection_cadence", 5
)
subscribe_to_data(
"scoresight.json", "detection_cadence", self.setUpdateFrameInterval
)
self.preview_frame_interval = 1000
self.fps = 1000 / self.frame_interval # frames per second
self.pps = 1000 / self.preview_frame_interval # previews per second
self.ups = 1000 / self.update_frame_interval # updates per second
self.fps_alpha = 0.1 # Smoothing factor
self.updateOnChange = True
self.crop = FrameCrop()
self.crop = FrameCropAndRotation()

def setUpdateFrameInterval(self, cadence):
self.update_frame_interval = 1000 / cadence

def connect_video_capture(self) -> bool:
if self.camera_info.type == CameraInfo.CameraType.NDI:
Expand All @@ -153,6 +166,7 @@ def connect_video_capture(self) -> bool:
if (
os_name == "Windows"
and self.camera_info.type != CameraInfo.CameraType.FILE
and self.camera_info.type != CameraInfo.CameraType.URL
):
# on windows use the dshow backend
self.video_capture = cv2.VideoCapture(
Expand Down Expand Up @@ -278,6 +292,20 @@ def run(self):
+ (1.0 - self.fps_alpha) * self.ups
)

# apply rotation if set
if self.crop.rotation != 0:
# use cv2.rotate to rotate the frame
rotateCode = (
cv2.ROTATE_90_CLOCKWISE
if self.crop.rotation == 90
else (
cv2.ROTATE_90_COUNTERCLOCKWISE
if self.crop.rotation == 270
else cv2.ROTATE_180
)
)
frame_rgb = cv2.rotate(frame_rgb, rotateCode)

# apply top-level crop if set
if self.crop.isCropSet:
frame_rgb = frame_rgb[
Expand Down
50 changes: 32 additions & 18 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
import datetime
from PySide6.QtWidgets import (
QApplication,
QMainWindow,
QDialog,
QFileDialog,
QInputDialog,
QLabel,
QMainWindow,
QMenu,
QDialog,
QInputDialog,
QMessageBox,
QTableWidgetItem,
)
from PySide6.QtGui import QIcon, QStandardItemModel, QStandardItem, QDesktopServices
Expand Down Expand Up @@ -145,7 +146,6 @@ def __init__(self, translator: QTranslator, parent: QObject):
self.installEventFilter(self)

self.ui.pushButton_connectObs.clicked.connect(self.openOBSConnectModal)
self.ui.statusbar.showMessage("OBS: Not Connected")

self.vmixUiSetup()

Expand Down Expand Up @@ -181,6 +181,15 @@ def __init__(self, translator: QTranslator, parent: QObject):
self.ui.spinBox_bottomCrop.setValue(
fetch_data("scoresight.json", "bottom_crop", 0)
)
self.ui.checkBox_enableOutAPI.toggled.connect(
partial(self.globalSettingsChanged, "enable_out_api")
)
self.ui.checkBox_enableOutAPI.setChecked(
fetch_data("scoresight.json", "enable_out_api", False)
)

# connect toolButton_rotate
self.ui.toolButton_rotate.clicked.connect(self.rotateImage)

self.ui.widget_detectionCadence.setVisible(True)
self.ui.horizontalSlider_detectionCadence.setValue(
Expand Down Expand Up @@ -370,6 +379,14 @@ def __init__(self, translator: QTranslator, parent: QObject):
self.get_sources.connect(self.getSources)
self.get_sources.emit()

def rotateImage(self):
# store the rotation in the scoresight.json
rotation = fetch_data("scoresight.json", "rotation", 0)
rotation += 90
if rotation >= 360:
rotation = 0
self.globalSettingsChanged("rotation", rotation)

def cropMode(self):
# if the toolButton_topCrop is unchecked, go to crop mode
if self.ui.toolButton_topCrop.isChecked():
Expand Down Expand Up @@ -441,8 +458,15 @@ def importConfiguration(self):
return
# load the configuration from the file
if not self.detectionTargetsStorage.loadBoxesFromFile(file):
# show an error message
self.ui.statusbar.showMessage("Error loading configuration file")
# show an error qmessagebox
logger.error("Error loading configuration file")
QMessageBox.critical(
self,
"Error",
"Error loading configuration file",
QMessageBox.StandardButton.Ok,
)
return

def exportConfiguration(self):
# open a file dialog to select the output file
Expand Down Expand Up @@ -516,11 +540,6 @@ def toggleStabilize(self):
self.image_viewer.toggleStabilization(self.ui.pushButton_stabilize.isChecked())

def toggleStopUpdates(self, value):
self.ui.statusbar.showMessage(
self.translator.translate("main", "Stopped updates")
if value
else self.translator.translate("main", "Resumed updates")
)
self.updateOCRResults = not value
# change the text on the button
self.ui.pushButton_stopUpdates.setText(
Expand Down Expand Up @@ -969,8 +988,7 @@ def connectObs(self):
self.ui.checkBox_recreate.setEnabled(True)
self.ui.pushButton_createOBSScene.setEnabled(True)

# set OBS status to connected in the status bar
self.ui.statusbar.showMessage("OBS: Connected")
logger.info("OBS: Connected")

@Slot()
def getSources(self):
Expand Down Expand Up @@ -1221,10 +1239,8 @@ def cameraConnectedEnableUI(self):

def updateError(self, error):
if not error:
self.ui.statusbar.clearMessage()
return
# show the error in the status bar
self.ui.statusbar.showMessage(error)
logger.error(error)
self.ui.frame_source_view.setEnabled(True)
self.ui.widget_viewTools.setEnabled(False)

Expand Down Expand Up @@ -1485,12 +1501,10 @@ def makeTemplateField(self, toggled: bool):
self.listItemClicked(item)

def createOBSScene(self):
self.ui.statusbar.showMessage("Creating OBS scene")
# get the scene name from the lineEdit_sceneName
scene_name = self.ui.lineEdit_sceneName.text()
# clear or create a new scene
create_obs_scene_from_export(self.obs_websocket_client, scene_name)
self.ui.statusbar.showMessage("Finished creating scene")

# on destroy, close the OBS connection
def closeEvent(self, event):
Expand Down
78 changes: 49 additions & 29 deletions mainwindow.ui
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>961</width>
<height>734</height>
<height>725</height>
</rect>
</property>
<property name="windowTitle">
Expand Down Expand Up @@ -1439,28 +1439,21 @@
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="1" column="0">
<widget class="QLabel" name="label_20">
<item row="0" column="1">
<widget class="QCheckBox" name="checkBox_enableOutAPI">
<property name="text">
<string>URL</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="lineEdit_api_url">
<property name="placeholderText">
<string>http://</string>
<string>Send out API requests to external services.</string>
</property>
</widget>
</item>
<item row="3" column="0">
<item row="4" column="0">
<widget class="QLabel" name="label_21">
<property name="text">
<string>Encode</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="4" column="1">
<widget class="QComboBox" name="comboBox_api_encode">
<item>
<property name="text">
Expand All @@ -1484,13 +1477,6 @@
</item>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="checkBox_is_websocket">
<property name="text">
<string>Websocket?</string>
</property>
</widget>
</item>
<item row="5" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
Expand All @@ -1504,17 +1490,45 @@
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="pushButton_api_start">
<property name="text">
<string>Start</string>
</property>
<item row="1" column="1">
<widget class="QWidget" name="widget_24" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_27">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="lineEdit_api_url">
<property name="placeholderText">
<string>http://</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="checkBox_is_websocket">
<property name="text">
<string>Websocket</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="QLabel" name="label_22">
<item row="1" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Send out API requests to external services.</string>
<string>URL</string>
</property>
</widget>
</item>
Expand Down Expand Up @@ -1802,6 +1816,13 @@
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="toolButton_rotate">
<property name="text">
<string>Rotate</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="pushButton_stabilize">
<property name="enabled">
Expand Down Expand Up @@ -2093,7 +2114,6 @@
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
Expand Down
Loading

0 comments on commit 40da39e

Please sign in to comment.