mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Refactor ImageProcessingFaceEntity (#14296)
* Refactor ImageProcessingFaceEntity * Replace STATE_UNKNOWN with None
This commit is contained in:
parent
4d085882d5
commit
b9e893184a
@ -10,6 +10,7 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID, CONF_NAME, CONF_ENTITY_ID)
|
ATTR_ENTITY_ID, CONF_NAME, CONF_ENTITY_ID)
|
||||||
@ -17,6 +18,7 @@ from homeassistant.exceptions import HomeAssistantError
|
|||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
from homeassistant.helpers.entity import Entity
|
from homeassistant.helpers.entity import Entity
|
||||||
from homeassistant.helpers.entity_component import EntityComponent
|
from homeassistant.helpers.entity_component import EntityComponent
|
||||||
|
from homeassistant.util.async_ import run_callback_threadsafe
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -33,7 +35,16 @@ DEVICE_CLASSES = [
|
|||||||
|
|
||||||
SERVICE_SCAN = 'scan'
|
SERVICE_SCAN = 'scan'
|
||||||
|
|
||||||
|
EVENT_DETECT_FACE = 'image_processing.detect_face'
|
||||||
|
|
||||||
|
ATTR_AGE = 'age'
|
||||||
ATTR_CONFIDENCE = 'confidence'
|
ATTR_CONFIDENCE = 'confidence'
|
||||||
|
ATTR_FACES = 'faces'
|
||||||
|
ATTR_GENDER = 'gender'
|
||||||
|
ATTR_GLASSES = 'glasses'
|
||||||
|
ATTR_NAME = 'name'
|
||||||
|
ATTR_MOTION = 'motion'
|
||||||
|
ATTR_TOTAL_FACES = 'total_faces'
|
||||||
|
|
||||||
CONF_SOURCE = 'source'
|
CONF_SOURCE = 'source'
|
||||||
CONF_CONFIDENCE = 'confidence'
|
CONF_CONFIDENCE = 'confidence'
|
||||||
@ -133,3 +144,90 @@ class ImageProcessingEntity(Entity):
|
|||||||
|
|
||||||
# process image data
|
# process image data
|
||||||
yield from self.async_process_image(image.content)
|
yield from self.async_process_image(image.content)
|
||||||
|
|
||||||
|
|
||||||
|
class ImageProcessingFaceEntity(ImageProcessingEntity):
|
||||||
|
"""Base entity class for face image processing."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
"""Initialize base face identify/verify entity."""
|
||||||
|
self.faces = []
|
||||||
|
self.total_faces = 0
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state(self):
|
||||||
|
"""Return the state of the entity."""
|
||||||
|
confidence = 0
|
||||||
|
state = None
|
||||||
|
|
||||||
|
# No confidence support
|
||||||
|
if not self.confidence:
|
||||||
|
return self.total_faces
|
||||||
|
|
||||||
|
# Search high confidence
|
||||||
|
for face in self.faces:
|
||||||
|
if ATTR_CONFIDENCE not in face:
|
||||||
|
continue
|
||||||
|
|
||||||
|
f_co = face[ATTR_CONFIDENCE]
|
||||||
|
if f_co > confidence:
|
||||||
|
confidence = f_co
|
||||||
|
for attr in [ATTR_NAME, ATTR_MOTION]:
|
||||||
|
if attr in face:
|
||||||
|
state = face[attr]
|
||||||
|
break
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_class(self):
|
||||||
|
"""Return the class of this device, from component DEVICE_CLASSES."""
|
||||||
|
return 'face'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def state_attributes(self):
|
||||||
|
"""Return device specific state attributes."""
|
||||||
|
attr = {
|
||||||
|
ATTR_FACES: self.faces,
|
||||||
|
ATTR_TOTAL_FACES: self.total_faces,
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr
|
||||||
|
|
||||||
|
def process_faces(self, faces, total):
|
||||||
|
"""Send event with detected faces and store data."""
|
||||||
|
run_callback_threadsafe(
|
||||||
|
self.hass.loop, self.async_process_faces, faces, total).result()
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_process_faces(self, faces, total):
|
||||||
|
"""Send event with detected faces and store data.
|
||||||
|
|
||||||
|
known are a dict in follow format:
|
||||||
|
[
|
||||||
|
{
|
||||||
|
ATTR_CONFIDENCE: 80,
|
||||||
|
ATTR_NAME: 'Name',
|
||||||
|
ATTR_AGE: 12.0,
|
||||||
|
ATTR_GENDER: 'man',
|
||||||
|
ATTR_MOTION: 'smile',
|
||||||
|
ATTR_GLASSES: 'sunglasses'
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
This method must be run in the event loop.
|
||||||
|
"""
|
||||||
|
# Send events
|
||||||
|
for face in faces:
|
||||||
|
if ATTR_CONFIDENCE in face and self.confidence:
|
||||||
|
if face[ATTR_CONFIDENCE] < self.confidence:
|
||||||
|
continue
|
||||||
|
|
||||||
|
face.update({ATTR_ENTITY_ID: self.entity_id})
|
||||||
|
self.hass.async_add_job(
|
||||||
|
self.hass.bus.async_fire, EVENT_DETECT_FACE, face
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update entity store
|
||||||
|
self.faces = faces
|
||||||
|
self.total_faces = total
|
||||||
|
@ -4,11 +4,12 @@ Support for the demo image processing.
|
|||||||
For more details about this platform, please refer to the documentation at
|
For more details about this platform, please refer to the documentation at
|
||||||
https://home-assistant.io/components/demo/
|
https://home-assistant.io/components/demo/
|
||||||
"""
|
"""
|
||||||
from homeassistant.components.image_processing import ATTR_CONFIDENCE
|
from homeassistant.components.image_processing import (
|
||||||
|
ImageProcessingFaceEntity, ATTR_CONFIDENCE, ATTR_NAME, ATTR_AGE,
|
||||||
|
ATTR_GENDER
|
||||||
|
)
|
||||||
from homeassistant.components.image_processing.openalpr_local import (
|
from homeassistant.components.image_processing.openalpr_local import (
|
||||||
ImageProcessingAlprEntity)
|
ImageProcessingAlprEntity)
|
||||||
from homeassistant.components.image_processing.microsoft_face_identify import (
|
|
||||||
ImageProcessingFaceEntity, ATTR_NAME, ATTR_AGE, ATTR_GENDER)
|
|
||||||
|
|
||||||
|
|
||||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||||
|
@ -11,9 +11,7 @@ from homeassistant.core import split_entity_id
|
|||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from homeassistant.components.image_processing import PLATFORM_SCHEMA # noqa
|
from homeassistant.components.image_processing import PLATFORM_SCHEMA # noqa
|
||||||
from homeassistant.components.image_processing import (
|
from homeassistant.components.image_processing import (
|
||||||
CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
|
ImageProcessingFaceEntity, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
|
||||||
from homeassistant.components.image_processing.microsoft_face_identify import (
|
|
||||||
ImageProcessingFaceEntity)
|
|
||||||
|
|
||||||
REQUIREMENTS = ['face_recognition==1.0.0']
|
REQUIREMENTS = ['face_recognition==1.0.0']
|
||||||
|
|
||||||
|
@ -11,9 +11,8 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.core import split_entity_id
|
from homeassistant.core import split_entity_id
|
||||||
from homeassistant.components.image_processing import (
|
from homeassistant.components.image_processing import (
|
||||||
PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
|
ImageProcessingFaceEntity, PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID,
|
||||||
from homeassistant.components.image_processing.microsoft_face_identify import (
|
CONF_NAME)
|
||||||
ImageProcessingFaceEntity)
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
REQUIREMENTS = ['face_recognition==1.0.0']
|
REQUIREMENTS = ['face_recognition==1.0.0']
|
||||||
|
@ -13,9 +13,8 @@ from homeassistant.core import split_entity_id
|
|||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
|
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
|
||||||
from homeassistant.components.image_processing import (
|
from homeassistant.components.image_processing import (
|
||||||
PLATFORM_SCHEMA, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
|
PLATFORM_SCHEMA, ImageProcessingFaceEntity, ATTR_AGE, ATTR_GENDER,
|
||||||
from homeassistant.components.image_processing.microsoft_face_identify import (
|
ATTR_GLASSES, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME)
|
||||||
ImageProcessingFaceEntity, ATTR_GENDER, ATTR_AGE, ATTR_GLASSES)
|
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
|
||||||
DEPENDENCIES = ['microsoft_face']
|
DEPENDENCIES = ['microsoft_face']
|
||||||
|
@ -9,30 +9,18 @@ import logging
|
|||||||
|
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.core import split_entity_id, callback
|
from homeassistant.core import split_entity_id
|
||||||
from homeassistant.const import STATE_UNKNOWN
|
|
||||||
from homeassistant.exceptions import HomeAssistantError
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
|
from homeassistant.components.microsoft_face import DATA_MICROSOFT_FACE
|
||||||
from homeassistant.components.image_processing import (
|
from homeassistant.components.image_processing import (
|
||||||
PLATFORM_SCHEMA, ImageProcessingEntity, CONF_CONFIDENCE, CONF_SOURCE,
|
PLATFORM_SCHEMA, ImageProcessingFaceEntity, ATTR_NAME,
|
||||||
CONF_ENTITY_ID, CONF_NAME, ATTR_ENTITY_ID, ATTR_CONFIDENCE)
|
CONF_CONFIDENCE, CONF_SOURCE, CONF_ENTITY_ID, CONF_NAME, ATTR_CONFIDENCE)
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util.async_ import run_callback_threadsafe
|
|
||||||
|
|
||||||
DEPENDENCIES = ['microsoft_face']
|
DEPENDENCIES = ['microsoft_face']
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
EVENT_DETECT_FACE = 'image_processing.detect_face'
|
|
||||||
|
|
||||||
ATTR_NAME = 'name'
|
|
||||||
ATTR_TOTAL_FACES = 'total_faces'
|
|
||||||
ATTR_AGE = 'age'
|
|
||||||
ATTR_GENDER = 'gender'
|
|
||||||
ATTR_MOTION = 'motion'
|
|
||||||
ATTR_GLASSES = 'glasses'
|
|
||||||
ATTR_FACES = 'faces'
|
|
||||||
|
|
||||||
CONF_GROUP = 'group'
|
CONF_GROUP = 'group'
|
||||||
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
@ -57,93 +45,6 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
async_add_devices(entities)
|
async_add_devices(entities)
|
||||||
|
|
||||||
|
|
||||||
class ImageProcessingFaceEntity(ImageProcessingEntity):
|
|
||||||
"""Base entity class for face image processing."""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Initialize base face identify/verify entity."""
|
|
||||||
self.faces = []
|
|
||||||
self.total_faces = 0
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state(self):
|
|
||||||
"""Return the state of the entity."""
|
|
||||||
confidence = 0
|
|
||||||
state = STATE_UNKNOWN
|
|
||||||
|
|
||||||
# No confidence support
|
|
||||||
if not self.confidence:
|
|
||||||
return self.total_faces
|
|
||||||
|
|
||||||
# Search high confidence
|
|
||||||
for face in self.faces:
|
|
||||||
if ATTR_CONFIDENCE not in face:
|
|
||||||
continue
|
|
||||||
|
|
||||||
f_co = face[ATTR_CONFIDENCE]
|
|
||||||
if f_co > confidence:
|
|
||||||
confidence = f_co
|
|
||||||
for attr in [ATTR_NAME, ATTR_MOTION]:
|
|
||||||
if attr in face:
|
|
||||||
state = face[attr]
|
|
||||||
break
|
|
||||||
|
|
||||||
return state
|
|
||||||
|
|
||||||
@property
|
|
||||||
def device_class(self):
|
|
||||||
"""Return the class of this device, from component DEVICE_CLASSES."""
|
|
||||||
return 'face'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def state_attributes(self):
|
|
||||||
"""Return device specific state attributes."""
|
|
||||||
attr = {
|
|
||||||
ATTR_FACES: self.faces,
|
|
||||||
ATTR_TOTAL_FACES: self.total_faces,
|
|
||||||
}
|
|
||||||
|
|
||||||
return attr
|
|
||||||
|
|
||||||
def process_faces(self, faces, total):
|
|
||||||
"""Send event with detected faces and store data."""
|
|
||||||
run_callback_threadsafe(
|
|
||||||
self.hass.loop, self.async_process_faces, faces, total).result()
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_process_faces(self, faces, total):
|
|
||||||
"""Send event with detected faces and store data.
|
|
||||||
|
|
||||||
known are a dict in follow format:
|
|
||||||
[
|
|
||||||
{
|
|
||||||
ATTR_CONFIDENCE: 80,
|
|
||||||
ATTR_NAME: 'Name',
|
|
||||||
ATTR_AGE: 12.0,
|
|
||||||
ATTR_GENDER: 'man',
|
|
||||||
ATTR_MOTION: 'smile',
|
|
||||||
ATTR_GLASSES: 'sunglasses'
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
This method must be run in the event loop.
|
|
||||||
"""
|
|
||||||
# Send events
|
|
||||||
for face in faces:
|
|
||||||
if ATTR_CONFIDENCE in face and self.confidence:
|
|
||||||
if face[ATTR_CONFIDENCE] < self.confidence:
|
|
||||||
continue
|
|
||||||
|
|
||||||
face.update({ATTR_ENTITY_ID: self.entity_id})
|
|
||||||
self.hass.async_add_job(
|
|
||||||
self.hass.bus.async_fire, EVENT_DETECT_FACE, face
|
|
||||||
)
|
|
||||||
|
|
||||||
# Update entity store
|
|
||||||
self.faces = faces
|
|
||||||
self.total_faces = total
|
|
||||||
|
|
||||||
|
|
||||||
class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity):
|
class MicrosoftFaceIdentifyEntity(ImageProcessingFaceEntity):
|
||||||
"""Representation of the Microsoft Face API entity for identify."""
|
"""Representation of the Microsoft Face API entity for identify."""
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user