Unified UI and API for processing and training images for facial recognition.
There's a lot of great open source software to perform facial recognition, but each of them behave differently. Double Take was created to abstract the complexities of the detection services and combine them into an easy to use UI and API.
- UI and API bundled into single Docker image
- Ability to password protect UI and API
- Support for multiple detectors
- Train and untrain images for subjects
- Process images from NVRs
- Publish results to MQTT topics
- REST API can be invoked by other applications
- DeepStack v2021.02.1-2021.06.01
- CompreFace v0.5.0-0.5.1
- Facebox
- Frigate v0.8.0-0.9.0
Subscribe to Frigate's MQTT topics and process images for analysis.
mqtt:
host: 192.168.1.1
frigate:
url: http://192.168.1.1:5000
When the frigate/events
topic is updated the API begins to process the snapshot.jpg
and latest.jpg
images from Frigate's API. These images are passed from the API to the configured detector(s) until a match is found that meets the configured requirements. To improve the chances of finding a match, the processing of the images will repeat until the amount of retries is exhausted or a match is found.
When the frigate/+/person/snapshot
topic is updated the API will process that image with the configured detector(s). It is recommended to increase the MQTT snapshot size in the Frigate camera config.
cameras:
front-door:
mqtt:
timestamp: False
bounding_box: False
crop: True
height: 500
If a match is found the image is saved to /.storage/matches/${filename}
.
Trigger automations / notifications when images are processed.
If the MQTT integration is configured within Home Assistant, then sensors will automatically be created.
alias: Notify
trigger:
- platform: state
entity_id: sensor.double_take_david
- platform: state
entity_id: sensor.double_take_unknown
condition:
- condition: template
value_template: '{{ trigger.to_state.state != trigger.from_state.state }}'
action:
- service: notify.mobile_app
data:
message: >-
{{trigger.to_state.attributes.friendly_name}} is near the
{{trigger.to_state.state}} @
{{trigger.to_state.attributes.match.confidence}}% by
{{trigger.to_state.attributes.match.detector}}:{{trigger.to_state.attributes.match.type}}
taking {{trigger.to_state.attributes.attempts}} attempt(s) @
{{trigger.to_state.attributes.duration}} sec
data:
attachment:
url: http://192.168.1.2:3000/api/storage/matches/{{trigger.to_state.attributes.match.filename}}?box=true&token={{trigger.to_state.attributes.token}}
actions:
- action: URI
title: View Image
uri: http://192.168.1.2:3000/api/storage/matches/{{trigger.to_state.attributes.match.filename}}?box=true&token={{trigger.to_state.attributes.token}}
Publish results to double-take/matches/${name}
and double-take/cameras/${camera}
. The number of results will also be published to double-take/cameras/${camera}/person
and will reset back to 0
after 30 seconds.
mqtt:
host: 192.168.1.1
{
"id": "1623906078.684285-5l9hw6",
"duration": 1.26,
"timestamp": "2021-06-17T05:01:36.030Z",
"attempts": 3,
"camera": "living-room",
"zones": [],
"match": {
"name": "david",
"confidence": 66.07,
"match": true,
"box": { "top": 308, "left": 1018, "width": 164, "height": 177 },
"type": "latest",
"duration": 0.28,
"detector": "compreface",
"filename": "2f07d1ad-9252-43fd-9233-2786a36a15a9.jpg"
}
}
{
"id": "ff894ff3-2215-4cea-befa-43fe00898b65",
"duration": 4.25,
"timestamp": "2021-06-17T03:19:55.695Z",
"attempts": 5,
"camera": "back-door",
"zones": [],
"matches": [
{
"name": "david",
"confidence": 100,
"match": true,
"box": { "top": 286, "left": 744, "width": 319, "height": 397 },
"type": "manual",
"duration": 0.8,
"detector": "compreface",
"filename": "4d8a14a9-96c5-4691-979b-0f2325311453.jpg"
}
]
}
notify:
gotify:
url: http://192.168.1.1:8080
token: XXXXXXX
The UI is accessible from http://localhost:3000
.
- Matches:
/
- Train:
/train
- Config:
/config
- Access Tokens:
/tokens
(if authentication is enabled)
Enable authentication to password protect the UI. This is recommended if running Double Take behind a reverse proxy which is exposed to the internet.
auth: true
Documentation can be viewed on Postman.
docker run -d \
--name=double-take \
--restart=unless-stopped \
-p 3000:3000 \
-v ${PWD}/.storage:/.storage \
jakowenko/double-take
version: '3.7'
services:
double-take:
container_name: double-take
image: jakowenko/double-take
restart: unless-stopped
volumes:
- ${PWD}/.storage:/.storage
ports:
- 3000:3000
Configurable options that can be passed by mounting a file at /double-take/config.yml
and is editable via the UI at http://localhost:3000/#/config
. Default values do not need to be specified in configuration unless they need to be overwritten.
mqtt:
host: 192.168.1.1
frigate:
url: http://192.168.1.1:5000
detectors:
compreface:
url: http://192.168.1.1:8000
key: xxx-xxx-xxx-xxx-xxx # key from recognition service in created app
deepstack:
url: http://192.168.1.1:8001
key: xxx-xxx-xxx-xxx-xxx # optional api key
facebox:
url: http://192.168.1.1:8002
Option | Default | Description |
---|---|---|
auth | false |
Add authentication to UI and API |
mqtt.host | MQTT host | |
mqtt.username | MQTT username | |
mqtt.password | MQTT password | |
mqtt.topics.frigate | frigate/events |
MQTT topic for Frigate message subscription |
mqtt.topics.homeassistant | homeassistant |
MQTT topic for Home Assistant Discovery subscription |
mqtt.topics.matches | double-take/matches |
MQTT topic where matches are published |
mqtt.topics.cameras | double-take/cameras |
MQTT topic where matches are published by camera name |
confidence.match | 60 |
Minimum confidence needed to consider a result a match |
confidence.unknown | 40 |
Minimum confidence needed before classifying a match name as unknown |
objects.face.min_area_match | 10000 |
Minimum area in pixels to consider a result a match |
save.matches | true |
Save match images |
save.unknown | true |
Save unknown images |
purge.matches | 168 |
Hours to keep match images until they are deleted |
purge.unknown | 8 |
Hours to keep unknown images until they are deleted |
frigate.url | Base URL for Frigate | |
frigate.attempts.latest | 10 |
Amount of times API will request a Frigate latest.jpg for facial recognition |
frigate.attempts.snapshot | 0 |
Amount of times API will request a Frigate snapshot.jpg for facial recognition |
frigate.image.height | 500 |
Height of Frigate image passed for facial recognition |
frigate.cameras | Only process images from specific cameras | |
frigate.zones | Only process images from specific zones | |
cameras.camera-name.snapshot.topic | Process jpeg encoded topic for facial recognition | |
cameras.camera-name.snapshot.url | Process HTTP image for facial recognition | |
detectors.compreface.url | Base URL for CompreFace API | |
detectors.compreface.key | API Key for CompreFace collection | |
detectors.compreface.det_prob_threshold | 0.8 |
Minimum required confidence that a recognized face is actually a face. Value is between 0.0 and 1.0 |
detectors.compreface.face_plugins | Comma-separated slugs of face plugins | |
detectors.deepstack.url | Base URL for DeepStack API | |
detectors.deepstack.key | API Key for DeepStack | |
detectors.facebox.url | Base URL for Facebox API | |
notify.gotify.url | Base URL for Gotify | |
notify.gotify.token | Gotify application token Gotify | |
notify.gotify.priority | 5 |
Gotify message priority |
notify.gotify.cameras | Only notify from specific cameras | |
notify.gotify.zones | Only notify from specific zones | |
time.format | Defaults to ISO 8601 format with support for token-based formatting | |
time.timezone | UTC |
Time zone used in logs |
If you would like to make a donation to support development, please use GitHub Sponsors.