Skip to content

Commit

Permalink
Update image_processing.py with prev
Browse files Browse the repository at this point in the history
  • Loading branch information
vingerha authored Aug 10, 2023
1 parent 6bc03f4 commit 35b3fd8
Showing 1 changed file with 99 additions and 24 deletions.
123 changes: 99 additions & 24 deletions custom_components/deepstack_object/image_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@
CONF_SAVE_TIMESTAMPTED_FILE = "save_timestamped_file"
CONF_ALWAYS_SAVE_LATEST_FILE = "always_save_latest_file"
CONF_SHOW_BOXES = "show_boxes"
CONF_PREV_OBJECTS_IGNORE = "prev_objects_ignore" #giddy - boolean
CONF_PREV_OBJECTS_PCT = "prev_objects_pct" #giddy - 0-100% PERCENTAGE difference to ignore prev objects
CONF_ROI_Y_MIN = "roi_y_min"
CONF_ROI_X_MIN = "roi_x_min"
CONF_ROI_Y_MAX = "roi_y_max"
Expand All @@ -79,10 +81,11 @@
CONF_CUSTOM_MODEL = "custom_model"
CONF_CROP_ROI = "crop_to_roi"

DATETIME_FORMAT = "%Y-%m-%d_%H-%M-%S-%f"
DATETIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
DEFAULT_API_KEY = ""
DEFAULT_TARGETS = [{CONF_TARGET: PERSON}]
DEFAULT_TIMEOUT = 10
DEFAULT_PREV_OBJECTS_PCT = 0.02
DEFAULT_ROI_Y_MIN = 0.0
DEFAULT_ROI_Y_MAX = 1.0
DEFAULT_ROI_X_MIN = 0.0
Expand All @@ -106,7 +109,9 @@

# rgb(red, green, blue)
RED = (255, 0, 0) # For objects within the ROI
GREEN = (0, 255, 0) # For ROI box
GREEN = (0, 255, 0) # For ignored objects previously detected
PURPLE = (170, 0, 255) # For vehicles
SILVER = (192, 192, 192) #giddy - For ROI box
YELLOW = (255, 255, 0) # Unused

