Skip to content

Commit

Permalink
Added cropping feature to color detection (only covered brightness until
Browse files Browse the repository at this point in the history
now).
Changed "crop_offset_left" to "crop_left".
Changed "crop_offset_top" to "crop_top".
  • Loading branch information
= committed Sep 7, 2023
1 parent d520568 commit 7a4bbcf
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 29 deletions.
9 changes: 6 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
# v0.5.2
Expanded crop functionality to color detection.
Changed "crop_offset_left" to "offset_left".
Changed "crop_offset_top" to "offset_top".

# v0.5.1
Fixed services.yaml

# v0.5
New features:
- Limit detection to an area of the source image.
- Limit brightness detection to an area of the source image.

Configuration:
- crop_offset_left = [0-100] in % width; default = 0
- crop_offset_top = [0-100] in % height; default = 0
- crop_width = [0-100] in % width; default = 0 == full image
- crop_height = [ß-100] in % height; default = 0 == full image

Poor man's lamp positioning? Yes. Very poor.

# v0.4

Released 2023-02-20
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ Additionally, overall brightness can be calculated and applied within adjustable
| brightness_mode | Yes | mean rms natural dominant | mean | Brightness calculation method. `mean` and `rms` use a grayscale image, `natural` uses perceived brightness, `dominant` the same color as for RGB (fastest).
| brightness_min | Yes | Int: 0 to 255 | 2 | Minimal brightness. `< 2` means off for most devices.
| brightness_max | Yes | Int: 0 to 255 | 70 | Maximal brightness, should be `> brightness_min`.
| crop_offset_left | Yes | Int: 0 to 99 | 0 | Crop area: Left offset in % of image width. Default: 0
| crop_offset_top | Yes | Int: 0 to 99 | 0 | Crop area: Top offset in % of image height. Default: 0
| crop_left | Yes | Int: 0 to 99 | 0 | Crop area: Left offset in % of image width. Default: 0
| crop_top | Yes | Int: 0 to 99 | 0 | Crop area: Top offset in % of image height. Default: 0
| crop_width | Yes | Int: 0 to 100 | 0 | Crop area: Width. Default: 0 (= no cropping)
| crop_height | Yes | Int: 0 to 100 | 0 | Crop area: Height. Default: 0 (= no cropping)

Expand Down Expand Up @@ -167,8 +167,8 @@ action:
- light.living_room_tv_left
transition: 0.6
brightness_auto: true
crop_offset_left: 0
crop_offset_top: 0
crop_left: 0
crop_top: 0
crop_width: 50
crop_height: 100
- delay:
Expand All @@ -183,8 +183,8 @@ action:
- light.living_room_tv_right
transition: 0.6
brightness_auto: true
crop_offset_left: 50
crop_offset_top: 0
crop_left: 50
crop_top: 0
crop_width: 50
crop_height: 100
- delay:
Expand All @@ -199,8 +199,8 @@ action:
- light.living_room_ceiling
transition: 0.6
brightness_auto: true
crop_offset_left: 0
crop_offset_top: 0
crop_left: 0
crop_top: 0
crop_width: 100
crop_height: 35
mode: single
Expand Down
38 changes: 25 additions & 13 deletions custom_components/ambient_extractor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from colorthief import ColorThief
import voluptuous as vol
import math
from tempfile import TemporaryFile

from homeassistant.components.light import (
ATTR_RGB_COLOR,
Expand All @@ -28,6 +29,7 @@
ATTR_URL,
DOMAIN,
SERVICE_TURN_ON,

ATTR_BRIGHTNESS_AUTO,
ATTR_BRIGHTNESS_MODE,
ATTR_BRIGHTNESS_MIN,
Expand Down Expand Up @@ -72,7 +74,13 @@ def _get_file(file_path):
return file_path


def _get_color(file_handler) -> tuple:
def _get_color_from_image(im) -> tuple:
file_handler = TemporaryFile()
im.save(file_handler, "PNG")
return _get_color_from_file(file_handler)


def _get_color_from_file(file_handler) -> tuple:
"""Given an image file, extract the predominant color from it."""
color_thief = ColorThief(file_handler)

Expand All @@ -82,13 +90,7 @@ def _get_color(file_handler) -> tuple:
return color


def _get_brightness(file_handler, br_mode, color, crop_area):

# No crop support for "dominant"
if br_mode == "dominant":
r, g, b = color
return (r + g + b) / 3

def _get_cropped_image(file_handler, crop_area):
im = Image.open(file_handler)
if crop_area['active']:
im_width, im_height = im.size
Expand All @@ -98,6 +100,13 @@ def _get_brightness(file_handler, br_mode, color, crop_area):
math.floor(im_width / 100 * (crop_area['x'] + crop_area['w'])),
math.floor(im_width / 100 * (crop_area['y'] + crop_area['h'])),
))
return im


def _get_brightness(im, br_mode, color):
if br_mode == "dominant":
r, g, b = color
return (r + g + b) / 3

if br_mode == "natural":
stat = ImageStat.Stat(im)
Expand Down Expand Up @@ -238,10 +247,12 @@ async def async_extract_color_from_url(url, check_brightness, br_mode, crop_area
_file.name = "ambient_extractor.jpg"
_file.seek(0)

color = _get_color(_file)
im = _get_cropped_image(_file, crop_area)

color = _get_color_from_image(im) if crop_area['active'] else _get_color_from_file(_file)
brightness = 0
if check_brightness:
brightness = _get_brightness(_file, br_mode, color, crop_area)
brightness = _get_brightness(im, br_mode, color)

return {
"color": color,
Expand All @@ -258,12 +269,13 @@ def extract_color_from_path(file_path, check_brightness, br_mode, crop_area):
return None

_LOGGER.debug("Getting predominant RGB from file path '%s'", file_path)

_file = _get_file(file_path)
color = _get_color(_file)
im = _get_cropped_image(_file, crop_area)
color = _get_color_from_image(im) if crop_area['active'] else _get_color_from_file(_file)

brightness = 0
if check_brightness:
brightness = _get_brightness(_file, br_mode, color, crop_area)
brightness = _get_brightness(im, br_mode, color)

return {
"color": color,
Expand Down
4 changes: 2 additions & 2 deletions custom_components/ambient_extractor/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

# Image cropping
# left offset in % of source width
ATTR_CROP_X = "crop_offset_left"
ATTR_CROP_X = "crop_left"
# right offset in % of source height
ATTR_CROP_Y = "crop_offset_top"
ATTR_CROP_Y = "crop_top"
# width in % of source width, 0 means full image
ATTR_CROP_W = "crop_width"
# height in % of source height, 0 means full image
Expand Down
2 changes: 1 addition & 1 deletion custom_components/ambient_extractor/manifest.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "0.5.1",
"version": "0.5.2",
"domain": "ambient_extractor",
"name": "Ambient Extractor",
"config_flow": false,
Expand Down
4 changes: 2 additions & 2 deletions custom_components/ambient_extractor/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ turn_on:
- natural
- dominant

crop_offset_left:
crop_left:
name: Crop image, left offset
description: In % of the original width
example: 0
selector:
number:
min: 0
max: 99
crop_offset_top:
crop_top:
name: Crop image, top offset
description: In % of the original height
example: 0
Expand Down

0 comments on commit 7a4bbcf

Please sign in to comment.