Encourager FUNIX

La consultation du site est totalement libre et gratuite, sans publicités. Les dons sont néanmoins appréciés pour payer l'hébergement et encourager son auteur


Systèmes de vidéosurveillance - présentation de Frigate

Systèmes de vidéosurveillance

Présentation de Frigate et Frigate+

14 avril 2026


Présentation

Cette section a pour objectif de vous présenter différents outils pour monter un système de vidéosurveillance basé sur Linux avec l'utilisation de caméras de surveillance indépendantes (IP ou Wifi), analogique, numérique ou de simples webcams, autrement appelé CCTV. Sont présentés dans cette section Zoneminder, Frigate et motionplus, ils permettent tous d'intégrer un large panel de caméras, de faire de la détection automatique de mouvement et de créer des événements, ils sont basés également sur un serveur web, permettant via un navigateur de les configurer, de visualiser en temps réel les caméras et de gérer les événements, que ce soit sur un poste fixe ou sur un mobile. Zoneminder et Frigate sont bien plus évolués et dispose de fonction d'apprentissage automatique et de reconnaissance d'objet basé sur l'intelligence artificielle, contrairement à motionplus qui est un outil bien plus simple et moins lourd à mettre en œuvre et qui pourra être utilisé pour débuter. On pourra considérer également que ZoneMinder est bien plus usine à gaz que Frigate qui s’intègre dans home assistant.

Cette section est décomposée en plusieurs pages:

Pour ma part j'utilise Frigate avec l'option Frigate+ avec une architecture du système qui ressemble au schéma ci-dessous. Frigate tourne sur un mini PC dédié accessible d'internet avec connexion chiffrée via un certificat Let's Encrypt le tout géré par traefik qui gère également l'authentification, les deux tournent sous l'outil de conteneurisation docker. Localement ce mini PC se nomme Cerbere, il est assisté d'un poste secondaire nommé Ultra qui sert à gérer une caméra bas coût dont l'adresse RTSP fluctue régulièrement et sur lequel tourne également un serveur apache httpd. Cerbere utilise une clé Google Coral TPU pour accélérer l'exploitation des modèles d'apprentissage. Six caméras IP sont connectées au total réparties en extérieur, 4 sont en connexion ethernet POE (alimentation par le câble ethernet), 3 utilisent les fonctions PTZ et sont mobiles. Dans les caméras on trouve un visiophone qui est piloté d'un écran lui même connecté en intérieur en ethernet POE, on trouve également une caméra bas coût connecté via un répéteur WIFI qui se trouve dans un local extérieur électrifié.
Physiquement mon système est réparti sur plusieurs zones :

  • extérieur : localisation des caméras, qui toutes supportent très bien les intempéries et le cagnard du sud.
  • garage : localisation de mon serveur Cerbere et de toutes les arrivées réseau de la maison avec un switch classique sur lequel sont brassés les câbles RJ45 qui desservent les différentes pièces de la maison (et qui ont remplacé les câbles téléphoniques) et un switch POE pour les caméras.
  • mezzanine : localisation de ma box, de mon routeur et de mon autre serveur Ultra.
  • local extérieur : localisation d'un répéteur WIFI pour étendre la portée d'une caméra fixé à l'extérieur de ce local.
  • cuisine : localisation de l'écran de pilotage du visiophone dont la caméra est intégrée au système.

Il y a pléthore de caméras IP sur le marché, les moins chères sont souvent des modèles chinois liées à une application qui fonctionne sous Android et à la sécurité plus que douteuse. Les critères de choix sont :

  • caméras qui génèrent un flux vidéo RTSP avec une adresse fixe et qui vont s'intégrer très facilement à ZoneMinder ou Frigate. Des sites comme ipcamlive ou ispyconnect recensent les caméras par marque ayant une adresse RTSP fixe. Malgré cela, comme je n'avais pas intégré ce critère initialement, j'ai une caméra dont l'adresse RSTP change régulièrement, ce n'est pas insurmontable mais ça complexifie l'installation car il faudra mettre en place des scripts qui vont rendre l'adresse fixe pour ZoneMinder ou Frigate.
  • caméras configurables en se connectant en indiquant leur adresse IP dans un navigateur sans avoir à passer par une application. Pour les caméras bas coût, j'ai dû néanmoins passer par l'application Android pour pouvoir les paramétrer initialement, j'ai supprimé l'application ensuite.
  • paramètres d'administration qui permettent de changer les paramètres réseau pour les connecter à un réseau local, les caméras ne doivent pas être directement accessibles sur internet mais uniquement via Frigate ou ZoneMinder
  • ce n'est pas strictement obligatoire, mais c'est mieux que la caméra puisse se connecter en ethernet POE ça permet d'avoir un seul fil (pas de câble d'alimentation, tout passe par le câble ethernet), sur de longues distances, (mon câble le plus long fait 30m) avec une connexion fiable et de bonne qualité. J'ai néanmoins deux caméras bas coût qui ne sont pas POE et qui sont alimentées avec un câble d’alimentation en plus d'être connectées par ethernet de manière classique, l'une d'entre elle est à plus d'une cinquantaine de mètre, elle est connectée à un répéteur WIFI dont le choix a été délicat car il me fallait un modèle totalement paramétrable pour les paramètres réseau.


Il existe également un autre acteur qui m'a donné beaucoup de fil à retordre, il s'agit du firewall shorewall. Il faut qu'il soit à la fois très restrictif pour interdire tout accès aux caméras directement via internet mais également qu'il puisse ouvrir les routes au juste besoin pour que Frigate soit accessible en sécurité d'un mobile sur internet via traefik, qui assure la connexion chiffrée avec un certificat validé par let's encrypt et puisse communiquer avec les bases de données Frigate+. Et je dois avouer que je n'y serais pas arrivé sans l'aide de gemini enfin du moins pas aussi rapidement.


Installation de Frigate

Frigate repose sur docker qui a été préalablement installé.

On créera le répertoire /etc/frigate et /etc/frigate/config, les enregistrements se feront dans le répertoire /media/frigate. On revient sous /etc/frigate/config dans lequel j'ai créé le fichier docker-compose.yml qui contiendra

services:
  frigate:
    container_name: frigate
    privileged: true # this may not be necessary for all setups
    network_mode: host # j'ai dû rajouter cette ligne pour accéder à ma caméra restreamée avec mediamtx
    restart: unless-stopped

    stop_grace_period: 30s # allow enough time to shut down the various services
    image: ghcr.io/blakeblackshear/frigate:stable
    shm_size: "512mb" # update for your cameras based on calculation above
    devices:
    
- /dev/bus/usb:/dev/bus/usb # ligne pour l'accélérateur USB Google Coral, à commenter sinon
     - /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware

    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/frigate/config:/config
      - /media/frigate:/media/frigate
      - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
        target: /tmp/cache
        tmpfs:
          size: 1000000000
    ports: #section inutile avec le mode réseau host
      - "8971:8971" # port de l'interface navigateur
      #- "8554:8554" # RTSP feeds, j'ai mis en commentaire car conflit avec mediamtx, utile pour renvoyer le flux vidéo vers homeassistant
      - "8555:8555/tcp" # WebRTC over tcp
      - "8555:8555/udp" # WebRTC over udp

[retour haut de la page]

et config.yaml dans lequel on définira les caméras et les objets à détecter (liste par ici https://docs.frigate.video/configuration/objects)

mqtt:
  enabled: false
cameras:
  Camera-sud:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.110
          roles:
            - detect
            - record
      hwaccel_args: preset-vaapi
    objects:
      track:
        - person
        - dog
        - cat
        - bird
 
       - bicycle
        - car
        - motorcycle
    detect:
      width: 1280
      height: 720
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-entree:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.112
          roles:
            - detect
            - record
      hwaccel_args: preset-vaapi
    objects:
      track:
        - person
        - dog
        - cat
        - bird
 
       - bicycle
        - car
        - motorcycle
    detect:
      width: 1280
      height: 720
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-piscine:
    ffmpeg:
      inputs:
        - path: rtsp://frigate:passwd@192.168.2.200:554/ch0_1.264
          roles:
            - detect
            - record
    objects:
      track:
        - person
        - dog
        - cat
        - bird

    detect:
      width: 800
      height: 448
    record:
      enabled: true
      retain:
        days: 7
        mode: all
  Camera-ouest:
    ffmpeg:
      inputs:
        - path: rtsp://ultra.kervao.fr:8554/cam1
          roles:
            - detect
            - record
    objects:
      track:

        - person
        - dog
        - cat
        - bird

    detect:
      width: 640
      height: 360
    record:
      enabled: true
      retain:
        days: 7
        mode: all
detectors:
  cpu1:
    type: cpu
    num_threads: 3
detect:
  enabled: true
version: 0.16-0

si vous avez un accélérateur USB Google Coral, on mettra à la place au niveau de detectors

 coral:
    type: edgetpu
    device: usb

à noter également si vous disposez d'un CPU ou GPU Intel pour l'argument hwaccel_args vous avez le choix entre

  • preset-vaapi
  • preset-intel-qsv-h264 pour les flux H264
  • preset-intel-qsv-h265 pour les flux H265

les deux derniers preset marchent sur les processeurs les plus récents, on peut donc penser qu'ils sont plus efficaces

on tape ensuite dans ce même répertoire

docker compose -f docker-compose.yml up -d

voilà le résultat                                                                                                                                          9.6s

+] Running 10/10
 ✔ frigate 9 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                           79.5s
   ✔ b1badc6e5066 Pull complete                                                                                                                                                   1.2s
   ✔ 4f4fb700ef54 Pull complete                                                                                                                                                   0.3s
   ✔ 14d4601b76c8 Pull complete                                                                                                                                                  14.6s
   ✔ cab46c26a5aa Pull complete                                                                                                                                                   1.1s
   ✔ 6891a7512d41 Pull complete                                                                                                                                                  14.3s
   ✔ b44b002328e4 Pull complete                                                                                                                                                   8.3s
   ✔ 8074b201d282 Pull complete                                                                                                                                                   8.6s
   ✔ d823104cefbd Pull complete                                                                                                                                                   9.0s
   ✔ f86078113133 Pull complete                                                                                                                                                   9.6s
[+] Running 2/2
 ✔ Network config_default  Created                                                                                                                                                0.1s
 ✔ Container frigate       Started

quelques commandes utiles, relancer frigate

docker restart frigate

stopper frigate

docker stop frigate 
[retour haut de la page]

Premier lancement

C'est bon Frigate est installé, lancé et accessible du navigateur localement en tapant

https://ultra.kervao.fr:8971/

pour connaitre le mot de passe il faudra taper

docker logs frigate

et on y voit

2025-09-26 17:46:21.528704726  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.529867665  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.530590965  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Auth is enabled, but no users exist.          ***
2025-09-26 17:46:21.531237207  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Created a default user:                       ***
2025-09-26 17:46:21.542212889  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    User: admin                                   ***
2025-09-26 17:46:21.566794613  [2025-09-26 17:46:21] frigate.app                    INFO    : ***    Password: 2bf8aeb98g16b66f54e70a8e8f121076   ***
2025-09-26 17:46:21.577589942  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************
2025-09-26 17:46:21.598394530  [2025-09-26 17:46:21] frigate.app                    INFO    : ********************************************************

