Manually plot boxes, rather than using `r.plot()`

This commit is contained in:
slashtechno 2023-10-13 17:44:38 -05:00
parent ccf560ab2a
commit 6dfb06989f
Signed by: slashtechno
GPG Key ID: 8EC1D9D9286C2B17
7 changed files with 2482 additions and 2416 deletions

View File

@ -1 +1 @@
3.10.5
3.10.5

28
.vscode/launch.json vendored
View File

@ -1,15 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "src",
"justMyCode": true
}
]
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "src",
"justMyCode": true
}
]
}

4588
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,59 +1,59 @@
[tool.poetry]
name = "set-detect-notify"
version = "0.1.0"
description = "Detect all the things"
authors = ["slashtechno <77907286+slashtechno@users.noreply.github.com>"]
license = "MIT"
readme = "README.md"
packages = [{include = "set-detect-notify"}]
[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
httpx = "^0.25.0"
opencv-python = "^4.8.1.78"
ultralytics = "^8.0.190"
hjson = "^3.1.0"
numpy = "^1.23.2"
# torch = [
# { version = "^2.0.0+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^2.0.0+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
# torchaudio = [
# { version = "^2.0.0+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^2.0.0+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
# torchvision = [
# { version = "^0.15+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^0.15+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
torch = "^2.1.0"
[tool.poetry.group.dev.dependencies]
black = "^23.9.1"
ruff = "^0.0.291"
ipykernel = "^6.25.2"
# [[tool.poetry.source]]
# name = "torch_cpu"
# url = "https://download.pytorch.org/whl/cpu"
# priority = "supplemental"
#
# [[tool.poetry.source]]
# name = "torch_cu118"
# url = "https://download.pytorch.org/whl/cu118"
# priority = "supplemental"
#
# [tool.poetry.extras]
# cuda = []
#
# [[tool.poetry.source]]
# name = "PyPI"
# priority = "primary"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "set-detect-notify"
version = "0.1.0"
description = "Detect all the things"
authors = ["slashtechno <77907286+slashtechno@users.noreply.github.com>"]
license = "MIT"
readme = "README.md"
packages = [{include = "set-detect-notify"}]
[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
httpx = "^0.25.0"
opencv-python = "^4.8.1.78"
ultralytics = "^8.0.190"
hjson = "^3.1.0"
numpy = "^1.23.2"
# torch = [
# { version = "^2.0.0+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^2.0.0+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
# torchaudio = [
# { version = "^2.0.0+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^2.0.0+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
# torchvision = [
# { version = "^0.15+cu118", source = "torch_cu118", markers = "extra=='cuda'" },
# { version = "^0.15+cpu", source = "torch_cpu", markers = "extra!='cuda'" },
# ]
torch = "^2.1.0"
[tool.poetry.group.dev.dependencies]
black = "^23.9.1"
ruff = "^0.0.291"
ipykernel = "^6.25.2"
# [[tool.poetry.source]]
# name = "torch_cpu"
# url = "https://download.pytorch.org/whl/cpu"
# priority = "supplemental"
#
# [[tool.poetry.source]]
# name = "torch_cu118"
# url = "https://download.pytorch.org/whl/cu118"
# priority = "supplemental"
#
# [tool.poetry.extras]
# cuda = []
#
# [[tool.poetry.source]]
# name = "PyPI"
# priority = "primary"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View File

@ -13,6 +13,7 @@ from ultralytics import YOLO
import argparse
from .utils import notify
from .utils import utils
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
args = None
@ -48,13 +49,13 @@ def main():
type=float,
help="The scale to run the detection at, default is 0.25",
)
# argparser.add_argument(
# '--view-scale',
# # Set it to the env VIEW_SCALE if it isn't blank, otherwise set it to 0.75
# default=os.environ['VIEW_SCALE'] if 'VIEW_SCALE' in os.environ and os.environ['VIEW_SCALE'] != '' else 0.75, # noqa: E501
# type=float,
# help="The scale to view the detection at, default is 0.75",
# )
argparser.add_argument(
'--view-scale',
# Set it to the env VIEW_SCALE if it isn't blank, otherwise set it to 0.75
default=os.environ['VIEW_SCALE'] if 'VIEW_SCALE' in os.environ and os.environ['VIEW_SCALE'] != '' else 0.75, # noqa: E501
type=float,
help="The scale to view the detection at, default is 0.75",
)
argparser.add_argument(
"--confidence-threshold",
@ -160,10 +161,12 @@ def main():
# Only process every other frame of video to save time
# Resize frame of video to a smaller size for faster recognition processing
run_frame = cv2.resize(frame, (0, 0), fx=args.run_scale, fy=args.run_scale)
# view_frame = cv2.resize(frame, (0, 0), fx=`args.`view_scale, fy=args.view_scale)
# view_frame = cv2.resize(frame, (0, 0), fx=args.view_scale, fy=args.view_scale)
results = model(run_frame, verbose=False)
for r in results:
for i, r in enumerate(results):
# list of dicts with each dict containing a label, x1, y1, x2, y2
plot_boxes = []
# Setup dictionary of object names
if not object_names:
for name in r.names.values():
@ -174,6 +177,7 @@ def main():
"last_notification_time": None,
}
for box in r.boxes:
# Get the name of the object
class_id = r.names[box.cls[0].item()]
# Get the coordinates of the object
@ -192,6 +196,18 @@ def main():
# If the confidence is less than 0.5, then SKIP!!!!
continue
# Add the object to the list of objects to plot
plot_boxes.append(
{
"label": class_id,
"x1": cords[0],
"y1": cords[1],
"x2": cords[2],
"y2": cords[3],
}
)
# End goal: Send a notification when an object has been detected for 2 seconds in the past 15 seconds.
# However, don't send a notification if the last notification was less than 15 seconds ago
@ -265,13 +281,17 @@ def main():
# Reset the detection duration
print("Just sent a notification - resetting detection duration")
object_names[class_id]["detection_duration"] = 0
im_array = r.plot()
# Scale back up the coordinates of the locations of detected objects.
# im_array = np.multiply(im_array, 1/args.run_scale)
# print(type(im_array))
# print(im_array)
# exit()
cv2.imshow("View", im_array)
frame_to_show = utils.plot_label(
boxes=plot_boxes,
full_frame=frame,
# full_frame=r.plot(),
run_scale=args.run_scale,
view_scale=args.view_scale,
)
# Display the resulting frame
# cv2.imshow("", r)
cv2.imshow(f"Video{i}", frame_to_show)
# Hit 'q' on the keyboard to quit!
if cv2.waitKey(1) & 0xFF == ord("q"):

View File

@ -1,32 +1,32 @@
import datetime
import httpx
def construct_ntfy_headers(
title: str = "Object/Person Detected",
tag="rotating_light", # https://docs.ntfy.sh/publish/#tags-emojis
priority="default", # https://docs.ntfy.sh/publish/#message-priority
) -> dict:
return {"Title": title, "Priority": priority, "Tags": tag}
def send_notification(data: str, headers: dict, url: str):
if url is None or data is None:
raise ValueError("url and data cannot be None")
httpx.post(url, data=data.encode("utf-8"), headers=headers)
def check_last_seen(last_seen: datetime.datetime, seconds: int = 15):
"""
Check if a time is older than a given number of seconds
If it is, return True
If last_seen is empty/null, return True
"""
if (
datetime.datetime.now() - last_seen > datetime.timedelta(seconds=seconds)
or last_seen == ""
or last_seen is None
):
return True
else:
return False
import datetime
import httpx
def construct_ntfy_headers(
title: str = "Object/Person Detected",
tag="rotating_light", # https://docs.ntfy.sh/publish/#tags-emojis
priority="default", # https://docs.ntfy.sh/publish/#message-priority
) -> dict:
return {"Title": title, "Priority": priority, "Tags": tag}
def send_notification(data: str, headers: dict, url: str):
if url is None or data is None:
raise ValueError("url and data cannot be None")
httpx.post(url, data=data.encode("utf-8"), headers=headers)
def check_last_seen(last_seen: datetime.datetime, seconds: int = 15):
"""
Check if a time is older than a given number of seconds
If it is, return True
If last_seen is empty/null, return True
"""
if (
datetime.datetime.now() - last_seen > datetime.timedelta(seconds=seconds)
or last_seen == ""
or last_seen is None
):
return True
else:
return False

View File

@ -0,0 +1,46 @@
import cv2
import numpy as np
def plot_label(
# list of dicts with each dict containing a label, x1, y1, x2, y2
boxes: list = None,
# opencv image
full_frame: np.ndarray = None,
# run_scale is the scale of the image that was used to run the model
# So the coordinates will be scaled up to the view frame size
run_scale: float = None,
# view_scale is the scale of the image, in relation to the full frame
# So the coordinates will be scaled appropriately when coming from run_frame
view_scale: float = None,
font: int = cv2.FONT_HERSHEY_SIMPLEX,
):
view_frame = cv2.resize(full_frame, (0, 0), fx=view_scale, fy=view_scale)
for thing in boxes:
cv2.rectangle(
# Image
view_frame,
# Start point
(int(thing["x1"] * (run_scale/view_scale)), int(thing["y1"] * (run_scale/view_scale))),
# End point
(int(thing["x2"] * (run_scale/view_scale)), int(thing["y2"] * (run_scale/view_scale))),
# Color
(0, 255, 0),
# Thickness
2,
)
cv2.putText(
# Image
view_frame,
# Text
thing["label"],
# Origin
(int(thing["x1"] * (run_scale/view_scale)), int(thing["y1"] * (run_scale/view_scale))),
# Font
font,
# Font Scale
1,
# Color
(0, 255, 0),
# Thickness
1
)
return view_frame