Skip to content

Commit

Permalink
Major Update - Regions 2.3
Browse files Browse the repository at this point in the history
- 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
hobbitsyfeet committed May 19, 2021
1 parent 7893294 commit 99d4aaa
Show file tree
Hide file tree
Showing 5 changed files with 461 additions and 403 deletions.
153 changes: 153 additions & 0 deletions src/Regions.py
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
232 changes: 232 additions & 0 deletions src/TrackerTab.py
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))
Loading

0 comments on commit 99d4aaa

Please sign in to comment.