TARGETS_SCHEMA = {
Expand Down Expand Up @@ -139,6 +144,8 @@
vol.Optional(CONF_SAVE_TIMESTAMPTED_FILE, default=False): cv.boolean,
vol.Optional(CONF_ALWAYS_SAVE_LATEST_FILE, default=False): cv.boolean,
vol.Optional(CONF_SHOW_BOXES, default=True): cv.boolean,
vol.Optional(CONF_PREV_OBJECTS_IGNORE, default=True): cv.boolean,
vol.Optional(CONF_PREV_OBJECTS_PCT, default=DEFAULT_PREV_OBJECTS_PCT): cv.small_float,
vol.Optional(CONF_CROP_ROI, default=False): cv.boolean,
}
)
Expand Down Expand Up @@ -235,6 +242,8 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
roi_x_max=config[CONF_ROI_X_MAX],
scale=config[CONF_SCALE],
show_boxes=config[CONF_SHOW_BOXES],
prev_objs_ignore=config[CONF_PREV_OBJECTS_IGNORE],
prev_objs_pct=config[CONF_PREV_OBJECTS_PCT],
save_file_folder=save_file_folder,
save_file_format=config[CONF_SAVE_FILE_FORMAT],
save_timestamped_file=config.get(CONF_SAVE_TIMESTAMPTED_FILE),
Expand Down Expand Up @@ -265,6 +274,8 @@ def __init__(
roi_x_max,
scale,
show_boxes,
prev_objs_ignore,
prev_objs_pct,
save_file_folder,
save_file_format,
save_timestamped_file,
Expand Down Expand Up @@ -303,6 +314,12 @@ def __init__(
self._state = None
self._objects = [] # The parsed raw data
self._targets_found = []
self._targets_last = {} # used for storing the previous/last objects
self._targets_last[self._camera] = []
self._targets_latest = {} # used for comparing latest objects
self._targets_latest[self._camera] = []
self._last_detection = None
self._last_filename = None
self._last_detection = None

self._roi_dict = {
Expand All @@ -314,6 +331,8 @@ def __init__(
self._crop_roi = crop_roi
self._scale = scale
self._show_boxes = show_boxes
self._prev_objs_ignore = prev_objs_ignore
self._prev_objs_pct = prev_objs_pct
self._image_width = None
self._image_height = None
self._save_file_folder = save_file_folder
Expand Down Expand Up @@ -373,7 +392,15 @@ def process_image(self, image):

self._objects = get_objects(predictions, self._image_width, self._image_height)
self._targets_found = []

real_targets_found = [] #only real targets excluding ignored targets
latest_targets_cp = {} #make copy of _targets_latest and pop matches, so its faster
if self._targets_latest and self._camera in self._targets_latest:
latest_targets_cp = self._targets_latest[self._camera]
self._targets_last[self._camera] = self._targets_latest[self._camera] #set the last targets to the existing latest

confidence = DEFAULT_CONFIDENCE
ignore_count = 0
target_count = 0
for obj in self._objects:
if not (
(obj["name"] in self._targets_names)
Expand All @@ -392,16 +419,49 @@ def process_image(self, image):
if obj["confidence"] > confidence:
if not self._crop_roi and not object_in_roi(self._roi_dict, obj["centroid"]):
continue
# Ignore target if it was previously detected
ignore = "false"
if self._prev_objs_ignore:
for last in latest_targets_cp:
if obj["name"] == last['name']: #FIXED CRASH here cuz last contains keys, should be last['name']
objBox = obj["bounding_box"]
lasBox = last["bounding_box"]
if (round(abs(objBox["x_min"]-lasBox["x_min"]),5) < self._prev_objs_pct) \
and (round(abs(objBox["x_max"]-lasBox["x_max"]),5) < self._prev_objs_pct) \
and (round(abs(objBox["y_min"]-lasBox["y_min"]),5) < self._prev_objs_pct) \
and (round(abs(objBox["y_max"]-lasBox["y_max"]),5) < self._prev_objs_pct):
ignore = "true"
ignore_count += 1
break
else: continue

obj["ignore"] = ignore
obj["ignoreCount"] = ignore_count
self._targets_found.append(obj)

self._state = len(self._targets_found)
if self._state > 0:
self._last_detection = dt_util.now().strftime(DATETIME_FORMAT)
if ignore == "false":
target_count += 1
real_targets_found.append(obj)

#END for obj in self._objects

targets_found = [
obj["name"] for obj in self._targets_found
real_targets_found = [
obj["name"] for obj in real_targets_found #self._targets_found
] # Just the list of target names, e.g. [car, car, person]
self._summary = dict(Counter(targets_found)) # e.g. {'car':2, 'person':1}
self._summary = dict(Counter(real_targets_found)) # e.g. {'car':2, 'person':1}

target_event_data = {}
self._state = target_count #len(self._targets_found)
if self._state > 0:
# Set last_detection time
self._last_detection = dt_util.now().strftime(DATETIME_FORMAT)
# Fire 1 event of all detected objects, with 'targets_found' and 'summary'
target_event_data[ATTR_ENTITY_ID] = self.entity_id
target_event_data["targets_found"] = real_targets_found
target_event_data["summary"] = self._summary
if saved_image_path:
target_event_data[SAVED_FILE] = saved_image_path
self.hass.bus.fire(EVENT_OBJECT_DETECTED, target_event_data)

if self._save_file_folder:
if self._state > 0 or self._always_save_latest_file:
Expand All @@ -410,13 +470,14 @@ def process_image(self, image):
self._save_file_folder,
)

# Fire events
for target in self._targets_found:
target_event_data = target.copy()
target_event_data[ATTR_ENTITY_ID] = self.entity_id
if saved_image_path:
target_event_data[SAVED_FILE] = saved_image_path
self.hass.bus.fire(EVENT_OBJECT_DETECTED, target_event_data)
# for target in self._targets_found:
# target_event_data = target.copy()
# target_event_data[ATTR_ENTITY_ID] = self.entity_id
# if saved_image_path:
# target_event_data[SAVED_FILE] = saved_image_path
# self.hass.bus.fire(EVENT_OBJECT_DETECTED, target_event_data)

self._targets_latest[self._camera] = self._targets_found #save the targets for next time

@property
def camera_entity(self):
Expand Down Expand Up @@ -448,16 +509,21 @@ def extra_state_attributes(self) -> Dict:
"""Return device specific state attributes."""
attr = {}
attr["targets"] = self._targets
attr["targets_last"] = [
{obj["name"]: obj["confidence"], "bounding_box": obj["bounding_box"]} for obj in self._targets_last[self._camera]
]
attr["targets_found"] = [
{obj["name"]: obj["confidence"]} for obj in self._targets_found
{obj["name"]: obj["confidence"], "bounding_box": obj["bounding_box"], "ignore": obj["ignore"]} for obj in self._targets_found
]
attr["summary"] = self._summary
if self._last_detection:
attr["last_target_detection"] = self._last_detection
if self._last_filename:
attr["last_filename"] = str(self._last_filename) #convert path to str otherwise will get error "Object of type PosixPath is not JSON serializable"
if self._custom_model:
attr["custom_model"] = self._custom_model
attr["all_objects"] = [
{obj["name"]: obj["confidence"]} for obj in self._objects
{obj["name"]: obj["confidence"], "bounding_box": obj["bounding_box"]} for obj in self._objects
]
if self._save_file_folder:
attr[CONF_SAVE_FILE_FOLDER] = str(self._save_file_folder)
Expand Down Expand Up @@ -486,7 +552,7 @@ def save_image(self, targets, directory) -> str:
img.width,
img.height,
text="ROI",
color=GREEN,
color=SILVER,
)

for obj in targets:
Expand All @@ -496,28 +562,33 @@ def save_image(self, targets, directory) -> str:
confidence = obj["confidence"]
box = obj["bounding_box"]
centroid = obj["centroid"]
box_label = f"{name}: {confidence:.1f}%"
box_label = f"{name}: {confidence:.0f}%"
boxColor = PURPLE
if obj["ignore"] == "true":
boxColor = GREEN
elif obj["object_type"]==PERSON:
boxColor = RED

draw_box(
draw,
(box["y_min"], box["x_min"], box["y_max"], box["x_max"]),
img.width,
img.height,
text=box_label,
color=RED,
color=boxColor,
)

# draw bullseye
draw.text(
(centroid["x"] * img.width, centroid["y"] * img.height),
text="X",
fill=RED,
fill=boxColor,
)

# Save images, returning the path of saved image as str
latest_save_path = (
directory
/ f"{get_valid_filename(self._name).lower()}_latest.{self._save_file_format}"
/ f"{get_valid_filename(self._name.replace('deepstack_object_', '')).lower()}_latest.{self._save_file_format}"
)
img.save(latest_save_path)
_LOGGER.info("Deepstack saved file %s", latest_save_path)
Expand All @@ -526,9 +597,13 @@ def save_image(self, targets, directory) -> str:
if self._save_timestamped_file:
timestamp_save_path = (
directory
/ f"{self._name}_{self._last_detection}.{self._save_file_format}"
/ f"{self._name.replace('deepstack_object_', '')}_{self._last_detection}.{self._save_file_format}"
)
img.save(timestamp_save_path)
_LOGGER.info("Deepstack saved file %s", timestamp_save_path)
saved_image_path = timestamp_save_path

# Only update last_filename if state > 0
if (self._state > 0): self._last_filename = saved_image_path

return str(saved_image_path)

0 comments on commit 35b3fd8

Please sign in to comment.