721 lines
26 KiB
Python

import kp
import cv2, os, shutil, sys
from enum import Enum
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QPushButton,
QComboBox, QFileDialog, QMessageBox, QHBoxLayout, QDialog, QListWidget,
QScrollArea, QFrame, QListWidgetItem, QTextEdit)
from PyQt5.QtSvg import QSvgWidget
from PyQt5.QtMultimedia import QCamera, QCameraImageCapture, QCameraInfo, QMediaRecorder, QAudioRecorder
from PyQt5.QtMultimediaWidgets import QVideoWidget
from PyQt5.QtGui import QPixmap, QMovie
from PyQt5.QtCore import Qt, QTimer, QUrl
# Global Constants
UXUI_ASSETS = "../../uxui/"
WINDOW_SIZE = (1200, 900)
BACKGROUND_COLOR = "#143058"
SECONDARY_COLOR = "#1260E6"
DEVICE_BOX_STYLE = f"background-color: {BACKGROUND_COLOR}; padding: 20px; border-radius: 20px; padding: 10px 20px;"
BUTTON_STYLE = """
QPushButton {
background: transparent;
color: white;
border: 1px solid white;
border-radius: 20px;
padding: 10px 20px;
}
QPushButton:hover {
background-color: rgba(255, 255, 255, 50);
}
QPushButton:pressed {
background-color: rgba(255, 255, 255, 100);
}
"""
SQUARE_BUTTON_STYLE = "background: transparent; color: white; border: 1px transparent; border-radius: 10px; "
POPUP_SIZE_RATIO = 0.67
NO_DEVICE_GIF = UXUI_ASSETS + "no_device_temp.gif"
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.init_ui()
self.model_buttons = [
('Face Detection', self.run_face_detection),
('Gender/Age Detection', self.run_gender_age_detection),
('Object Detection', self.run_object_detection),
('Mask Detection', self.run_mask_detection),
('Image Project', self.start_image_project),
('Upload Model', self.upload_model)
]
self.connected_devices = [
]
self.input_directory = ""
self.to_keep_directory = ""
self.to_delete_directory = ""
self.image_directory = ""
self.label_directory = ""
self.video_widget = QVideoWidget(self)
self.camera = QCamera(QCameraInfo.defaultCamera())
self.image_capture = QCameraImageCapture(self.camera)
self.media_recorder = QMediaRecorder(self.camera)
self.audio_recorder = QAudioRecorder(self)
self.camera.setViewfinder(self.video_widget)
self.right_layout = QVBoxLayout()
self.left_layout = QVBoxLayout()
# TODO: find the correct mapping of the values
class K_(Enum):
KL520 = 256
KL720 = 720
KL720_L = 512 #legacy
KL530 = 530
KL832 = 832
KL730 = 732
KL630 = 630
KL540 = 540
def init_ui(self):
self.setGeometry(100, 100, *WINDOW_SIZE)
self.setWindowTitle('Innovedus AI Playground')
self.setStyleSheet(f"background-color: {BACKGROUND_COLOR};")
self.layout = QVBoxLayout(self)
self.show_welcome_label()
QTimer.singleShot(5000, self.show_device_popup_and_main_page)
def show_welcome_label(self):
welcome_label = QLabel(self)
welcome_pixmap = QPixmap(UXUI_ASSETS + "kneron_logo.png")
welcome_label.setPixmap(welcome_pixmap)
welcome_label.setAlignment(Qt.AlignCenter)
self.layout.addWidget(welcome_label)
def close_connection_page(self):
print("closing device connection page")
device_descriptors = kp.core.scan_devices()
if device_descriptors.device_descriptor_number > 0:
for device in device_descriptors.device_descriptor_list:
self.parse_and_store_devices(device_descriptors.device_descriptor_list)
kp.core.connect_devices(usb_port_ids=[device.usb_port_id])
self.load_firmware()
self.popup_window.close()
def load_firmware(self):
print("loading firmware")
for device in self.connected_devices:
device_group = kp.core.connect_devices(usb_port_ids=[device.get["usb_port_id"]])
kp.core.set_timeout(device_group=device_group, milliseconds=5000)
SCPU_FW_PATH = f'../../external/res/firmware/{device.get["product_id"]}/fw_scpu.bin'
NCPU_FW_PATH = f'../../external/res/firmware/{device.get["product_id"]}/fw_ncpu.bin'
kp.core.load_firmware_from_file(device_group=device_group,
scpu_fw_path=SCPU_FW_PATH,
ncpu_fw_path=NCPU_FW_PATH)
def show_error_popup(self, message):
error_dialog = QMessageBox.critical(self, "Error", message)
def parse_and_store_devices(self, devices):
for device in devices:
new_device = {
'usb_port_id': device.usb_port_id,
'product_id': device.product_id,
'kn_number': device.kn_number
}
print(device)
existing_device_index = next((index for (index, d) in enumerate(self.connected_devices)
if d['usb_port_id'] == new_device['usb_port_id']), None)
if existing_device_index is not None:
self.connected_devices[existing_device_index] = new_device
else:
self.connected_devices.append(new_device)
def check_available_device(self):
print("checking available devices")
device_descriptors = kp.core.scan_devices()
self.clear_device_layout(self.device_layout)
if device_descriptors.device_descriptor_number > 0:
if device_descriptors.device_descriptor_number > 0:
self.parse_and_store_devices(device_descriptors.device_descriptor_list)
self.display_devices(device_descriptors.device_descriptor_list)
else:
self.show_no_device_gif()
def get_dongle_type(self, product_id):
for dongle_type in self.K_:
if dongle_type.value == product_id:
return dongle_type
return None
def display_devices(self, device_descriptor_list):
hbox_layout = QHBoxLayout()
hbox_layout.setAlignment(Qt.AlignCenter)
for device in device_descriptor_list:
device_layout = QVBoxLayout()
box_layout = QVBoxLayout()
icon = QLabel()
pixmap = QPixmap(UXUI_ASSETS + "kneron_logo.png")
icon.setPixmap(pixmap.scaled(50, 50, Qt.KeepAspectRatio, Qt.SmoothTransformation))
print(device.product_id)
usb_type_label = QLabel(f"Device: {self.get_dongle_type(device.product_id)}")
usb_type_label.setAlignment(Qt.AlignCenter)
usb_type_label.setStyleSheet("color: white")
box_layout.addWidget(icon, alignment=Qt.AlignCenter)
box_layout.addWidget(usb_type_label)
box_widget = QWidget()
box_widget.setLayout(box_layout)
box_size = 200
box_widget.setFixedSize(box_size, box_size)
box_widget.setStyleSheet(DEVICE_BOX_STYLE)
usb_port_label = QLabel(f"KN number:\n{device.kn_number}")
usb_port_label.setAlignment(Qt.AlignLeft)
usb_port_label.setStyleSheet("color: white;")
label_icon_layout = QHBoxLayout()
small_icon = QSvgWidget(UXUI_ASSETS + "./Assets_svg/btn_dialog_device_disconnect_normal.svg")
small_icon.setFixedSize(30, 30)
label_icon_layout.addWidget(usb_port_label)
label_icon_layout.addWidget(small_icon, alignment=Qt.AlignRight)
device_layout.addWidget(box_widget)
device_layout.addLayout(label_icon_layout)
device_widget = QWidget()
device_widget.setLayout(device_layout)
hbox_layout.addWidget(device_widget)
self.device_layout.addLayout(hbox_layout)
def show_no_device_gif(self):
no_device_label = QLabel(self)
no_device_movie = QMovie(NO_DEVICE_GIF)
no_device_label.setMovie(no_device_movie)
no_device_movie.start()
no_device_label.setAlignment(Qt.AlignCenter)
self.device_layout.addWidget(no_device_label)
def show_device_connection_popup(self):
self.popup_window = QDialog(self)
self.popup_window.setWindowTitle("Device Connection")
self.popup_window.setFocusPolicy(Qt.StrongFocus)
popup_width = int(self.width() * POPUP_SIZE_RATIO)
popup_height = int(self.height() * POPUP_SIZE_RATIO)
self.popup_window.setGeometry(100, 100, popup_width, popup_height)
self.popup_window.setStyleSheet(f"background-color: {SECONDARY_COLOR};")
popup_layout = QVBoxLayout()
self.device_layout = QVBoxLayout()
popup_title = QHBoxLayout()
small_icon = QSvgWidget(UXUI_ASSETS + "./Assets_svg/ic_window_device.svg")
small_icon.setFixedSize(30, 30)
popup_title.addWidget(small_icon)
device_popup_label = QLabel("Device Connection", self.popup_window)
device_popup_label.setAlignment(Qt.AlignCenter)
popup_title.addWidget(device_popup_label)
popup_layout.addLayout(self.device_layout)
button_layout = QHBoxLayout()
refresh_button = QPushButton('Refresh')
refresh_button.clicked.connect(self.check_available_device)
refresh_button.setStyleSheet(BUTTON_STYLE)
button_layout.addWidget(refresh_button)
done_button = QPushButton('Done')
done_button.setStyleSheet(BUTTON_STYLE)
#done_button.clicked.connect(self.close_connection_page)
done_button.clicked.connect(lambda: self.close_connection_page())
button_layout.addWidget(done_button)
popup_layout.addLayout(button_layout)
self.popup_window.setLayout(popup_layout)
self.popup_window.setModal(True)
self.setEnabled(False)
self.popup_window.finished.connect(lambda: self.setEnabled(True))
self.popup_window.show()
self.check_available_device()
def show_device_popup_and_main_page(self):
self.show_device_connection_popup()
self.popup_window.finished.connect(self.main_page)
def clear_device_layout(self, layout):
for i in reversed(range(layout.count())):
layout.itemAt(i).widget().deleteLater()
def clear_layout(self):
for i in reversed(range(self.device_layout.count())):
self.device_layout.itemAt(i).widget().deleteLater()
def create_frame(self, title, icon_path):
frame = QFrame(self)
frame.setStyleSheet(f"border: none; background: {SECONDARY_COLOR}; border-radius: 20px;")
layout = QVBoxLayout(frame)
title_layout = QHBoxLayout()
title_icon = QSvgWidget(icon_path)
title_icon.setFixedSize(40, 40)
title_layout.addWidget(title_icon)
title_label = QLabel(title)
title_label.setStyleSheet("color: white;")
title_layout.addWidget(title_label)
layout.addLayout(title_layout)
return frame
def add_model_buttons(self, layout):
for model_name, run_function in self.model_buttons:
button = QPushButton(model_name)
button.clicked.connect(run_function)
button.setStyleSheet("""
QPushButton {
color: white;
border: 2px solid white;
border-radius: 10px;
padding: 10px;
background-color: transparent;
}
QPushButton:hover {
background-color: rgba(255, 255, 255, 50);
}
QPushButton:pressed {
background-color: rgba(255, 255, 255, 100);
}
""")
layout.addWidget(button)
def start_camera(self):
print("opening camera")
self.right_layout.replaceWidget(self.canvas_label, self.video_widget)
self.canvas_label.hide()
self.camera.start()
def stop_camera(self):
self.camera.stop()
self.right_layout.replaceWidget(self.video_widget, self.canvas_label)
self.video_widget.hide()
self.canvas_label.show()
# TODO: implement these functions and add button state/style change
def record_video(self):
output_file = "output_video.mp4"
self.media_recorder.setOutputLocation(QUrl.fromLocalFile(os.path.abspath(output_file)))
self.media_recorder.record()
def stop_recording(self):
self.media_recorder.stop()
def record_audio(self):
audio_output_file = "output_audio.wav"
self.audio_recorder.setOutputLocation(QUrl.fromLocalFile(os.path.abspath(audio_output_file)))
self.audio_recorder.record()
def stop_audio(self):
self.audio_recorder.stop()
def take_screenshot(self):
self.image_capture.capture()
self.image_capture.imageCaptured.connect(self.process_capture)
def process_capture(self, requestId, image):
file_name = f"screenshot_{requestId}.png"
image.save(file_name)
print(f"Screenshot saved as {file_name}")
def run_face_detection(self):
self.start_camera()
print("Running Face Detection")
def run_gender_age_detection(self):
self.start_camera()
print("Running Gender/Age Detection")
def run_object_detection(self):
self.start_camera()
print("Running Object Detection")
def run_mask_detection(self):
self.start_camera()
print("Running Mask Detection")
def choose_folder(self):
self.input_directory = QFileDialog.getExistingDirectory(self, "Select Input Directory")
self.to_keep_directory = QFileDialog.getExistingDirectory(self, "Select 'To Keep' Directory")
self.to_delete_directory = QFileDialog.getExistingDirectory(self, "Select 'To Delete' Directory")
if self.input_directory and self.to_keep_directory and self.to_delete_directory:
print(f"Selected directories:\nInput: {self.input_directory}\nTo Keep: {self.to_keep_directory}\nTo Delete: {self.to_delete_directory}")
def create_folder_button(self):
folder_button_widget = QWidget()
folder_button_layout = QVBoxLayout()
text_label = QLabel("Image")
text_label.setAlignment(Qt.AlignCenter)
text_label.setStyleSheet("color: white;")
folder_button_layout.addWidget(text_label)
folder_button_widget.setLayout(folder_button_layout)
folder_button_widget.setStyleSheet(f"""
QWidget {{
background-color: {SECONDARY_COLOR};
border: 2px solid white;
border-radius: 10px;
padding: 10px;
min-width: 100px;
min-height: 100px;
}}
""")
return folder_button_widget
def process_image_project(self):
print("processing_image_project")
def start_image_project(self):
print("running image project")
self.popup_window = QDialog(self)
self.popup_window.setWindowTitle("Choose Folder")
popup_width = int(self.width() * POPUP_SIZE_RATIO)
popup_height = int(self.height() * POPUP_SIZE_RATIO)
self.popup_window.setGeometry(100, 100, popup_width, popup_height)
self.popup_window.setStyleSheet(f"background-color: {SECONDARY_COLOR};")
popup_layout = QVBoxLayout()
self.device_layout = QVBoxLayout()
cust_label = QLabel("Customization", self.popup_window)
cust_label.setAlignment(Qt.AlignCenter)
cust_label.setStyleSheet("color: white")
popup_layout.addWidget(cust_label)
popup_layout.addLayout(self.device_layout)
folder_icon = QSvgWidget(UXUI_ASSETS + "./Assets_svg/ic_customization_upload_folder.svg")
folder_icon.setFixedSize(100, 100)
upload_icon = QSvgWidget(UXUI_ASSETS + "./Assets_svg/bt_function_upload_normal.svg")
upload_icon.setFixedSize(40, 40)
folder_button_widget = QWidget()
folder_button_layout = QVBoxLayout()
text_label = QLabel("Image")
text_label.setAlignment(Qt.AlignCenter)
text_label.setStyleSheet("color: white; border: none")
folder_button_layout.addWidget(text_label)
folder_button_layout.addWidget(folder_icon)
description_label = QLabel("Upload or drag files")
description_label.setAlignment(Qt.AlignCenter)
description_label.setStyleSheet("color: white; border: none")
folder_button_layout.addWidget(description_label)
folder_button_layout.addWidget(upload_icon)
folder_button_layout.setAlignment(Qt.AlignCenter)
folder_button_widget.setLayout(folder_button_layout)
folder_frame = QFrame()
folder_frame.setStyleSheet(f"""
QFrame {{
border: 2px solid white;
border-radius: 10px;
padding: 10px;
background-color: {SECONDARY_COLOR};
}}
""")
folder_frame.setLayout(QVBoxLayout())
folder_frame.layout().addWidget(folder_button_widget)
folder_icon2 = QSvgWidget(UXUI_ASSETS + "./Assets_svg/ic_customization_upload_folder.svg")
folder_icon2.setFixedSize(100, 100)
upload_icon2 = QSvgWidget(UXUI_ASSETS + "./Assets_svg/bt_function_upload_normal.svg")
upload_icon2.setFixedSize(40, 40)
folder_button_widget2 = QWidget()
folder_button_layout2 = QVBoxLayout()
text_label2 = QLabel("Label")
text_label2.setAlignment(Qt.AlignCenter)
text_label2.setStyleSheet("color: white; border: none")
folder_button_layout2.addWidget(text_label2)
folder_button_layout2.addWidget(folder_icon2)
folder_button_layout2.setAlignment(Qt.AlignCenter)
description_label2 = QLabel("Upload or drag files")
description_label2.setAlignment(Qt.AlignCenter)
description_label2.setStyleSheet("color: white; border: none")
folder_button_layout2.addWidget(description_label2)
folder_button_layout2.addWidget(upload_icon2)
folder_button_widget2.setLayout(folder_button_layout2)
folder_frame2 = QFrame()
folder_frame2.setStyleSheet(f"""
QFrame {{
border: 2px solid white;
border-radius: 10px;
padding: 10px;
background-color: {SECONDARY_COLOR};
}}
""")
folder_frame2.setLayout(QVBoxLayout())
folder_frame2.layout().addWidget(folder_button_widget2)
folder_buttons_layout = QHBoxLayout()
folder_buttons_layout.addWidget(folder_frame)
folder_buttons_layout.addWidget(folder_frame2)
popup_layout.addLayout(folder_buttons_layout)
button_layout = QHBoxLayout()
self.cancel_button = QPushButton('Cancel', self.popup_window)
self.cancel_button.clicked.connect(self.popup_window.close)
self.cancel_button.setStyleSheet(BUTTON_STYLE)
button_layout.addWidget(self.cancel_button)
self.done_button = QPushButton('Done', self.popup_window)
self.done_button.setStyleSheet(BUTTON_STYLE)
self.done_button.clicked.connect(self.process_image_project)
button_layout.addWidget(self.done_button)
popup_layout.addLayout(button_layout)
self.popup_window.setLayout(popup_layout)
self.popup_window.setModal(True)
self.setEnabled(False)
self.popup_window.finished.connect(lambda: self.setEnabled(True))
self.popup_window.show()
def upload_model(self):
print("Uploading Model")
def main_page(self):
self.clear_device_layout(self.layout)
self.setWindowTitle('Innovedus AI Playground')
self.setGeometry(100, 100, *WINDOW_SIZE)
main_layout = QHBoxLayout()
top_nav = QHBoxLayout()
welcome_label = QLabel(self)
welcome_pixmap = QPixmap(UXUI_ASSETS + "kneron_logo.png").scaled(150, 150, Qt.KeepAspectRatio)
welcome_label.setPixmap(welcome_pixmap)
top_nav.addWidget(welcome_label, alignment=Qt.AlignLeft)
top_nav.addStretch()
settings_button = QPushButton("Settings", self)
settings_button.setStyleSheet(BUTTON_STYLE)
top_nav.addWidget(settings_button, alignment=Qt.AlignRight)
self.layout.addLayout(top_nav)
left_widget = QWidget()
left_widget.setLayout(self.left_layout)
left_widget.setFixedWidth(self.geometry().width() // 3)
right_widget = QWidget()
right_widget.setLayout(self.right_layout)
right_widget.setFixedWidth(self.geometry().width() * 2 // 3)
main_layout.addWidget(left_widget)
main_layout.addWidget(right_widget)
self.layout.addLayout(main_layout)
self.setLayout(self.layout)
self.create_device_layout()
self.create_right_layout()
def show_device_details(self):
print("show_device_details")
def create_device_layout(self):
devices_frame = self.create_frame("Device", UXUI_ASSETS + "./Assets_svg/ic_window_device.svg")
devices_frame_layout = QVBoxLayout()
self.device_list = QListWidget(self)
print(self.connected_devices)
for device in self.connected_devices:
usb_port_id = device.get("usb_port_id")
product_id = device.get("product_id")
kn_number = device.get("kn_number")
h_layout = QHBoxLayout()
icon = QSvgWidget(UXUI_ASSETS + "./Assets_svg/ic_window_device.svg")
icon.setFixedSize(40, 40)
h_layout.addWidget(icon)
text_layout = QVBoxLayout()
line1_label = QLabel(f"Dongle: {product_id}")
line1_label.setStyleSheet("font-weight: bold; color: white;")
text_layout.addWidget(line1_label)
line2_label = QLabel(f"KN number: {kn_number}")
line2_label.setStyleSheet("color: white;")
text_layout.addWidget(line2_label)
h_layout.addLayout(text_layout)
item_widget = QWidget()
item_widget.setLayout(h_layout)
list_item = QListWidgetItem()
list_item.setSizeHint(item_widget.sizeHint())
self.device_list.addItem(list_item)
self.device_list.setItemWidget(list_item, item_widget)
devices_frame_layout.addWidget(self.device_list)
detail_button = QPushButton("Details", self)
detail_button.clicked.connect(self.show_device_details)
devices_frame_layout.addWidget(detail_button)
devices_frame.setLayout(devices_frame_layout)
self.left_layout.addWidget(devices_frame)
self.models_frame = self.create_frame("AI Toolbox", UXUI_ASSETS + "./Assets_svg/ic_window_toolbox.svg")
models_layout = QVBoxLayout(self.models_frame)
self.models_frame.setLayout(models_layout)
self.add_model_buttons(self.models_frame.layout())
self.left_layout.addWidget(self.models_frame)
def create_right_layout(self):
self.canvas_label = QLabel("Canvas Area (Camera Screen)", self)
self.canvas_label.setAlignment(Qt.AlignCenter)
self.canvas_label.setStyleSheet("border: 1px transparent; background: gray; border-radius: 20px; ")
self.right_layout.addWidget(self.canvas_label)
button_overlay_layout = QVBoxLayout()
button_overlay_layout.setContentsMargins(0, 0, 0, 0)
self.create_square_buttons(button_overlay_layout)
button_overlay_widget = QWidget(self)
button_overlay_widget.setLayout(button_overlay_layout)
button_overlay_widget.setStyleSheet(f"background: {SECONDARY_COLOR}; border-radius: 20px; padding: 10px;")
button_overlay_widget.setFixedHeight(150)
self.right_layout.addWidget(button_overlay_widget, alignment=Qt.AlignBottom | Qt.AlignRight)
def create_square_buttons(self, layout):
square_buttons_info = [
('video', UXUI_ASSETS + "./Assets_svg/ic_recording_camera.svg"),
('voice', UXUI_ASSETS + "./Assets_svg/ic_recording_voice.svg"),
('screenshot', UXUI_ASSETS + "./Assets_svg/bt_function_screencapture_normal.svg"),
]
for button_name, icon_path in square_buttons_info:
button = QPushButton(self)
button.setFixedSize(50, 50)
button.setStyleSheet(SQUARE_BUTTON_STYLE)
button_layout = QHBoxLayout(button)
button_layout.setContentsMargins(0, 0, 0, 0)
icon = QSvgWidget(icon_path)
icon.setFixedSize(40, 40)
button_layout.addWidget(icon)
layout.addWidget(button)
def upload_model(self):
model_file, _ = QFileDialog.getOpenFileName(self, "Upload Model", "", "NEF Files (*.nef)")
if model_file:
if model_file.endswith('.nef'):
model_name = os.path.basename(model_file)
self.model_buttons.insert(-1, (model_name, self.run_uploaded_model))
print(f"Model uploaded: {model_name}")
self.refresh_model_buttons()
else:
self.show_error_popup("Invalid file format. Please upload a .nef file.")
def refresh_model_buttons(self):
layout = self.models_frame.layout()
for i in reversed(range(layout.count())):
widget_to_remove = layout.itemAt(i).widget()
if widget_to_remove is not None:
widget_to_remove.deleteLater()
self.add_model_buttons(layout)
def run_uploaded_model(self):
print("Running uploaded model")
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())