Skip to content
This repository was archived by the owner on Mar 19, 2023. It is now read-only.

Commit df9765f

Browse files
authored
Merge pull request #201 from jodur/master
Add image scale option
2 parents 823f3be + 6245cd1 commit df9765f

File tree

2 files changed

+38
-9
lines changed

2 files changed

+38
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ Configuration variables:
5858
- **roi_x_max**: (optional, default 1), range 0-1, must be more than roi_x_min
5959
- **roi_y_min**: (optional, default 0), range 0-1, must be less than roi_y_max
6060
- **roi_y_max**: (optional, default 1), range 0-1, must be more than roi_y_min
61+
- **scale**: (optional, default 1.0), range 0.1-1.0, scaling factor for the images that are pulled from the camera stream. This results in smaller images files, especially when using hires-cameras.
6162
- **source**: Must be a camera.
6263
- **targets**: The list of target object names and/or `object_type`, default `person`. Optionally a `confidence` can be set for this target, if not the default confidence is used. Note the minimum possible confidence is 10%.
6364

custom_components/deepstack_object/image_processing.py

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
CONF_ROI_X_MIN = "roi_x_min"
7777
CONF_ROI_Y_MAX = "roi_y_max"
7878
CONF_ROI_X_MAX = "roi_x_max"
79+
CONF_SCALE = "scale"
7980
CONF_CUSTOM_MODEL = "custom_model"
8081

8182
DATETIME_FORMAT = "%Y-%m-%d_%H-%M-%S"
@@ -86,6 +87,7 @@
8687
DEFAULT_ROI_Y_MAX = 1.0
8788
DEFAULT_ROI_X_MIN = 0.0
8889
DEFAULT_ROI_X_MAX = 1.0
90+
DEAULT_SCALE = 1.0
8991
DEFAULT_ROI = (
9092
DEFAULT_ROI_Y_MIN,
9193
DEFAULT_ROI_X_MIN,
@@ -127,6 +129,9 @@
127129
vol.Optional(CONF_ROI_X_MIN, default=DEFAULT_ROI_X_MIN): cv.small_float,
128130
vol.Optional(CONF_ROI_Y_MAX, default=DEFAULT_ROI_Y_MAX): cv.small_float,
129131
vol.Optional(CONF_ROI_X_MAX, default=DEFAULT_ROI_X_MAX): cv.small_float,
132+
vol.Optional(CONF_SCALE, default=DEAULT_SCALE): vol.All(
133+
vol.Coerce(float, vol.Range(min=0.1, max=1))
134+
),
130135
vol.Optional(CONF_SAVE_FILE_FOLDER): cv.isdir,
131136
vol.Optional(CONF_SAVE_TIMESTAMPTED_FILE, default=False): cv.boolean,
132137
vol.Optional(CONF_SHOW_BOXES, default=True): cv.boolean,
@@ -223,6 +228,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
223228
roi_x_min=config[CONF_ROI_X_MIN],
224229
roi_y_max=config[CONF_ROI_Y_MAX],
225230
roi_x_max=config[CONF_ROI_X_MAX],
231+
scale=config[CONF_SCALE],
226232
show_boxes=config[CONF_SHOW_BOXES],
227233
save_file_folder=save_file_folder,
228234
save_timestamped_file=config.get(CONF_SAVE_TIMESTAMPTED_FILE),
@@ -234,7 +240,7 @@ def setup_platform(hass, config, add_devices, discovery_info=None):
234240

235241

236242
class ObjectClassifyEntity(ImageProcessingEntity):
237-
"""Perform a face classification."""
243+
"""Perform a object classification."""
238244

239245
def __init__(
240246
self,
@@ -249,6 +255,7 @@ def __init__(
249255
roi_x_min,
250256
roi_y_max,
251257
roi_x_max,
258+
scale,
252259
show_boxes,
253260
save_file_folder,
254261
save_timestamped_file,
@@ -291,19 +298,34 @@ def __init__(
291298
"y_max": roi_y_max,
292299
"x_max": roi_x_max,
293300
}
294-
301+
self._scale = scale
295302
self._show_boxes = show_boxes
296303
self._last_detection = None
297304
self._image_width = None
298305
self._image_height = None
299306
self._save_file_folder = save_file_folder
300307
self._save_timestamped_file = save_timestamped_file
308+
self._image = None
301309

302310
def process_image(self, image):
303311
"""Process an image."""
304-
self._image_width, self._image_height = Image.open(
305-
io.BytesIO(bytearray(image))
306-
).size
312+
self._image = Image.open(io.BytesIO(bytearray(image)))
313+
self._image_width, self._image_height = self._image.size
314+
315+
# resize image if different then default
316+
if self._scale != DEAULT_SCALE:
317+
newsize = (self._image_width * self._scale, self._image_width * self._scale)
318+
self._image.thumbnail(newsize, Image.ANTIALIAS)
319+
self._image_width, self._image_height = self._image.size
320+
with io.BytesIO() as output:
321+
self._image.save(output, format="JPEG")
322+
image = output.getvalue()
323+
_LOGGER.debug(
324+
(
325+
f"Image scaled with : {self._scale} W={self._image_width} H={self._image_height}"
326+
)
327+
)
328+
307329
self._state = None
308330
self._objects = [] # The parsed raw data
309331
self._targets_found = []
@@ -344,7 +366,8 @@ def process_image(self, image):
344366

345367
if self._save_file_folder and self._state > 0:
346368
saved_image_path = self.save_image(
347-
image, self._targets_found, self._save_file_folder,
369+
self._targets_found,
370+
self._save_file_folder,
348371
)
349372

350373
# Fire events
@@ -396,13 +419,13 @@ def device_state_attributes(self) -> Dict:
396419
attr[CONF_SAVE_TIMESTAMPTED_FILE] = self._save_timestamped_file
397420
return attr
398421

399-
def save_image(self, image, targets, directory) -> str:
422+
def save_image(self, targets, directory) -> str:
400423
"""Draws the actual bounding box of the detected objects.
401424
402425
Returns: saved_image_path, which is the path to the saved timestamped file if configured, else the default saved image.
403426
"""
404427
try:
405-
img = Image.open(io.BytesIO(bytearray(image))).convert("RGB")
428+
img = self._image.convert("RGB")
406429
except UnidentifiedImageError:
407430
_LOGGER.warning("Deepstack unable to process image, bad data")
408431
return
@@ -411,7 +434,12 @@ def save_image(self, image, targets, directory) -> str:
411434
roi_tuple = tuple(self._roi_dict.values())
412435
if roi_tuple != DEFAULT_ROI and self._show_boxes:
413436
draw_box(
414-
draw, roi_tuple, img.width, img.height, text="ROI", color=GREEN,
437+
draw,
438+
roi_tuple,
439+
img.width,
440+
img.height,
441+
text="ROI",
442+
color=GREEN,
415443
)
416444

417445
for obj in targets:

0 commit comments

Comments
 (0)