-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Overhauled regions to use an id as id rather than name as id. This fixes all overwriting issues and issues with regions disappearing. - Moved Regions from multitracker to its own - Regions generate uuid as id if no id is given - Renamed radius in methods to regions - radius_regions renamed to region_dict for better description of the dictionary data structure - Moved people_tab from input_dialog to TrackerTab - people_tab now has uuid - Retain region now sets to false at default, working with read_only and other_room - Fixed export to print regions properly. Old method failed when duplicate regions were present - Multitracker.update(frame) in multitracker is now fixed, it looks unused but the function would have failed by calling non-existent value of show_frame when only frame was passed into the method and present. WARNING: Duplicate region names are now supported, this is up to user discretion to decide if this is valid or invalid.
- Loading branch information
1 parent
7893294
commit 99d4aaa
Showing
5 changed files
with
461 additions
and
403 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
from PyQt5.QtCore import QCoreApplication | ||
from PyQt5.QtWidgets import (QApplication, QComboBox, QInputDialog, QLineEdit, | ||
QMessageBox, QWidget,QSplashScreen) | ||
|
||
import uuid | ||
|
||
import cv2 | ||
import math | ||
class Regions(QWidget): | ||
|
||
def __init__(self, log = None): | ||
super().__init__() | ||
self.region_dict = dict() | ||
self.log = log #logs the information | ||
|
||
def add_region(self, frame): | ||
""" | ||
Creates an ellipse given a rectangle ROI. | ||
""" | ||
name, okPressed = QInputDialog.getText(self, 'Region', 'Region Name:') | ||
if okPressed and name != '': | ||
if self.log is not None: | ||
self.log(name) | ||
key = uuid.uuid1() | ||
point_x, point_y, width, height = cv2.selectROI("Frame", frame, fromCenter=False, showCrosshair=True) | ||
self.region_dict[key] = (name, point_x, point_y, width, height) | ||
|
||
def add_moving_region(self, name, point, dimensions, id=None): | ||
if name not in [self.region_dict.keys()]: | ||
|
||
if uuid is None: | ||
id = uuid.uuid1() | ||
|
||
self.region_dict[id] = (name, point[0], point[1], dimensions[0], dimensions[1]) | ||
else: | ||
print("Radius already Exists") | ||
|
||
def set_moving_region(self, name, point, dimensions, id): | ||
""" | ||
Creates and sets a region with a given name, and given dimensions | ||
Point (x, y) : (int, int) | ||
Dimensions (width, height), (int, int) | ||
""" | ||
# if id in [self.region_dict.keys()]: | ||
self.region_dict[id] = (name, point[0], point[1], dimensions[0], dimensions[1]) | ||
|
||
def del_region(self): | ||
items = (self.region_dict.values()) | ||
name_list = [] | ||
for item in items: | ||
name_list.append(item[0]) | ||
# items = ("Red","Blue","Green") | ||
item, okPressed = QInputDialog.getItem(self, "Select Region","Delete Regions:", name_list, 0, False) | ||
if okPressed and item: | ||
for index, value in enumerate(items): | ||
print(value) | ||
if item in value: | ||
if self.log is not None: | ||
self.log(str(("Deleting " + str(item)))) | ||
|
||
# key = items.index(index) | ||
# print(items) | ||
key = list(self.region_dict.keys())[index] | ||
print("KEY", key) | ||
del self.region_dict[key] | ||
return | ||
# name, okPressed = QInputDialog.getText(self, 'Region', 'Delete Region Name:') | ||
|
||
def del_moving_region(self, name, id=None): | ||
if id in self.region_dict: | ||
del self.region_dict[id] | ||
# combo_box = QComboBox(self) | ||
# for item in items: | ||
# combo_box.addItem(item) | ||
# combo_box.move(50, 250) | ||
# combo_box.showPopup() | ||
# selected = combo_box.activated[str] | ||
# creating a combo box widget | ||
|
||
|
||
# adding action to the button | ||
# button.pressed.connect(self.action) | ||
|
||
|
||
|
||
# comboBox.activated[str].connect(lambda parameter_list: expression) | ||
# del self.region_dict[selected] | ||
# item, okPressed = QInputDialog.getItem(self.parent, "Get item","Region Name", items, 0, False) | ||
# if okPressed and item: | ||
# input_dialog.log(item) | ||
|
||
def display_region(self, frame): | ||
""" | ||
Displays all region created Radius on given frame | ||
""" | ||
for key, region in self.region_dict.items(): | ||
name = region[0] | ||
x, y, w, h = region[1], region[2], region[3], region[4] | ||
ellipse_center = (int(x + (w/2)) ,int( y + (h/2))) | ||
|
||
frame = cv2.ellipse(frame, ellipse_center, (int((w/2)),(int(h/2))), 0, 0,360, (0,255,0) ) | ||
# cv2.ellipse(frame, box=w/2,color=(0,255,0)) | ||
cv2.rectangle(frame, (x + int(w/2.1) , y - 1), (x + int(w/2.1) + 10 * (len(name)) , y - 15),(255,255,255),-1) | ||
cv2.putText(frame, name, (x + int(w/2.1) , y - 1), cv2.FONT_HERSHEY_PLAIN, 1, (0,0,0),1) | ||
return frame | ||
|
||
def test_region(self, test_point): | ||
""" | ||
Tests weather a point value exists within an eclipse | ||
p <= 1 exists in or on eclipse | ||
Equation: Given p = (x-h)^2/a^2 + (y-k)^2/b^2 | ||
Where test_point is (x,y) and ellipse_center is (h,k), | ||
radius_width, radius_height as a,b respectivly | ||
Returns list(regions), p | ||
""" | ||
#overlapping areas may result in multiple True tests | ||
within_points = [] | ||
test_x = test_point[0] | ||
test_y = test_point[1] | ||
p = float('inf') | ||
for key, region in self.region_dict.items(): | ||
name = region[0] | ||
x, y, w, h = region[1], region[2], region[3], region[4] | ||
|
||
#Invalid inputs are areas with zero or less, return no region and invalid p value | ||
if w <= 0 or h <= 0: | ||
return [], float("inf") | ||
|
||
#handle if devisor == 0 | ||
denom_x = math.pow((w/2), 2) | ||
denom_y = math.pow((h/2), 2) | ||
if denom_x == 0: | ||
denom_x = 1 | ||
elif denom_y == 0: | ||
denom_y = 1 | ||
ellipse_center = (x + (w/2) , y + (h/2)) | ||
|
||
# checking the equation of | ||
# ellipse with the given point | ||
try: | ||
p = ((math.pow((test_x - ellipse_center[0]), 2) / denom_x) + | ||
(math.pow((test_y - ellipse_center[1]), 2) / denom_y)) | ||
except ZeroDivisionError as zerodiverr: | ||
p = float("inf") # Does not count inside the eclipse | ||
|
||
if p <= 1: #point exists in or on eclipse | ||
within_points.append(name) | ||
return within_points, p | ||
|
||
def handle_inputs(): | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
from PyQt5.QtWidgets import QWidget, QInputDialog, QLineEdit, QLabel, QPushButton, QPlainTextEdit, QVBoxLayout, QHBoxLayout, QCheckBox | ||
|
||
from PyQt5.QtGui import QIntValidator, QPixmap | ||
from PyQt5.QtCore import Qt | ||
|
||
import uuid | ||
|
||
class person_tab(): | ||
def __init__(self, window): | ||
self.id = uuid.uuid1() | ||
self.parent = window | ||
self.tab = QWidget() | ||
|
||
|
||
#these are used to record metadata | ||
self.name_line = QLineEdit(window) | ||
self.id_line = QLineEdit(window) | ||
self.group_line = QLineEdit(window) | ||
self.group_line.setValidator(QIntValidator(0,999)) | ||
# self.name_line.textChanged.connect(self.update_tab_name) | ||
self.sex_line = QLineEdit(window) | ||
self.desc_line = QPlainTextEdit(window) | ||
|
||
self.length_tracked = QLabel(window) | ||
self.length_tracked.setText("00:00") | ||
|
||
self.active = True | ||
self.read_only = False | ||
self.other_room = False | ||
self.beginning = False | ||
self.is_region = False | ||
self.is_chair = False | ||
self.init_tab(window) | ||
|
||
def init_tab(self, parent_window): | ||
try: | ||
self.tab.layout = QVBoxLayout(parent_window) | ||
|
||
#Start Name | ||
name_layout = QHBoxLayout() | ||
self.tab.layout.addLayout(name_layout) | ||
|
||
name_btn = QPushButton('Name', parent_window) | ||
name_btn.clicked.connect(lambda: self.getText(input_name="Name:",line=self.name_line)) | ||
|
||
name_layout.addWidget(name_btn) | ||
|
||
name_layout.addWidget(self.name_line) | ||
|
||
|
||
id_btn = QPushButton('ID', parent_window) | ||
id_btn.clicked.connect(lambda: self.getText(input_name="ID:",line=self.id_line)) | ||
|
||
name_layout.addWidget(id_btn) | ||
|
||
name_layout.addWidget(self.id_line) | ||
# #Start Sex | ||
# sex_layout = QHBoxLayout() | ||
|
||
sex_btn = QPushButton('Sex', parent_window) | ||
sex_btn.clicked.connect(self.getChoice) | ||
|
||
name_layout.addWidget(sex_btn) | ||
name_layout.addWidget(self.sex_line) | ||
|
||
group_size_btn = QPushButton('Group Size', parent_window) | ||
group_size_btn.clicked.connect(lambda: self.getInteger()) | ||
|
||
name_layout.addWidget(group_size_btn) | ||
name_layout.addWidget(self.group_line) | ||
|
||
#Start Description | ||
desc_layout = QHBoxLayout() | ||
|
||
desc_btn = QPushButton('Description', parent_window) | ||
desc_btn.clicked.connect(lambda: self.getText(input_name="Description:",line=self.desc_line)) | ||
|
||
desc_layout.addWidget(desc_btn) | ||
desc_layout.addWidget(self.desc_line) | ||
|
||
self.image = QPixmap() | ||
self.tab.layout.addLayout(desc_layout) | ||
|
||
#setup length of time tracked | ||
length_layout = QHBoxLayout() | ||
length_label = QLabel(parent_window) | ||
length_label.setText("Total Tracked (mm:ss): ") | ||
|
||
length_layout.addWidget(length_label) | ||
length_layout.addWidget(self.length_tracked) | ||
length_layout.setAlignment(Qt.AlignCenter) | ||
|
||
# self.active_button = QCheckBox("Active") | ||
# self.active_button.setChecked(True) | ||
# self.active_button.stateChanged.connect(lambda:self.toggle_active()) | ||
# self.active_button.setToolTip("Sets the current tracking to actively record. \nIf unchecked, no box will be processed, displayed or recorded.") | ||
# length_layout.addWidget(self.active_button) | ||
|
||
self.read_only_button = QCheckBox("Read Only") | ||
self.read_only_button.setChecked(False) | ||
self.read_only_button.stateChanged.connect(lambda:self.toggle_read()) | ||
self.read_only_button.setToolTip("Sets the person to read only. \nThis is useful for scrolling through the video without overwriting data.\n Also useful for people exiting the frame") | ||
length_layout.addWidget(self.read_only_button) | ||
|
||
self.other_room_button = QCheckBox("Other Room") | ||
self.other_room_button.setChecked(False) | ||
self.other_room_button.stateChanged.connect(lambda:self.toggle_other_room()) | ||
self.other_room_button.setToolTip("Sets the person to Other Room. \n This is useful for maintaining time without location.") | ||
length_layout.addWidget(self.other_room_button) | ||
|
||
|
||
self.beginning_button = QCheckBox("Beginning") | ||
self.beginning_button.setChecked(False) | ||
self.beginning_button.stateChanged.connect(lambda:self.toggle_beginning()) | ||
self.beginning_button.setToolTip("Sets the 'present at beginning' to be True or False for this person.") | ||
length_layout.addWidget(self.beginning_button) | ||
self.tab.layout.addLayout(length_layout) | ||
|
||
self.is_region_button = QCheckBox("Is Region") | ||
self.is_region_button.setChecked(False) | ||
self.is_region_button.stateChanged.connect(lambda:self.toggle_region()) | ||
length_layout.addWidget(self.is_region_button) | ||
|
||
self.is_chair_button = QCheckBox("Chair") | ||
self.is_chair_button.setChecked(False) | ||
self.is_chair_button.stateChanged.connect(lambda:self.toggle_chair()) | ||
length_layout.addWidget(self.is_chair_button) | ||
|
||
|
||
self.tab.layout.addLayout(length_layout) | ||
|
||
|
||
self.tab.setLayout(self.tab.layout) | ||
|
||
except: | ||
crashlogger.log(str(traceback.format_exc())) | ||
|
||
# #inital load of variables | ||
# self.getText(input_name="Name:",line=self.name_line) | ||
# self.getChoice() | ||
# self.getText(input_name="Description:",line=self.desc_line) | ||
|
||
|
||
|
||
def getInteger(self): | ||
i, okPressed = QInputDialog.getInt(self.parent, "QInputDialog().getInteger()", | ||
"Number:", 1, 0, 999, 1) | ||
self.group_line.setText(str(i)) | ||
if okPressed: | ||
return i | ||
|
||
# def getDouble(self): | ||
# d, okPressed = QInputDialog.getDouble(self.parent, "Get double","Value:", 10.50, 0, 100, 10) | ||
# if okPressed: | ||
# return d | ||
|
||
def getChoice(self): | ||
items = ("Female","Male", "Other") | ||
item, okPressed = QInputDialog.getItem(self.parent, "Get item","Sex:", items, 0, False) | ||
if okPressed and item: | ||
# print(item) | ||
self.sex_line.setText(item) | ||
# return item | ||
|
||
def getText(self, input_name, line): | ||
text, okPressed = QInputDialog.getText(self.parent, "Get text", input_name, QLineEdit.Normal, "") | ||
if okPressed and text != '': | ||
if type(line) == type(QPlainTextEdit()): | ||
line.setPlainText(text) | ||
else: | ||
line.setText(text) | ||
# print(text) | ||
|
||
# return text | ||
|
||
def get_uuid(self): | ||
return self.id | ||
|
||
def get_beginning(self): | ||
return self.beginning | ||
|
||
def get_is_region(self): | ||
return self.is_region | ||
|
||
# def get_region_state(self): | ||
# return self.region_state | ||
|
||
def get_is_chair(self): | ||
return self.is_chair | ||
|
||
def get_read_only(self): | ||
return self.read_only | ||
|
||
def get_other_room(self): | ||
return self.other_room | ||
|
||
def toggle_active(self): | ||
self.parent.log("Setting Active to " + str(not self.active)) | ||
self.active = not self.active | ||
return self.active | ||
|
||
def toggle_read(self): | ||
self.parent.log("Setting Read only to " + str(not self.read_only)) | ||
self.read_only = not self.read_only | ||
return self.read_only | ||
|
||
def toggle_beginning(self): | ||
self.parent.log("Setting person present at beginning to " + str(not self.beginning)) | ||
self.beginning = not self.beginning | ||
return self.beginning | ||
|
||
def toggle_region(self): | ||
# self.region_state = True | ||
self.parent.log("Setting person to a region " + str(not self.is_region)) | ||
self.is_region = not self.is_region | ||
return self.is_region | ||
|
||
def toggle_other_room(self): | ||
self.parent.log("Setting person to other room " + str(not self.other_room)) | ||
self.other_room = not self.other_room | ||
return self.other_room | ||
|
||
def toggle_chair(self): | ||
self.parent.log("Setting person in chair " + str(not self.is_region)) | ||
self.is_chair = not self.is_chair | ||
return self.is_chair | ||
|
||
def update_length_tracked(self, time): | ||
self.length_tracked.setText("00:00") | ||
seconds = round((time)%60,2) | ||
minutes = int(((time)/60)%60) | ||
self.length_tracked.setText( str(minutes) + ":" + str(seconds)) |
Oops, something went wrong.