avec un TPU on devrait avoir un message qui ressemble à ça

2025-10-30 07:55:40.442764361  [2025-10-30 07:55:40] frigate.detectors.plugins.edgetpu_tfl INFO    : TPU found
2025-10-30 07:55:40.443899970  INFO: Created TensorFlow Lite XNNPACK delegate for CPU.

cela permet de pouvoir se connecter et voilà l'interface générale avec les derniers évènements en haut et les vues en direct juste en dessous

Avec seulement le TPU, autant dire que c'est inutilisable au quotidien, le programme prend quasiment 100% des ressources constamment.

et avec un TPU, c'est le jour et la nuit !! D'autant qu'il n'est pas tout seul il est aidé par le GPU comme on peut le voir.

[retour haut de la page]

Configuration

Avant d'aller plus loin il est important de connaitre quelques notions à la base du fonctionnement de Frigate, on parle d'alertes et de détections, les alertes sont à la base des détections mais considérées comme des évènements prioritaires à signaler alors que les détections sont des évènements peu significatifs, qui ne méritent pas de notification mais qui sont tout de même enregistrées et visibles dans la revue des évènements (voir la documentation en anglais ici https://docs.frigate.video/configuration/review/).

Passons à la configuration accessible via la roue crantée en bas à gauche de l'écran principal. On obtient cette interface


Dans le premier onglet Interface Utilisateur, il y a des paramètres généraux que je n'ai pas modifiés.

On accède ensuite aux paramétrages des caméras via Paramètres Des Caméras, le menu Gestion permet d'activer ou désactiver les caméras, c'est utile quand une caméra pose problème et pour éviter les messages d'erreurs.

Avant d'aller à Activités on commencera par définir les masques et les zones de détection par via Masques/Zones, pour chaque caméra sélectionnable à partir du bouton bleu en haut à droite on déclare des zones de détection, les autres zones de l'image seront ignorées pour la détection



A noter qu'on peut sélectionner le type d'objet qui peut être détecté et estimer la vitesse du mouvement.



En revanche si on souhaite que seules les personnes et les voitures soient considérées comme des alertes, il faudra modifier le fichier de configuration pour la caméra en question au niveau de la section review en rajoutant une sous section labels comme ceci

    review:
      alerts:
        labels:
          - person
          - car
        required_zones: Zone-det-camera-sud
      detections:
        required_zones: Zone-det-camera-sud

On peut également déclarer des masques de mouvement pour empêcher la détection à des endroits où on risque d'avoir des mouvements indésirables (végétation), ça peut perturber le suivi des objets.



pour que votre voiture garée habituellement à sa place n'apparaisse pas comme un faux positif, il faudra créer un masque d'objet particulier comme ceci


Au niveau de Réglage De La Détection de Mouvement, pour chaque caméra on pourra jouer sur certains paramètres pour affiner la détectionon, j'ai laissé les valeurs par défaut.

On revient maintenant au menu Activités, dans la configuration ci-dessous, seulement les objets Personne et Voiture seront affichées sous forme d'alertes dans la zone prédéfinie et les autres objets détectés seront classés détections dans la même zone.

et ainsi de suite pour toutes les caméras. A noter qu'il faudra relancer Frigate pour que ça soit pris en compte, c'est possible à partir du menu des Paramètres.

Dans le menu Enrichissements, on peut activer (ils sont désactivés par défaut)

  • la recherche sémantique qui permet de retrouver les objets suivis dans votre revue d'évènements en utilisant soit l'image elle-même, soit une description textuelle définie par l'utilisateur, soit une description générée automatiquement.
  • la reconnaissance faciale
  • la reconnaissance des plaques d'immatriculation
  • l'identification des oiseaux

Le menu Utilisateurs comme son nom l'indique permet de gérer les utilisateurs, deux types d'utilisateurs sont possibles les administrateurs accèdent à l'ensemble des fonctionnalités de l'interface, les observateurs sont limités à la consultation des caméras, de la revue d'événements et à l'historique des enregistrements dans l'interface utilisateur. L'onglet Notifications permet d'envoyer des mails pour les alertes.

Pour ce qui concerne les enregistrements, par défaut on a

    record:
      enabled: true
      retain:
        days: 7
        mode: all

c'est à dire qu'alerte comme détection sont enregistrées et conservées 7 jours. Pour une gestion plus fine des enregistrements, voir cette page https://docs.frigate.video/configuration/record/

[retour haut de la page]

Configuration avancée

Régler la détection d'objet

D'après la page https://docs.frigate.video/frigate/camera_setup/#choosing-a-detect-resolution la résolution idéale d'un objet à détecter doit tenir dans un cadre de 320x320 pixels qui correspond au modèle utilisé par Frigate. Si la zone du mouvement est supérieur à 320x320, il va la recadrer et la redimensionner s'il le faut avec éventuellement la perte de détails supplémentaires au passage. Donc des résolutions plus élevées n'améliorent pas significativement la précision de la détection, en revanche elles améliorent les performances si les objets sont très petits dans l'image.
Tout cela est assez contre intuitif car quand je dispose un cadre de 320x320 sur l'image de la caméra qui a une résolution de 1280x720, je me dis que je suis pas prêt de détecter des personnes à cette échelle sur cette caméra, alors qu'une résolution plus grande pourrait permettre la détection d'objets plus petits.



En mettant la résolution maximum de ma caméra à 3072x1728, voilà à quoi correspond une boîte de 320x320 pixels, ce n'est pas la panacée pour la détection mais c'est déjà mieux.


On peut en tirer la règle suivante : caméra avec champ de vision proche (caméra intérieur par exemple) on la réglera avec une faible résolution qui sera suffisante et a contrario pour une caméra avec champ de vision éloigné (caméra extérieure essentiellement) on choisira plutôt une grande résolution pour une meilleure détection des objets.
Je vous enjoins à coller un cadre de 320x320 pixels sur chacune de vos vues à l'échelle pour voir s'il est utile d'augmenter la résolution de la caméra correspondante au regard des objets que vous souhaitez détecter.

Maintenant en cas de faux positif, il est sans doute nécessaire d'ajuster quelques réglages, pour les détails il faudra consulter cette page https://docs.frigate.video/configuration/object_filters. En fait pour chaque objet il faut retenir plusieurs paramètres :
  • score : c'est un paramètre non modifiable calculé à chaque image pour chaque objet détecté
  • min_score : tous les objets détectés dont le score est inférieur à ce paramètre seront ignorées
  • threshold : c'est la moyenne des scores pour un objet détecté sur plusieurs images consécutives

Frigate calcule un score pour chaque objet détecté à chaque image et l'objet est considéré comme positif quand à la fois le score dépasse le min_score et le threshold, c'est à dire la moyenne des scores sur plusieurs images consécutives, ce dernier paramètre permet de confirmer la détection.
Voilà ce qu'on pourra rajouter au fichier config.yaml comme section commune pour l'ensemble des caméras, ce sont des valeurs communément partagées par la communauté des utilisateurs de Frigate.

objects:
  filters:
    dog:
      min_score: .7
      threshold: .9
    cat:
      min_score: .65
      threshold: .8
    person:
      min_score: .7
      threshold: .9
    car:
      min_score: .65
      threshold: .85

Il ne faut pas considérer ces valeurs comme des valeurs idéales, pour ma part avec des caméras extérieures qui ont plutôt tendance à avoir un champ de vision plus lointain, j'ai préféré rajouter une sous section filters à chaque section objects de chacune de mes caméras pour pouvoir ajuster ces valeurs en fonction des caméras, comme ceci

    objects:
      track:
        - person
        - dog
        - cat
        - face
      filters:
        person:
          min_score: .7
          threshold: .8

Pour réduire encore la détection de faux positifs on peut définir une zone minimale et une zone maximale de détection, c'est à dire que si l'objet est trop gros ou trop petit, il sera automatiquement rejeté. Pour un flux avec une résolution de 3072x1728, on peut en extraire une image à la taille et l'ouvrir sous Gimp

Avec l'outil de sélection on trace les boîtes min et max pour détecter une voiture (par exemple), la largeur et la hauteur apparait en bas au milieu Rectangle: 245x252

Le paramètre de boîte de détection minimale est min_area et a pour valeur largeur*hauteur, donc pour l'exemple ci-dessus min_area=245x252=61470, c'est similaire pour le paramètre max_area. Dans la pratique, j'ai mis

pour une voiture

min_area 22000
max_area 230000

pour une personne

min_area 6000
max_area 30000

ce qui donne dans config.yaml

filters:
        car:
          min_score: .7
          threshold: .9
          min_area: 22000
          max_area: 230000
        person:
          min_score: .7
          threshold: .8
          min_area: 6000
          max_area: 3000

Activer la détection des visages

Pour activer la détection des visages, on commencera par rajouter la section correspondante dans le fichier config.yaml

face_recognition:
  enabled: true

et pour les caméras concernées on rajoutera dans la sous section track la mention face

objects:
      track:
        - person
        - dog
        - cat
        - bicycle
        - car
        - motorcycle
        - face

On relance Frigate et les logs renvoient les messages suivantes

2025-11-02 11:20:57.493001613  [2025-11-02 11:20:57] frigate.config.config          WARNING : Camera-entree is configured to track ['face'] objects, which are not supported by the current model.
2025-11-02 11:20:57.495025758  [2025-11-02 11:20:57] frigate.config.config          WARNING : Camera-piscine is configured to track ['face'] objects, which are not supported by the current model.
2025-11-02 11:20:57.519957917  [2025-11-02 11:20:57] frigate.app                    INFO    : Starting Frigate (0.16.1-e664cb2)
2025-11-02 11:20:57.520656637  [2025-11-02 11:20:57] frigate.app                    INFO    : Creating directory: /media/frigate/clips/faces

(...)

2025-11-02 11:20:58.822822712  [2025-11-02 11:20:58] frigate.api.fastapi_app        INFO    : FastAPI started
2025-11-02 11:21:00.117012864  [2025-11-02 11:21:00] frigate.util.downloader        INFO    : Downloading complete: https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/facenet.tflite
2025-11-02 11:21:00.269584095  [2025-11-02 11:21:00] frigate.detectors.plugins.edgetpu_tfl INFO    : TPU found
2025-11-02 11:21:00.270930420  INFO: Created TensorFlow Lite XNNPACK delegate for CPU.
2025-11-02 11:21:03.456767865  [2025-11-02 11:21:03] frigate.util.downloader        INFO    : Downloading complete: https://github.com/NickM-27/facenet-onnx/releases/download/v1.0/landmarkdet.yaml

Une icône Bibliothèque de visages fait son apparition



On rajoute des visages connus en cliquant sur Ajouter un visage, j'ai sélectionné pour commencer deux images de face bien identifiables, une à une





Ensuite on clique sur le bouton Entraîner en haut à gauche et on télécharge des images avec la personne dans différentes situations







il rajoute la photo en zoomant sur le visage s'il a été clairement identifié, il est recommandé de rajouter au total de 20 à 30 images pour que ça soit le plus robuste, c'est un peu laborieux à faire car il faut y aller image par image. Sur ma configuration les images seront placées sous /media/frigate/clips/faces
Et voilà ce que ça va donner quand il identifie les personnes



Maintenant vous pouvez entraîner les modèles en cliquant sur l’icône , on peut confirmer la détection en cliquant sur les personnes qui ont été créées précédemment.




En revanche si Frigate se trompe en affichant un faux positif, on peut les supprimer en les sélectionnant à l'aide la commande CTRL. Puis le bouton en haut à droite Supprimer les visages



Plus d'information par ici https://docs.frigate.video/configuration/face_recognition.

Mettre en place une classification d'objet

Cette fonctionnalité est apparue dans la version 0.17.0, elle permet de créer des sous catégories d'objet, dans l'exemple ci-dessous je vais faire un distinguo entre les chats que mes caméras voient habituellement. Au niveau de la fenêtre principale on cliquera sur l'icône Classification


On créera une sous catégorie d'un objet existant, ici les chats résidents en faisant le distinguo entre les chats



Ensuite il faudra sélectionner les images qui correspondent au chat Plumette et idem pour l'autre chat.



On lance ensuite l'apprentissage du modèle


voilà ce que ce ça donne dans les traces pour une classe voiture

2026-03-15 13:53:54.922614420  [2026-03-15 13:53:54] frigate.util.classification    INFO    : Wrote training metadata for Voitures: 32 images
2026-03-15 13:53:54.922912195  [2026-03-15 13:53:54] frigate.util.classification    INFO    : Finished training Voitures
2026-03-15 13:53:55.004893124  [2026-03-15 13:53:54] frigate.data_processing.real_time.custom_classification INFO    : Successfully loaded updated model for Voitures
2026-03-15 13:53:55.028924711  192.168.2.14 - - [15/Mar/2026:13:53:55 +0100] "GET /api/classification/Voitures/dataset HTTP/1.1" 200 1399 "http://192.168.2.11:8971/classification" "Mozilla/
5.0 (X11; Linux x86_64; rv:148.0) Gecko/20100101 Firefox/148.0" "-" request_time="0.004" upstream_response_time="0.003"

A partir de là les objets seront identifiés par les sous catégories ainsi créées.

Plus d'information par ici https://docs.frigate.video/configuration/custom_classification/object_classification



Mettre en place une classification d'état

Cette fonctionnalité est apparue dans la version 0.17.0, elle permet de distinguer l'état d'un objet particulier, l'ouverture d'une porte, d'un portail. Dans l'exemple je vais considérer l'état ouvert ou fermé du portail. Au niveau de la fenêtre principale on choisit l'icône Classification




Dans mon exemple l'objet à considérer est le portail et l'état ouvert ou fermé




On choisit la caméra et la zone à surveiller pour vérifier l'état d'ouverture du portail



On clique sur Continuer, ensuite en utilisation courante on reclique sur Classification puis États et là on peut voir les images du portail, on les sélectionne pour signifier l'état




on pourra éventuellement créer un nouvel état, pour le portail j'ai créé En mouvement


On peut ensuite entraîner le modèle en cliquant en haut à droite sur Entraîner le modèle.

Plus d'information par ici https://docs.frigate.video/configuration/custom_classification/state_classification


Connecter une caméra orientable PTZ compatible ONVIF

On peut également animer les caméras PTZ via le protocole ONVIF, on pourra utiliser OnvifDeviceManager présenté ici pour détecter comment accéder aux paramètres ONVIF de la caméra. Dans le fichier config.yaml au niveau la caméra concernée on rajoutera une section onvif après la section ffmpeg, comme ceci :

  Camera-est:
    ffmpeg:
      inputs:
        - path: rtsp://user:motdepasse@192.168.2.187:554/11
          roles:
            - detect
            - record
      hwaccel_args: []
  onvif:
      host: 192.168.2.187
      port: 8080
      user: "user"
      password: "motdepasse"

on relance Frigate et les commandes font leur apparition dans l'interface



dans les logs, à chaque commande cela donne

2025-12-14 16:23:19.863412328  [2025-12-14 16:23:19] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_down for Camera-est
2025-12-14 16:23:20.014362034  [2025-12-14 16:23:20] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:24.154372463  [2025-12-14 16:23:24] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:24.324886496  [2025-12-14 16:23:24] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:27.660911995  [2025-12-14 16:23:27] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:27.831550797  [2025-12-14 16:23:27] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:31.340495182  [2025-12-14 16:23:31] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:31.471007427  [2025-12-14 16:23:31] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:35.053131483  [2025-12-14 16:23:35] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:35.203476745  [2025-12-14 16:23:35] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est
2025-12-14 16:23:38.560508556  [2025-12-14 16:23:38] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.move_left for Camera-est
2025-12-14 16:23:38.691835827  [2025-12-14 16:23:38] frigate.comms.dispatcher       INFO    : Setting ptz command to OnvifCommandEnum.stop for Camera-est




Rajouter un certificat Let's Encrypt

En utilisant un serveur httpd Apache

Par défaut Frigate génère un certificat auto signé pour utiliser le port 8971 ce qui génère un avertissement de sécurité dans le navigateur, sous Firefox on peut accepter le risque pour pouvoir y accéder. Mais c'est pas génial d'autant plus si on accède à Frigate d'internet. Dans la configuration qui est présentée ici on utilisera apache et non nginx comme c'est présenté un peu partout. Pour la configuration de Let's Encrypt avec apache on consultera cette page.
Le principe est le suivant

Internet -> HTTPS port 443 -> reverse proxy -> HTTP interne -> Frigate port 8971

Apache fait office reverse proxy, c'est à dire qu'il permet d’accéder à une URL interne en modifiant automatiquement l'URL et en pointant vers le bon port. Mon adresse accessible sur internet est https://adresseperso.ddns.net qui pointe déjà sur une page index.html, cette configuration permet donc d'accéder à frigate à l'adresse https://adresseperso.ddns.net/frigate

Dans le fichier de configuration d'apache httpd.conf, on activera les modules suivants:

LoadModule proxy_module modules/mod_proxy.so 
LoadModule proxy_http_module modules/mod_proxy_http.so

et on rajoutera l'hôte virtuel

<VirtualHost 192.168.2.13:443>
   ServerName adresseperso.ddns.net

   SSLEngine on
   SSLCertificateFile /etc/letsencrypt/live/adresseperso.ddns.net/fullchain.pem
   SSLCertificateKeyFile /etc/letsencrypt/live/adresseperso.ddns.net/privkey.pem

   ProxyPreserveHost On
   ProxyRequests Off

   # Redirection vers Frigate sur port 8971
   ProxyPass /frigate http://127.0.0.1:8971/frigate
   ProxyPassReverse /frigate http://127.0.0.1:8971/frigate

   # Websocket support
   RewriteEngine On
   RewriteCond %{HTTP:Upgrade} =websocket [NC]
   RewriteRule ^/frigate/(.*) ws://127.0.0.1:8971/frigate/$1 [P,L]
</VirtualHost>

on relance apache en tapant systemctl restart httpd

dans le fichier docker-compose.yml on modifiera dans la section environnement le chemin par défaut en rajoutant

environment:
     FRIGATE_BASE_PATH: "/frigate"

Pour prendre en compte les modifications on stoppera Frigate en tapant docker stop frigate  puis

docker compose -f docker-compose.yml up -d

et on relance Frigate en tapant docker start frigate


En utilisant traefik

On va utiliser traefik en tant que Reverse Proxy, ce dernier va gèrer le certificat Let's Encrypt et rediriger le trafic sécurisé vers Frigate. Autre subtilité de configuration, dans ma configuration j'ai 2 serveurs :

- serveur Apache adresse locale 192.168.2.13 accessible via monserveurapache.ddns.net
- serveur frigate adresse locale 192.168.2.11 accessible via monserveurfrigate.ddns.net

traefik va se retrouver sur le serveur frigate et gérera aussi le certificat Let's encrypt et la redirection du trafic sécurisé vers le serveur Apache. Sur le box et/ou le routeur, il faut ouvrir une route vers le serveur traefik avec l'adresse locale 192.168.2.11 vers les ports 80 (pour le renouvellement automatique du certificat) et 443, il faudra également ouvrir les mêmes ports avec shorewall sur le poste traefik.
Il n'est pas nécessaire sur le router d'ouvrir la route vers le port 8971 (connexion avec login/password sur frigate) ou sur le port 5000 (connexion sans login/password avec frigate)

Centralisation : traefik devient le seul point d'entrée. On ouvre les ports 80/443 que sur le PC qui fait tourner docker/traefik.
SSL pour tous :
Le serveur httpd Apache bénéficiera lui aussi de la connexion sécurisée HTTPS (SSL) automatiquement, même s'il ne la gére pas lui-même. Traefik s'occupe de déchiffrer et redirige vers la connexion non sécurisée HTTP au serveur Apache sur le réseau local.
Renouvellement automatique :
Puisque le port 80 pointe vers traefik, Let's Encrypt pourra renouveler les certificats de Frigate et d'Apache sans avoir rien à faire.

En résumé le principe est le suivant pour le relais vers Apache

Internet -> HTTPS port 443 -> reverse proxy -> HTTP interne -> Apache port 80

Pour frigate c'est un peu différent car comme on se connecte sur son port 8971 avec une connexion chiffrée, il faudra que traefik accepte le certificat auto signé de Frigate, cela implique un peu de configuration supplémentaire notamment au niveau du firewall shorewall. Le principe de connexion est le suivant :

Internet -> HTTPS port 443 -> reverse proxy -> HTTPS interne -> Frigate port 8971

En ce qui concerne l'authentication, traefik va gérer le login/password et les transmettra à frigate sans qu'il soit nécessaire à nouveau de les ressaisir via la mire de connexion frigate.

Commençons donc par créer un répertoire /etc/traefik dans lequel on se place et on crée un fichier vide acme.json avec les droits 600, il contiendra les certificats.

touch acme.json
chmod 600 acme.json

ensuite on créera un fichier traefik.yml qui contiendra

api:
 dashboard: true # Active l'interface de contrôle sur le port 8080
 insecure: true  # Permet l'accès direct via le port 8080 sans SSL

# cela permet d'accepter le certicat autosigné de Frigate
serversTransport:
 insecureSkipVerify: true

entryPoints:
 web:
   address: ":80"
   http:
     redirections:
       entryPoint:
         to: websecure
         scheme: https
 websecure:
   address: ":443"

certificatesResolvers:
 myresolver:
   acme:
     email: olivier.hoarau@funix.org
     storage: acme.json
     httpChallenge:
       entryPoint: web

providers:
 docker:
   exposedByDefault: false # Sécurité : n'expose que les conteneurs avec le label 'enable=true'
 file:
   filename: dynamic_conf.yml # Ajoute la prise en charge du serveur Apache
   watch: true

au même endroit on crée maintenant un fichier dynamic_conf.yml qui contiendra

http:
 # 1. TRANSPORTS (Pour ignorer les erreurs de certificat auto-signé de Frigate)
 serversTransports:
   ignore-ssl:
     insecureSkipVerify: true

 # 2. ROUTERS (Configuration  pour Apache)
 routers:
   to-apache:
     rule: "Host(`monserveurapache.ddns.net`)"
     service: apache-service
     entryPoints:
       - websecure
     tls:
       certresolver: myresolver

 # 3. SERVICES (Cibles réseau pour apache)
 services:
   apache-service:
     loadBalancer:
       servers:
         - url: "http://192.168.2.13:80"

 # 4. MIDDLEWARES (Centralisation de l'auth pour Frigate)
 middlewares:
   frigate-auth:
     basicAuth:
       # liste des utilisateurs avec htpasswd
       users:
         - "frigateadmin:password-hash"
         - "frigateuser:password-hash"

   frigate-header:
     headers:
       customRequestHeaders:
         # Traefik v3 utilise cette syntaxe pour injecter l'utilisateur authentifié
         x-forwarded-user: '{{ printf "{{.RemoteUser}}" }}'
         x-forwarded-groups: "admin"

On créera le mot de passe en tapant

docker run --rm httpd:alpine htpasswd -nb frigateuser motdepasse

cela doit donner directement quelque chose comme frigateuser:password-hash et il suffira de faire un copier coller dans le fichier ci-dessus. La commande ci-dessous doit donner un résultat identique

echo $(/usr/local/apache2/bin/htpasswd -nB useradmin) | sed -e 's/\$/\$\$/g'

On va créer maintenant docker-compose.yml qui contiendra

services:
 traefik:
   image: traefik:v3.0
   command:
     - "--providers.docker=true"
     - "--serversTransport.insecureSkipVerify=true"
   container_name: traefik
   restart: always
   ports:
     - "80:80"
     - "443:443"
     - "8080:8080" # Dashboard (à protéger en production)
   volumes:
     - /var/run/docker.sock:/var/run/docker.sock:ro
     - ./traefik.yml:/traefik.yml:ro
     - ./dynamic_conf.yml:/dynamic_conf.yml:ro
     - ./acme.json:/acme.json
   networks:
     - proxy

networks:
 proxy:
   external: true


On modifie maintenant le fichier docker de configuration de frigate /etc/frigate/config/docker-compose.yml le voici en entier avec les lignes mises en commentaire

services:
 frigate:
   container_name: frigate
   privileged: true # this may not be necessary for all setups
   #network_mode: host     
   restart: unless-stopped
   stop_grace_period: 30s # allow enough time to shut down the various services
   image: ghcr.io/blakeblackshear/frigate:0.17.0
   shm_size: "540mb" # update for your cameras based on calculation above
   devices:
     - /dev/bus/usb:/dev/bus/usb # Passes the USB Coral, needs to be modified for other versions
     - /dev/dri/renderD128:/dev/dri/renderD128 # For intel hwaccel, needs to be updated for your hardware
   volumes:
     - /etc/localtime:/etc/localtime:ro
     - /etc/frigate/config:/config
     - /media/frigate:/media/frigate
     - type: tmpfs # Optional: 1GB of memory, reduces SSD/SD Card wear
       target: /tmp/cache
       tmpfs:
         size: 1000000000
   ports:
     - "8971:8971"
     #- "5000:5000" # Internal unauthenticated access. Expose carefully.
     - "8554:8554" # RTSP feeds
     - "8555:8555/tcp" # WebRTC over tcp
     - "8555:8555/udp" # WebRTC over udp
   environment:
     #- FRIGATE_BASE_PATH="/frigate"
     - PLUS_API_KEY=clé-frigate-plus
     - FRIGATE_AUTH_METHOD=proxy
     - FRIGATE_PROXY_HEADER=Remote-User
     - FRIGATE_TRUSTED_PROXIES=172.18.0.0/16
   networks:
     - proxy # Doit être sur le même réseau que Traefik
   labels:
     - "traefik.enable=true"
     - "traefik.http.routers.frigate.rule=Host(`monserveurfrigate.ddns.net`)"
     - "traefik.http.routers.frigate.entrypoints=websecure"
     - "traefik.http.routers.frigate.tls.certresolver=myresolver"

     # Appelle les middlewares définis dans le fichier (d'où le @file)
     - "traefik.http.routers.frigate.middlewares=frigate-auth@file,frigate-header@file"

     # Configuration du service vers le port 8971
     - "traefik.http.services.frigate-service.loadbalancer.server.port=8971"
     - "traefik.http.services.frigate-service.loadbalancer.server.scheme=https"

     # UTILISATION DU TRANSPORT DEFINI DANS LE FICHIER DYNAMIQUE
     - "traefik.http.services.frigate-service.loadbalancer.serverstransport=ignore-ssl@file"

networks:
 proxy:
   external: true

dans le fichier /etc/frigate/config/config.yaml on aura bien

tls:
 enabled: true
auth:
 enabled: true
proxy:
 header_map:
   user: x-forwarded-user
   role: x-forwarded-groups

Avant de lancer traefik et relancer frigate il faudra créer le réseau docker avec le nom proxy en tapant

docker network create proxy

cela va donner le numéro d'identification du réseau proxy avec  une longue suite de chiffres et de lettres accolés entre eux. J'ai ensuite simplifié la configuration d'apache en supprimant toutes les VirtualHost en lien avec mon adresse monserveurapache.ddns.net ou les redirections automatiques du port 80 vers 443. Comme traefik va maintenant envoyer les requêtes à Apache en HTTP (port 80) sur votre réseau local, je l'ai remis en écoute sur le port 80 par défaut.

Passons maintenant à la configuration de shorewall sous /etc/shorewall tout d'abord fichier shorewall.conf, il faut veiller à avoir

DOCKER=Yes

DOCKER_BRIDGE=docker0

IP_FORWARDING=Keep

ROUTE_FILTER=No

Fichier interfaces

#
# Shorewall -- /etc/shorewall/interfaces
#
# For information about entries in this file, type "man shorewall-interfaces"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-interfaces.html
#
?FORMAT 2 
-       enp1s0      proxyarp,dhcp,nosmurfs,routefilter

# On utilise 'physical' pour éviter que Shorewall ne cherche le broadcast
dock     docker0          physical=docker+,routeback=1
dock     br               physical=br-+,routeback=1

fichier zones

#
# Shorewall -- /etc/shorewall/zones
#
# For information about this file, type "man shorewall-zones"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-zones.html
#
###############################################################################
#ZONE           TYPE            OPTIONS         IN_OPTIONS      OUT_OPTIONS
fw    firewall
loc   ipv4
net   ipv4
dock  ipv4

fichier snat

#
# Shorewall -- /etc/shorewall/snat
#
# For information about entries in this file, type "man shorewall-snat"
#
# See https://shorewall.org/manpages/shorewall-snat.html for more information
#
?FORMAT 2
###################################################################################################################################################
#ACTION                 SOURCE                  DEST            PROTO   DPORT   SPORT   IPSEC   MARK    USER    SWITCH  ORIGDEST        PROBABILITY
MASQUERADE  172.18.0.0/16       enp1s0
MASQUERADE  172.17.0.0/16       enp1s0

fichier policy

#
# Shorewall -- /etc/shorewall/policy
#
# For information about entries in this file, type "man shorewall-policy"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-policy.html
#
###############################################################################
$FW        dock      ACCEPT
dock       $FW       ACCEPT
dock       dock      ACCEPT
dock       net       ACCEPT

fw      all     ACCEPT
loc     all     ACCEPT
all     all     REJECT



Fichier rules

#
# Shorewall -- /etc/shorewall/rules
#
# For information on the settings in this file, type "man shorewall-rules"
#
# The manpage is also online at
# https://shorewall.org/manpages/shorewall-rules.html
#
##############################################################################################################################################################
#ACTION         SOURCE          DEST            PROTO   DPORT   SPORT   ORIGDEST        RATE    USER    MARK    CONNLIMIT       TIME    HEADERS SWITCH  HELPER
#INCLUDE        rules.drakx

# ==============================================================================
# 1. FLUX DOCKER (FRIGATE / TRAEFIK INTERNAL)
# ==============================================================================
# Communication entre l'hôte et les conteneurs (Port Frigate+)
ACCEPT    $FW                dock:172.18.0.0/16    tcp    8971
ACCEPT    dock:172.18.0.0/16 $FW                   tcp    8971

# Sortie DNS pour les conteneurs (indispensable pour Frigate+)
ACCEPT    dock:172.18.0.0/16 net                   udp    53
ACCEPT    dock:172.18.0.0/16 net                   tcp    53

# Sortie HTTPS pour les conteneurs (Upload photos Frigate+)
ACCEPT    dock:172.18.0.0/16 net                   tcp    443

# ==============================================================================
# 2. ACCÈS TRAEFIK (HTTP/HTTPS)
# ==============================================================================
# On autorise le Web depuis Internet ET depuis le réseau local
# Note : On utilise $FW car 192.168.2.11 est l'IP de cette machine.
ACCEPT    net                $FW                   tcp    80,443
ACCEPT    loc                $FW                   tcp    80,443

# Ports spécifiques (WebRTC / Go2RTC)
ACCEPT    net                $FW                   tcp    8555
ACCEPT    net                $FW                   udp    8555
ACCEPT    loc                $FW                   tcp    8555
ACCEPT    loc                $FW                   udp    8555

# Redirection du trafic Web vers le conteneur Traefik
DNAT    net    dock:172.18.0.2    tcp    80,443
DNAT    loc    dock:172.18.0.2    tcp    80,443

# ==============================================================================
# 3. ACCÈS LOCAL & ADMINISTRATION (SSH, MQTT, FRIGATE UI)
# ==============================================================================
# Autorise ton LAN à administrer le serveur
ACCEPT    loc                $FW                   tcp    22,1883,5000,8080,8971,8554
ACCEPT    loc                $FW                   udp    554,2049,8000
ACCEPT    loc                $FW                   icmp

# ==============================================================================
# 4. SORTIE SERVEUR (PING & MAJ)
# ==============================================================================
ACCEPT    $FW                net                   icmp
ACCEPT    $FW                net                   tcp    80,443
ACCEPT    $FW                net                   udp    53

# ==============================================================================
# 5. FLUX DOCKER VERS LAN (POUR APACHE EXTERNE)
# ==============================================================================
# Autorise Traefik (zone dock) à joindre Apache (zone loc)
ACCEPT    dock:172.18.0.0/16    loc:192.168.2.13    tcp    80

# ==============================================================================
# 6. FLUX FRIGATE -> CAMÉRAS (RÉSEAU LOCAL)
# ==============================================================================
# Autorise Frigate à lire les flux RTSP (vidéo) et HTTP (commandes/snapshot)
ACCEPT    dock:172.18.0.0/16    loc    tcp    554,80,81,8000,8554
ACCEPT    dock:172.18.0.0/16    loc    udp    554,8000-8010


On relance shorewall

Maintenant on va lancer traefik, sous /etc/traefik on tapera

docker compose up -d

et on relance la configuration de frigate en tapant

docker stop frigate
cd /etc/frigate/config
docker compose down
docker compose up -d

pour voir si ça fonctionne on pourra taper docker network inspect proxy  voilà le résultat

[
   {
       "Name": "proxy",
       "Id": "id-reseau-proxy",
       "Created": "2026-03-21T07:40:45.423464906+01:00",
       "Scope": "local",
       "Driver": "bridge",
       "EnableIPv4": true,
       "EnableIPv6": false,
       "IPAM": {
           "Driver": "default",
           "Options": {},
           "Config": [
               {
                   "Subnet": "172.18.0.0/16",
                   "Gateway": "172.18.0.1"
               }
           ]
       },
       "Internal": false,
       "Attachable": false,
       "Ingress": false,
       "ConfigFrom": {
           "Network": ""
       },
       "ConfigOnly": false,
       "Containers": {
           "1935c6946444a4be193c3fc3cf89ad4e00d76b0dace34c032266c72a36789caf": {
               "Name": "frigate",
               "EndpointID": "e3630a1c73245a57c88048edbb1c9e49dee86d925ff8a735c42827b1b4959823",
               "MacAddress": "06:fa:01:8a:b1:5a",
               "IPv4Address": "172.18.0.2/16",
               "IPv6Address": ""
           },
           "564a20b201a1adf544020be253b267f5472c3cceec0cdc31f208b4f02089141e": {
               "Name": "traefik",
               "EndpointID": "671f4968920ec3497c8bbfe8855c99e10626907159abc374bf5d0be8ea555bbd",
               "MacAddress": "46:2e:a8:5b:bc:ec",
               "IPv4Address": "172.18.0.3/16",
               "IPv6Address": ""
           }
       },
       "Options": {},
       "Labels": {}
   }
]

On voit bien les instances traefik et frigate sont bien sur le même réseau proxy.

On pourra aller maintenant sur http://192.168.2.11:8080 pour voir le tableau de bord de traefik, on pourra vérifier si les routes sont actives et les certificats valides.




En cliquant sur Explore au niveau de Routers, on peut voir à la fois frigate et le serveur httpd apache avec chacun un certificat valide.



J'ai gardé le port ouvert 8080 sur le réseau local mais il est non accessible d'internet.
Au besoin visualiser le fichier acme.json vous devriez retrouver vos certificats et on pourra afficher les logs de traefik en tapant

docker logs -f traefik



Faire une mise à jour

Pour faire une mise à jour on commence par stopper frigate en tapant

docker stop frigate

on édite maintenant le fichier docker-compose.yml et au niveau de image on indique la dernière version stable

services:
 frigate:
  (...)
   image: ghcr.io/blakeblackshear/frigate:0.17.0

On prend en compte la modification en tapant

docker compose up -d

voilà ce que ça donne

[+] Running 11/11
✔ frigate 10 layers [⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿]      0B/0B      Pulled                                                                                                                              160.9s  
  ✔ 4831516dd0cb Pull complete                                                                                                                                                         1.9s  
  ✔ 4f4fb700ef54 Pull complete                                                                                                                                                         1.2s  
  ✔ cb7d423543ee Pull complete                                                                                                                                                        19.8s  
  ✔ d2f5da7ad830 Pull complete                                                                                                                                                         2.6s  
  ✔ 7e04514a5073 Pull complete                                                                                                                                                        32.6s  
  ✔ 34300798769b Pull complete                                                                                                                                                         5.7s  
  ✔ 2ad10e3dbd66 Pull complete                                                                                                                                                        18.0s  
  ✔ 848dd1cbc10e Pull complete                                                                                                                                                        18.5s  
  ✔ 8226412a03ca Pull complete                                                                                                                                                        19.0s  
  ✔ b22355f413a7 Pull complete                                                                                                                                                        19.8s  
[+] Running 2/2
✔ Container frigate                                                  Started                                                                                                          12.1s  
! frigate Published ports are discarded when using host network mode                                                                                                                   0.0s

et on peut voir dans les logs que c'est bien pris en compte en tapant

docker logs frigate

on peut voir
 
2026-02-27 18:03:04.431938998  [2026-02-27 18:03:04] frigate.app                    INFO    : Starting Frigate (0.17.0-f0d69f7)

Si vous avez l'erreur

[2026-02-27 18:03:15] frigate.api.auth               ERROR   : Error parsing jwt: bad_signature:

il faudra stopper frigate, supprimer le fichier /etc/frigate/config/.jwt_secret et vider le cache du navigateur qui accède à frigate et on relance frigate.

Pour l'erreur suivante

2026-02-27 18:03:15.508446035  Fatal Python error: Illegal instruction
2026-02-27 18:03:15.508451526
2026-02-27 18:03:15.508494078  Thread 0x00007f2970ce66c0 (most recent call first):
2026-02-27 18:03:15.508587864    File "/usr/lib/python3.11/threading.py", line 320 in wait
2026-02-27 18:03:15.508683237    File "/usr/lib/python3.11/queue.py", line 171 in get
2026-02-27 18:03:15.508775665    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 162 in loop
2026-02-27 18:03:15.508860665    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 137 in run
2026-02-27 18:03:15.508965544    File "/usr/local/lib/python3.11/dist-packages/playhouse/sqliteq.py", line 272 in run
2026-02-27 18:03:15.509051339    File "/usr/lib/python3.11/threading.py", line 975 in run
2026-02-27 18:03:15.509143141    File "/usr/lib/python3.11/threading.py", line 1038 in _bootstrap_inner
2026-02-27 18:03:15.509234196    File "/usr/lib/python3.11/threading.py", line 995 in _bootstrap
2026-02-27 18:03:15.509249695
2026-02-27 18:03:15.509286238  Current thread 0x00007f299b08a040 (most recent call first):
2026-02-27 18:03:15.509368220    File "<frozen importlib._bootstrap>", line 241 in _call_with_frames_removed
2026-02-27 18:03:15.509501615    File "<frozen importlib._bootstrap_external>", line 1233 in create_module
2026-02-27 18:03:15.509584820    File "<frozen importlib._bootstrap>", line 573 in module_from_spec

(...)
 
2026-02-27 18:03:15.514956630    File "/usr/lib/python3.11/multiprocessing/spawn.py", line 133 in _main
2026-02-27 18:03:15.514958723    File "/usr/lib/python3.11/multiprocessing/forkserver.py", line 313 in _serve_one
2026-02-27 18:03:15.514960962    File "/usr/lib/python3.11/multiprocessing/forkserver.py", line 274 in main
2026-02-27 18:03:15.514962618    File "<string>", line 1 in <module>
                                                                                             
Dans Frigate 0.17, les nouvelles fonctionnalités d'embeddings (recherche sémantique, reconnaissance faciale, etc.) utilisent des modèles IA plus complexes via TensorFlow et Jina. Ces bibliothèques sont compilées pour des processeurs modernes supportant les instructions AVX/AVX2. Pour savoir si votre processeur dispose de ces instructions il faut taper

grep -o 'avx[^ ]*' /proc/cpuinfo

s'il y a rien qui sort c'est que le processeur est trop ancien, la désactivation de la recherche sémantique suffit à résoudre le problème dans le fichier config.yaml.

semantic_search:
  enabled: false
J'ai fait plutôt le choix de basculer sur un mini PC avec un N150 qui comprend les instructions AVX. Plus d'info sur la migration par là https://docs.frigate.video/frigate/updating/



[retour haut de la page]

Utilisation

Utilisation classique

Revenons maintenant à l'interface principale, on a vu plus haut que la vue principale renvoie vers les vues des caméras, elle est accessible en cliquant sur l'icône représentant une caméra sur la gauche.

En cliquant à gauche sur la Revue des évènements, on voit s'afficher tous les derniers évènements (alertes, détections, mouvement). L'objet détecté apparait en icône dans le coin en haut à gauche de l'image, on accède au replay en cliquant sur l'image. On acte la revue en cliquant en bas sur Marquer ces éléments comme passés en revue



Quand on clique sur la petite icône en haut à gauche qui symbolise l'objet détecté, on tombe sur les détails de l'évènement, à droite on retrouve une frise temporelle avec les derniers évènements de la journée.
on peut aller encore plus loin en cliquant sur l'image de l'objet détecté, on tombe sur une fenêtre qui permet de visualiser la vidéo avec des détails succincts sur la détection.




Quand on clique sur les ... à droite de l'objet détecté on tombe sur ce menu pour aller plus loin



Pour afficher la cinématique de l'objet on cliquera sur Afficher le parcours de l'objet dans l'image ci-dessous la trajectoire peut paraitre bizarre car la caméra est PTZ et s'est orientée vers l'objet pour le suivre automatiquement



Le menu Explorer à gauche permet d'afficher tous les objets détectés qu'on peut filtrer avec les outils en haut à droite



Quand on clique sur une image de détection on peut visualiser la cinématique de l'objet de manière plus détaillée avec les paramètres de détection qui pourront servir à affiner la configuration de frigate.


Vous pouvez les supprimer un à un à l'aide du menu contextuel, ou par paquet avec un CTRL-A puis le bouton Supprimer en haut à droite

On a vu plus haut qu'on pouvait accéder à un état des ressources du système via le menu Paramètres->Système->Métriques du système, il y a également un onglet Stockage qui donne un état des enregistrements



Et l'onglet Caméras qui donne un état des ressources utilisées par chaque caméra, même si on ne voit pas le TPU


Pour le reste,  je vous renvoie à la documentation de frigate pour l'utilisation avec quelques URL intéressantes

https://docs.frigate.video/guides/getting_started
https://community.jeedom.com/t/tuto-integrer-frigate-et-faire-de-la-reconnaissance-video-dobjet-par-ia-en-local-dans-son-jeedom/117108
https://www.simplehomelab.com/frigate-docker-guide/



Visualiser Frigate d'un mobile

Pour ce qui concerne la visualisation à distance sur un mobile, il existe bien une application Android non officielle, mais j'en vois pas trop l'intérêt car de base en accédant directement à l'adresse (dès lors qu'on l'a rendu accessible sur internet) à partir d'un navigateur du mobile, c'est plutôt convivial comme on peut le voir ci-dessous


 [Retour haut de la page]


Notifications avec Signal via MQTT

Présentation

Frigate intègre déjà un service de notification par mail comme évoqué plus haut qui ne marche pas chez moi, mais peu importe ce paragraphe explique recevoir les notifications d'alerte avec Signal en utilisant le service MQTT. MQTT (Message Queuing Telemetry Transport) est un protocole de messagerie qui fonctionne sur le principe de souscription / publication, c'est à dire qu'un serveur (ou broker) peut publier des notifications sur un canal et des clients peuvent soucrire pour visualiser les notifications. Pour ce qui concerne Frigate, en activant MQTT, on pourra lancer un script qui déclenche une action à la réception d'un évènement comme une alerte, il est possible également que le broker MQTT soit accessible d'internet et une application cliente MQTT android peut être installé pour être prévenu des alertes.

Installation de Mosquitto

Le site officiel https://mosquitto.org, sur ma mageia je me suis contenté de l'installer en tapant

urpmi mosquitto

on crée un utilisateur avec son mot de passe

mosquitto_passwd -c /etc/mosquitto/passwd  olivier
Password:  
Reenter password:

dans le fichier /etc/mosquitto/mosquitto.conf on décommente et modifie les lignes suivantes

allow_anonymous false

password_file /etc/mosquitto/passwd

listener 1883 0.0.0.0


on change le propriétaire du fichier passwd

chown mosquitto:mosquitto /etc/mosquitto/passwd

on peut lancer mosquitto

systemctl start mosquitto

et voilà ce que donne la commande systemctl status mosquitto

mosquitto.service - Mosquitto MQTT Broker
    Loaded: loaded (/usr/lib/systemd/system/mosquitto.service; disabled; preset: disabled)
    Active: active (running) since Sun 2025-12-07 09:14:56 CET; 2min 12s ago
      Docs: man:mosquitto.conf(5)
            man:mosquitto(8)
   Process: 1426932 ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426933 ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426934 ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto (code=exited, status=0/SUCCESS)
   Process: 1426935 ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto (code=exited, status=0/SUCCESS)
  Main PID: 1426936 (mosquitto)
     Tasks: 1 (limit: 18901)
    Memory: 1004.0K
       CPU: 43ms
    CGroup: /system.slice/mosquitto.service
            └─1426936 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

déc. 07 09:14:56 ultra.kervao.fr systemd[1]: Starting mosquitto.service...
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: mosquitto version 2.0.21 starting
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Config loaded from /etc/mosquitto/mosquitto.conf.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Starting in local only mode. Connections will only be possible from clients running on this machine.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Create a configuration file which defines a listener to allow remote access.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: For more details see https://mosquitto.org/documentation/authentication-methods/
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Opening ipv4 listen socket on port 1883.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: Opening ipv6 listen socket on port 1883.
déc. 07 09:14:56 ultra.kervao.fr mosquitto[1426936]: 1765095296: mosquitto version 2.0.21 running
déc. 07 09:14:56 ultra.kervao.fr systemd[1]: Started mosquitto.service.

on n'oublie pas d'ouvrir le port TCP 1883 utilisé par MQTT via shorewall

Pour voir si ça fonctionne bien on peut lancer un client qui va attendre une notification d'un broker

mosquitto_sub -v -t 'test/topic' -u olivier -P motdepasse

Maintenant on va lancer un broker en publiant une notification

mosquitto_pub -u olivier -P motdepasse -t 'test/topic' -m 'helloWorld'

et du client on va voir s'afficher

test/topic helloWorld

A noter que le broker tourne sur le serveur où se trouve Frigate, l'ensemble tourne en local et je n'ai pas besoin qu'il soit accessible d'internet.

Configuration de Frigate et scripts de gestion des alertes

Maintenant dans le fichier de configuration de Frigate config.yaml, on va activer MQTT

mqtt:
 enabled: true
 host: localhost
 user: olivier
 password: motdepasse
 port: 1883

on va créer le script d'écoute /usr/local/bin/alerte_frigate qui va envoyer un mail dès qu'il y a une alerte, on verra plus loin pour Signal.

#!/bin/bash
mosquitto_sub -t "frigate/events" -u olivier -P motdepasse | while read -r payload
do
   #à décommenter pour visualiser pour des tests de fonctionnement
   #echo "Rx MQTT: ${payload}"
   #envoi d'un mail pour chaque nouveau évènement
   if echo "${payload}" | grep -q '"type": "new"'; then
       echo "${payload}" | mail -s "Frigate alerte" olivier
   fi
done

le mail contiendra

{"before": {"id": "1765103169.376268-4ybyyv", "camera": "Camera-sud", "frame_time": 1765103169.376268, "snapshot": null, "label": "car", "sub_label": null, "top_score": 0.0, "false_positive": true, "start_time": 1765103169.376268, "end_time": null, "score": 0.9765625, "box": [1876, 1001, 2407, 1285], "area": 150804, "ratio": 1.869718309859155, "region": [1356, 0, 3072, 1716], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null}, "after": {"id": "1765103169.376268-4ybyyv", "camera": "Camera-sud", "frame_time": 1765103169.59021, "snapshot": {"frame_time": 1765103169.376268, "box": [1876, 1001, 2407, 1285], "area": 150804, "region": [1356, 0, 3072, 1716], "score": 0.9765625, "attributes": [], "current_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null, "recognized_license_plate_score": null}, "label": "car", "sub_label": null, "top_score": 0.9765625, "false_positive": false, "start_time": 1765103169.376268, "end_time": null, "score": 0.9765625, "box": [1872, 1003, 2411, 1287], "area": 153076, "ratio": 1.897887323943662, "region": [1356, 0, 3072, 1716], "active": true, "stationary": false, "motionless_count": 1, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [[[0.6969, 0.7448], 1765103169.59021]], "recognized_license_plate": null}, "type": "new"}

le script va filtrer les nouvelles alertes uniquement, en revanche il prendra aussi bien les alertes que les détections, il n'existe pas de champs qui les distinguent.

Voilà un script plus évolué (merci ChatGPT !!) qui permettra d'envoyer un mail d'alerte uniquement pour les objets person et car en y joignant la photo instantanée avec le contour autour de l'objet. Il nécessitera l'installation du package jq qui permettra d'extraire plus facilement des informations utiles.

#!/bin/bash
# /usr/local/bin/alerte_frigate
# Attendre que le clip soit complètement écrit (taille stable) avant envoi

#paramètres de connexion MQTT"
MQTT_USER="olivier"
MQTT_PASS="motdepasse"
MQTT_TOPIC="frigate/events"

#paramètre de connexion API FRIGATE

FRIGATE_USER="loginfrigate"
FRIGATE_PASS="motdepasse"
FRIGATE_API="https://192.168.2.11:8971"
COOKIE_FILE="/tmp/frigate_cookies.txt"

CLIP_DIR="/media/frigate/clips"

MAIL_FROM="webmaster@funix.org"
MAIL_TO="olivier.hoarau@funix.org"

THROTTLE_SECONDS=40        # délai minimum entre deux alertes par caméra
THROTTLE_DIR="/var/tmp/frigate_throttle"
mkdir -p "$THROTTLE_DIR"

SIGNAL_USER="+336XXXXXXXX"       # ton numéro Signal
SIGNAL_DEST="+336YYYYYYYY"       # numéro qui reçoit les alertes

LOGGER_TAG="alerte_frigate"

### --- LOG FUNCTION ---
log()     { logger -t "$LOGGER_TAG" "[INFO] $*"; }
log_warn(){ logger -t "$LOGGER_TAG" "[WARN] $*"; }
log_dbg() { logger -t "$LOGGER_TAG" "[DEBUG] $*"; }

log "Démarrage du service."

# Paramètres d'attente pour la stabilité du fichier
WAIT_MAX_SECS=20       # temps max d'attente pour que le fichier soit complet
CHECK_STEP=0.5         # intervalle entre deux vérifs de taille (sec)
STABLE_ROUNDS=2        # nombre de mesures identiques successives pour considérer "stable"

# Fonction qui permet une alerte max par caméra toutes les THROTTLE_SECONDS secondes
should_throttle() {
   local cam="$1"
   local now
   now=$(date +%s)
   local file="$THROTTLE_DIR/$cam.last"

   # Si jamais aucun envoi encore
   if [ ! -f "$file" ]; then
       echo "$now" > "$file"
       return 1    # PAS de throttle
   fi

   local last
   last=$(cat "$file")

   local delta=$((now - last))

   if [ "$delta" -lt "$THROTTLE_SECONDS" ]; then
       # throttle actif
       return 0
   fi

   # assez de temps écoulé, mise à jour
   echo "$now" > "$file"
   return 1
}

# Premier login au lancement du script
#login_frigate

mosquitto_sub -t "$MQTT_TOPIC" -u "$MQTT_USER" -P "$MQTT_PASS" | while read -r payload
do
   # Ne traiter que les "new"
   if echo "$payload" | jq -e . >/dev/null 2>&1; then
       if ! echo "$payload" | grep -q '"type"[[:space:]]*:[[:space:]]*"new"'; then
           continue
       fi
   else
       continue
   fi

   # Extraire infos
   camera=$(echo "$payload" | jq -r '.after.camera // .camera // empty')
   label=$(echo "$payload" | jq -r '.after.label // "inconnu"')
   id=$(echo "$payload" | jq -r '.after.id // .id // empty')

   # THROTTLE PAR CAMERA
   if should_throttle "${camera}"; then
       log_dbg "Throttle actif : alerte ignorée sur ${camera} (moins de $THROTTLE_SECONDS secondes)"
       continue
   fi

   # Filtrer uniquement les labels car et person
   if [[ "${label}" != "car" && "${label}" != "person" ]]; then
       log_dbg "Événement ignoré pour label=${label}"
       continue
   fi

   if [ -z "$id" ] || [ -z "${camera}" ]; then
       log_warn "Événement MQTT invalide, ignoré."
       continue
   fi

   EVENT_ID="$id"
   log "Détection : caméra=${camera}, label=${label}, id=${EVENT_ID}"
   log_dbg "Recherche du fichier annoté…"
    
   CLIP_FILE=""
   MAX_WAIT=20   # secondes
   WAIT=0  
   while [ $WAIT -lt $MAX_WAIT ]; do
       CLIP_FILE=$(find "$CLIP_DIR" -type f -iname "*${EVENT_ID}*.jpg" | head -n 1)

       if [ -n "$CLIP_FILE" ]; then
                   log_dbg "Fichier trouvé après ${WAIT}s : $CLIP_FILE"
               break
       fi

       sleep 1
       WAIT=$((WAIT+1))
   done

   if [ -z "$CLIP_FILE" ]; then
          log_warn "Aucun snapshot annoté trouvé pour $EVENT_ID après ${MAX_WAIT}s."
   else
          log "[INFO] Clip annoté trouvé:  $CLIP_FILE"
   fi

   ATTACH=""
   # Si clip trouvé, attendre la stabilisation de la taille
   if [[ -n "$CLIP_FILE" && -f "$CLIP_FILE" ]]; then
       log "Clip candidate trouvé: $CLIP_FILE"
       elapsed=0
       stable_count=0
       last_size=-1

       while (( $(echo "$elapsed < $WAIT_MAX_SECS" | bc -l) )); do
           if [[ -f "$CLIP_FILE" ]]; then
               size=$(stat -c %s "$CLIP_FILE" 2>/dev/null || echo 0)
           else
               size=0
           fi

           if [[ "$size" -eq "$last_size" && "$size" -gt 0 ]]; then
               stable_count=$((stable_count+1))
           else
               stable_count=0
           fi

           # Si on a STABLE_ROUNDS mesures identiques => on le considère complet
           if (( stable_count >= STABLE_ROUNDS )); then
               log "Fichier stable (taille=$size). Prêt à être envoyé."
               ATTACH="$CLIP_FILE"
               break
           fi

           last_size=$size
           sleep "$CHECK_STEP"
           elapsed=$(echo "$elapsed + $CHECK_STEP" | bc -l)
       done

       # si pas stable au bout du timeout, on considère quand même (mais on peut fallback)
       if [[ -z "$ATTACH" ]]; then
           log_warn "Fichier non stable après ${WAIT_MAX_SECS}s (taille actuelle=${last_size}). Tentative fallback API."
       fi
   else
       log_warn "Aucun clip annoté trouvé localement pour ${EVENT_ID}."
   fi

   # Si pas d'attach encore, tenter de récupérer la snapshot via API
   if [[ -z "$ATTACH" ]]; then
       TMPFILE="/tmp/frigate_snapshot_${EVENT_ID//[^a-zA-Z0-9._-]/_}.jpg"
       rm -f "$TMPFILE"

       # Récupération via API (retry court car la snapshot peut être générée juste après l'event)
       got=0
       retries=10
       sleep_between=0.5
       #connexion sécurisée à l'API FRIGATE en passant par la création d'un cookie
       curl -k -c ${COOKIE_FILE} -X POST -d "user=${FRIGATE_USER}&password=${FRIGATE_PASS}" "${FRIGATE_API}/api/login"
       for ((i=0;i<retries;i++)); do
           curl -k -c ${COOKIE_FILE} -s -f "${FRIGATE_API}/api/events/${EVENT_ID}/snapshot.jpg" -o "$TMPFILE"
           if [[ $? -eq 0 && -s "$TMPFILE" ]]; then
               log "Snapshot récupérée via API -> $TMPFILE"
               ATTACH="$TMPFILE"
               got=1
               break
           fi
           sleep "$sleep_between"
       done

       if [[ $got -ne 1 ]]; then
           log_warn "API snapshot échouée ou introuvable pour ${EVENT_ID}."
       fi
   fi
    
   ### --- MAIL HTML ---
   subject="Frigate : détection de ${label} sur ${camera}"
   body="Nouvelle détection Frigate.\n\nCaméra : ${camera}\nObjet  : ${label}\nID     : ${EVENT_ID}\n\nPayload brut :\n${payload}"

   if [[ -n "$ATTACH" && -f "$ATTACH" ]]; then
      echo -e "$body" | mail -s "$subject" -r "$MAIL_FROM" -a "$ATTACH" "$MAIL_TO"
      log "Mail envoyé avec pièce jointe : $ATTACH"
      # si tmp file, on peut le supprimer
      if [[ "$ATTACH" == /tmp/frigate_snapshot_* ]]; then
          rm -f "$ATTACH"
      fi
   else
      echo -e "$body" | mail -s "$subject" -r "$MAIL_FROM" "$MAIL_TO"
      log "Mail envoyé sans pièce jointe."
   fi
 
done

on va créer un service systemd pour que le script soit lancé automatiquement, voilà le contenu de /usr/lib/systemd/system/frigate_alert.service

[Unit]
Description=Frigate MQTT Alert Email Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/alerte_frigate
Restart=always
RestartSec=5
User=root
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

[Install]
WantedBy=multi-user.target



on l'active en tapant

systemctl enable frigate_alert

et on lance via la commande

systemctl start frigate_alert

on pense à relancer Frigate car on a touché à sa configuration

docker stop frigate
docker start frigate

voilà ce que renvoie journalctl

déc. 07 12:20:43 ultra.kervao.fr systemd[1]: Started frigate_alert.service.
déc. 07 12:20:43 ultra.kervao.fr mosquitto[1436264]: 1765106443: New connection from 127.0.0.1:55164 on port 1883.
déc. 07 12:20:43 ultra.kervao.fr mosquitto[1436264]: 1765106443: New client connected from 127.0.0.1:55164 as auto-5B7917FC-A109-8461-E38C-B893A8928EA6 (p2, c1, k60, u'olivier').

et pour une détection

déc. 07 19:47:55 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Detected: camera=Camera-entree, label=person, id=1765133275.303314-xxsfrt
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [DEBUG] Trouvé après 3 seconde(s): /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Clip annoté trouvé: /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:47:59 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Clip candidate trouvé: /media/frigate/clips/Camera-entree-1765133275.303314-xxsfrt.jpg
déc. 07 19:48:00 ultra.kervao.fr alerte_frigate[1596716]: [INFO] Fichier stable (taille=223910). Prêt à être envoyé.

Le contenu du mail est le suivant en cas d'alerte

Nouvelle détection Frigate.

Caméra : Camera-entree
Objet  : person
ID     : 1765133275.303314-xxsfrt

Payload brut :
{"before": {"id": "1765133275.303314-xxsfrt", "camera": "Camera-entree", "frame_time": 1765133275.303314, "snapshot": null, "label": "person", "sub_label": null, "top_score": 0.0, "false_positive": true, "start_time": 1765133275.303314, "end_time": null, "score": 0.89453125, "box": [832, 201, 907, 368], "area": 12525, "ratio": 0.4491017964071856, "region": [724, 143, 1044, 463], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 0, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": false, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null}, "after": {"id": "1765133275.303314-xxsfrt", "camera": "Camera-entree", "frame_time": 1765133275.499867, "snapshot": {"frame_time": 1765133275.303314, "box": [832, 201, 907, 368], "area": 12525, "region": [724, 143, 1044, 463], "score": 0.89453125, "attributes": [], "current_estimated_speed": 0, "velocity_angle": 0, "path_data": [], "recognized_license_plate": null, "recognized_license_plate_score": null}, "label": "person", "sub_label": null, "top_score": 0.8671875, "false_positive": false, "start_time": 1765133275.303314, "end_time": null, "score": 0.84765625, "box": [815, 201, 897, 364], "area": 13366, "ratio": 0.5030674846625767, "region": [716, 134, 1036, 454], "active": true, "stationary": false, "motionless_count": 0, "position_changes": 1, "current_zones": [], "entered_zones": [], "has_clip": false, "has_snapshot": true, "attributes": {}, "current_attributes": [], "pending_loitering": false, "max_severity": null, "current_estimated_speed": 0, "average_estimated_speed": 0, "velocity_angle": 0, "path_data": [[[0.6687, 0.5056], 1765133275.499867]], "recognized_license_plate": null}, "type": "new"}

avec l'image de l'instantanée en pièce jointe. Et voilà une autre variante avec un envoi avec un mail en HTML dans le corps du message

   ### --- MAIL HTML ---
   TMPMAIL=$(mktemp)
   cat > "$TMPMAIL" <<EOF
<html>
<body style="font-family:Arial,sans-serif">
<h2 style="color:#c00">🚨 Alerte Frigate</h2>
<p>
<b>Caméra :</b> ${camera}<br>
<b>Objet détecté :</b> ${label}<br>
<b>ID événement :</b> ${EVENT_ID}<br>
<b>Date :</b> $(date +"%Y-%m-%d %H:%M:%S")
</p>
<p>Snapshot annoté :</p>
</body>
</html>
EOF

   s-nail \
     -S smtp="$SMTP_URL" \
     -S from="$MAIL_FROM" \
     -s "Alerte Frigate — ${label} (${camera})" \
     -a "$ATTACH" \
     -M "text/html" \
     "$MAIL_TO" < "$TMPMAIL"

   log "Mail envoyé avec snapshot : $CLIP_FILE"
   rm -f "$TMPMAIL"

Envoi des alertes avec Signal

On en vient à Signal ou plutôt signal-cli la version en ligne de commande dont l'URL est https://github.comAsamK/signal-cli, j'ai récupéré le binaire que j'ai décompressé et placé au bon endroit

tar xvfz signal-cli-0.14.1-Linux-native.tar.gz
mv signal-cli /usr/local/bin

on aura besoin aussi du générateur qrencode qu'on installera via urpmi. On suppose maintenant que vous disposez déjà d'un compte Signal, on va lier le poste Frigate au compte principal qui est sur le mobile en tapant

signal-cli link -n "FrigateAlert"

cela donne quelque chose comme

█████████████████████████████████████████████████
█████████████████████████████████████████████████
████ ▄▄▄▄▄ █▀ ▄▀▄█▄▀ █ ▄█ ▄▄█▄█▀ ██▄ █ ▄▄▄▄▄ ████
████ █   █ █▀█ ▄▀█▄ ▄█▀█▄▄▀▀▄▀▀ ▀▀▄  █ █   █ ████
████ █▄▄▄█ █▀ ▀▀▄▄▄▄▄██▀▄▄█▄▀██ ▄█▄▄██ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀ █ █ █ █▄▀ ▀▄▀ █ █▄█▄▀▄█▄▄▄▄▄▄▄████
████▄    ▀▄▄ █▄█  ▀█▀ ▄▄▀ ▄█▄█ █▄ █▀▄ ▀ ▀ ▀ █████
████▄ ██▀█▄  ▄ ▀  ▄▄▄ ▀▀▄ ▄▀ ▄█▄▄███ █▀▀█▀▄██████
████  ▀█▀ ▄█▀█▄▄▀▀▄▀▄█▀█▄█▄▄▄▄█  ▄▄█▀▀▀▀ █   ████
█████▄▀▀ ▄▄  ▀▀█▀█  ▄▄▄█▄ █▀ ▄█▄███▀█▄ ▄ ███▀████
████▄█▄▄▄█▄█  ▄█▄█▄▄▀█▀▀▄▀ ███▄▄▄▄▀▀▀▀▀▀▀  ▀█████
████▄▀▀▀▀ ▄▄▀ ▀█▄█▀▄ █▄█  ▄█ █  ▀▄▄▀▄█▄▄▄████████
████▄▀██▄▀▄██ ▄█▄█▀▀▀█▄█▀▀▄ ▀▄▀ ▀ █▀▄▄▀  ▄▄▀▀████
████▀ ██▄▄▄ █▄  ▄▄██▀█▀ ▀ ███▄   █▄█ ▀█  ███▀████
████  ▄▄ ▀▄▄▄▀█ ▄▀ ▀    █  ▀▄▄▄ ▀▄▀▀▀▀▀▀▀▄ ▄▀████
████ █▀ █▄▄██ ▀█▄ ▄▄█ ▀█▄ ▄█▀▄█ ▀█▀█▄ ▄▀▄█▄▄▀████
████▀▀▀▀ ▀▄▄ ▀▀▄▀▀▄ ▄█▀█▄▀▀▀ ▀▄  ██ █▀█▀ █▄▄ ████
████ █ ▀▀▀▄██▄██▀▄█ ▄▄▀█▄  ▀▀▄▄▄██▀█▄███▄ ▄▀▀████
████▄████▄▄█ ▀█▀▄▄▀▄▀▄██  ▄▄▄█▄▀ ▄▄█ ▄▄▄ ▀ ▀ ████
████ ▄▄▄▄▄ █▄  █ █▀  █▄█▄ ▄▀█▄▄ ▄▄▄  █▄█ ▄▄▀▀████
████ █   █ █  ▀▄▄█▀ ▀█▄█▀ ██▄ ▀▀▀ ▀█ ▄▄▄▄█   ████
████ █▄▄▄█ █  █ ▄███▀█  ▀▀▄█ █▀▄███▄▀█▄ ▄ ▄▀█████
████▄▄▄▄▄▄▄█▄▄█▄█▄▄█▄▄█▄▄█▄▄▄█▄▄███▄██▄▄█████████
█████████████████████████████████████████████████
█████████████████████████████████████████████████

sgnl://linkdevice?uuid=ITD32aRGnIGBhZIrHpUFqQ%3D%3D&pub_key=BZ0yldJk5JH04kHEQGm8Ga%1B4b3tSaJUBZXb%2F8K%2Bpdee4

avec le mobile on associe le poste Frigate en visualisant le QRCode ainsi obtenu et dans le premier shell on a comme message

INFO  ProvisioningManagerImpl - Received link information from +336XXXXXXXX, linking in progress ...
Associated with: +336XXXXXXXX

C'est bon le serveur Frigate est bien associé au compte Signal principal

Pour voir si ça fonctionne on peut taper

signal-cli --username +336XXXXXXXX send -m "This is a message" 06YYYYYYYY

Voilà une variante pour envoyer sur un groupe spécifique où on pourra rajouter d'autres personnes. Pour lister les groupes on tapera

signal-cli listGroups 

voilà le résultat

INFO  AccountHelper - The Signal protocol expects that incoming messages are regularly received.
Id: 2BVGj6gu1es4K9(...)4BdAwavlpon8rE= Name: groupe1  Active: true Blocked: false
Id: vToJsVzeJeME(...)iyP+Xxf61plZA8= Name: groupe2  Active: true Blocked: false
Id: ZWLvp3vW(....)vYy5hse3hpTQ0= Name: groupe3  Active: true Blocked: false
Id: 9dhS9vM5(...)rVVUJSM/7jYIrIU84= Name: groupe4  Active: true Blocked: false
Id: puoaoo7(...)8hm62WWcMlFqo= Name: groupe5  Active: true Blocked: false

et on met l'id du groupe concerné, pour envoyer un message ça donnera

signal-cli --username +336XXXXXXXX send -g vToJsVzeJeME(...)iyP+Xxf61plZA8=  -m "This is a test message"




maintenant on pourra rajouter en début de script avec les autres variables

   SIGNAL_USER="+336XXXXXXXX"       # ton numéro Signal
   SIGNAL_DEST="+336YYYYYYYY"       # numéro qui reçoit les alertes qui peut être le même numéro, ça apparaitra dans les Notes perso

puis plus loin à la place de l'envoi par mail

### --- ENVOI SIGNAL ---
   if [[ -n "$ATTACH" && -f "$ATTACH" ]]; then
       signal-cli \
               send -u "$SIGNAL_USER"\
               -g "Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
               -a "$ATTACH"\
               -m "🚨 Alerte Frigate
               Caméra : ${camera}
               Objet  : ${label}
               ID     : ${EVENT_ID}
               Date   : $(date)"
       # si tmp file, on peut le supprimer
       if [[ "$ATTACH" == /tmp/frigate_snapshot_* ]]; then
          rm -f "$ATTACH"
       fi
       log "Alerte envoyée sur Signal au groupe Alarme avec pj : $ATTACH"
   else  
       signal-cli \
               send -u "$SIGNAL_USER"\
               -g "Wti9r4I7n+76f(...)O3yLkUIgh3TAt9Y="\
               -m "🚨 Alerte Frigate
               Caméra : ${camera}
               Objet  : ${label}
               ID     : ${EVENT_ID}
               Date   : $(date)"
       log "Alerte envoyée sur Signal au groupe Alarme sans pj"
   fi

Et voilà ce que ça donne avec un envoi sur mon propre numéro



en cas d'alerte qui ne correspond pas à person ou car voilà ce que journalctl remontera

déc. 12 18:39:10 ultra.kervao.fr alerte_frigate[3356635]: [DEBUG] Événement ignoré pour label=cat


Frigate+

Frigate+ est l'option payante de Frigate qui pour un abonnement de 50$ (payable via paypal) par an, le principal attrait de cette version est de pouvoir bénéficier d'un modèle personnalisé qui permet d'affiner la détection en classant les vrais et faux positifs et d'accéder également aux modèles de la communauté. A force d'apprentissage le modèle personnalisé deviendra de plus en plus précis et nécessitera de moins en moins de ressource.
La page officielle est https://plus.frigate.video, une fois l'abonnement payé la première chose à faire est de créer la clé API puis de créer les caméras.



Dans la configuration de Frigate maintenant on indiquera la clé et on activera les photos instantanées (snapshots). Dans le fichier docker-compose.yml on rajoute dans la section environnement

   environment:
     PLUS_API_KEY: votre-clé-API

dans le fichier config.yaml on rajoute si ce n'est pas déjà fait une section snapshots

snapshots:
 enabled: true
 timestamp: true
 bounding_box: true
 retain:
   objects:
     person: 7
 clean_copy: true

Pour prendre en compte les modifications on stoppera Frigate en tapant docker stop frigate  puis

docker compose -f docker-compose.yml up -d

et on relance Frigate en tapant docker start frigate et on peut se rendre compte que Frigate+ est bien fonctionnel



Maintenant on clique sur Explorer et dans les filtres, on retient que les objets qui ont un instantané (snapshot) et qui n'ont pas été encore soumis à la base centralisée de Frigate+



on clique sur une image d'un objet et puis sur Instantané, on confirme si l'objet détecté en bas à droite de l'image est bien identifé


Dans cette copie d'écran on voit un exemple de classification avec la sous catégorie de l'objet Voiture et la plaque d'immatriculation qui a été identifiée. L'image a été soumise à la base Frigate+.


L'image est soumis au serveur centralisé et ainsi de suite, on se rend maintenant sous https://plus.frigate.video/dashboard/images/?tab=0 où on retrouve toutes les images qui ont été soumises. En cliquant sur une image on retrouve les objets qui ont été détectés




Assurez-vous que vos boîtes d'annotation sont bien cadrée autour de l'objet, il faut éviter de laisser du vide inutile autour de l'objet. Si une voiture est partiellement cachée par un arbre (comme ci-dessus), il faut recadrer la boîte sur la partie visible de la voiture si elle reste reconnaissable, sinon il peut être préférable de l'ignorer.

Si on est d'accord, on clique sur Verify & Save



Maintenant si un objet n'a pas été détecté on peut lui indiquer en cliquant sur Add (w) et on place la boîte de détection à l'endroit qui va bien en identifiant clairement l'objet (dans l'image ci-dessous il n'y a personne, c'est pour l'illustration)



a fortiori s'il y a un faux positif, on cliquera sur la boîte pour la supprimer et on voit que le label de l'objet se retrouve barré



On va maintenant générer son modèle personnalisé en plus du modèle de base de Frigate+ qui est lui même enrichi et entraîné par les objets identifiés par la communauté. Pour le créer il faut télécharger au moins dix images qui auront été correctement annotés et vérifiés, puis on l'enrichit en continuant à télécharger des images. L'idéal est d'avoir une bonne centaine d'images dans toutes les configurations météo pour les caméras extérieures et de luminosité.
Il faut cliquer sur Request Model



J'ai choisi yolov9 qui est réputé meilleur que mobiledet qui correspond à ma clé TPU



Et voilà le résultat





L'onglet Base Models donne la liste des modèles de base utilisés par défaut



Une fois que la génération du modèle est achevée qui peut être assez rapide si on n'a qu'une dizaine d'images téléchargées, on obtient un identifiant. Ci-dessous il m'en a créé deux.



Je me limite aux modèles pour ma clé TPU et j'indique son identifiant dans le fichier config.yaml avec une nouvelle section

model:
  path: plus://id-du-modèle

et on relance Frigate pour que le fichier soit pris en compte, et voilà ce que ça donne dans l'interface

A partir de là il faudra modifier les réglages de détection conformément à cette page. Ensuite on peut constater qu'on capte bien plus d'objets comme ce chat que je n'arrivais pas à détecter auparavant

Frigate+ permet de générer jusqu'à 12 modèles par an, l'intérêt de générer plusieurs modèles est de générer, modèle après modèle, des modèles affinés qui s'adaptent précisément à l'environnement des caméras. Les modèles successifs vont améliorer la détection de faux positifs, vont s'adapter aux conditions variables (saison, vent, luminosité, ...), mieux détecter certains objets (animaux, objet fixe, ...). Générer un modèle supplémentaire ne signifie pas qu'on utilisera plusieurs modèles jusqu'à saturer les ressources du système, le nouveau modèle remplacera juste le précédent.

Générer un modèle par mois est un bon rythme, dans la période de vie d'un modèle, il faudra continuer à annoter les images, dans l'idéal il faut au moins 200 à 300 images annotées pour que ça vaille le coup de changer de modèle et qu'on puisse constater une réelle amélioration.

Attention pour l'annotation des images, dans les images qu'on soumet il ne faut pas se limiter aux faux positifs sinon le modèle va devenir paranoïaque et ne plus rien détecter ou de se mélanger les pinceaux dans la détection, confondre par exemple une personne avec un chat, il faut donc veiller également à soumettre des détections positives. Le ratio idéal est de soumettre 80% de vrais positifs et 20% de faux positifs.

S'il détecte régulièrement un pilier de portail comme une personne, il faudra soumettre également des images où une vraie personne passe devant le pilier histoire qu'il voit la différence, c'est ce qu'on appelle de l'apprentissage contrastif.

Pour créer un nouveau modèle dans l'interface Frigate+ on cliquera sur Request Model




Puis Continue, j'ai choisi le modèle adapté a la clé Coral


cela va prendre un peu de temps pour le générer


on est averti par mail quand le modèle est prêt


Il faut ensuite modifier le fichier config.yaml pour indiquer l'identifiant du nouveau modèle et relancer frigate.

model:
 path: plus://nouvel-identifiant

et pour vérifier que c'est bien pris en compte, on ira vérifier les paramètres au niveau de Frigate+

A noter que si le modèle de base évolue vers une nouvelle version, il faudra redemander la génération d'un nouveau modèle personnalisé pour qu'il soit pris en compte.

En cas de non reconduction de l'abonnement on pourra toujours avoir accès aux modèles qui ont été générés.

Quelques liens pour en savoir plus