<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
		<id>http://wikigeii.iut-troyes.univ-reims.fr//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fredmn</id>
		<title>troyesGEII - Contributions de l’utilisateur [fr]</title>
		<link rel="self" type="application/atom+xml" href="http://wikigeii.iut-troyes.univ-reims.fr//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Fredmn"/>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Sp%C3%A9cial:Contributions/Fredmn"/>
		<updated>2026-05-06T23:41:06Z</updated>
		<subtitle>Contributions de l’utilisateur</subtitle>
		<generator>MediaWiki 1.30.1</generator>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20964</id>
		<title>Cours:Elen4 TNS</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20964"/>
				<updated>2026-05-04T14:56:09Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Travaux Pratiques */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;S4 - ESE - Traitement numérique du signal&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Description générale ==&lt;br /&gt;
&lt;br /&gt;
=== Contenu (Programme National) ===&lt;br /&gt;
&lt;br /&gt;
Filtrage numérique :&lt;br /&gt;
* Équation de récurrence, opérations élémentaires, fonction de transfert en Z, passage à un programme informatique;&lt;br /&gt;
* Filtre numérique : ﬁltres récursif et non récursif, réponses temporelles (impulsionnelle, indicielle, signal quelconque), réponse fréquentielle, stabilité;&lt;br /&gt;
* Synthèse de ﬁltres numériques simples (approximation d’Euler);&lt;br /&gt;
* Utilisation d’outils de synthèse des ﬁltres numériques RIF et RII (Matlab, Labview, GNU radio...) et comparaison des performances des ﬁltres RIF et RII;&lt;br /&gt;
* Présentation des différentes structures des ﬁltres numériques (direct, cascade, parallèle);&lt;br /&gt;
* Implantation d’un traitement numérique sur cible : ﬁltrage numérique simple, asservissement numérique simple (robot mobile, drone...), traitement de données.&lt;br /&gt;
&lt;br /&gt;
=== Volumes horaire ===&lt;br /&gt;
&lt;br /&gt;
* CM : 4 séances d'1,5h&lt;br /&gt;
* TD : 6 séances d'1,5h + une évaluation d'1,5h&lt;br /&gt;
* TP : 6 séances de 3h + une évaluation de 2h&lt;br /&gt;
&lt;br /&gt;
=== Évaluations ===&lt;br /&gt;
&lt;br /&gt;
* une évaluation sur table (derniere séance de CM/TD - 1,5h)&lt;br /&gt;
* une évaluation pratique (dernière séance de TP - 2h)&lt;br /&gt;
&lt;br /&gt;
== Cours Magistraux ==&lt;br /&gt;
&lt;br /&gt;
Contenu :&lt;br /&gt;
* Signaux et systèmes numériques&lt;br /&gt;
* Filtres numériques&lt;br /&gt;
* Quelques filtres RIF&lt;br /&gt;
* Synthèse de filtres numériques&lt;br /&gt;
* PID numérique &lt;br /&gt;
* Élements sur le traitement des images numériques&lt;br /&gt;
* Élements sur le temps-fréquence&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Documents :&lt;br /&gt;
* [[Media:TNS-Support.pdf|Support de cours en pdf]]&lt;br /&gt;
* [[Media:TNS-diapos.pdf|Diapositives de cours en pdf]]&lt;br /&gt;
&lt;br /&gt;
== Travaux Dirigés ==&lt;br /&gt;
&lt;br /&gt;
Textes en pdf :&lt;br /&gt;
&lt;br /&gt;
* [[Media:TNS-td1.FxTransfert.pdf|TD1 : Fonctions de transfert]]&lt;br /&gt;
* [[Media:TNS-td2.Stabilité.pdf|TD2 : Causabilité et stabilité]]&lt;br /&gt;
* [[Media:TNS-td3.RepFreq.pdf|TD3 : Réponses fréquentielles]]&lt;br /&gt;
* [[Media:TNS-td4.Facteurs1et2.pdf|TD4 : Facteurs du premier et du second ordre]]&lt;br /&gt;
* [[Media:TNS-td5-Structures.pdf|TD5 : Structures de réalisation]]&lt;br /&gt;
* [[Media:TNS-td6-ConceptionFiltresRII.pdf|TD6 : Conception de filtres récursifs]]&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Les TP utiliserons principalement :&lt;br /&gt;
* Gnuradio (cf premier TP)&lt;br /&gt;
* Matlab, que vous pourrez utiliser en ligne avec votre compte universitaire : [https://fr.mathworks.com/products/matlab-online.html Matlab online]&lt;br /&gt;
* Python, que nous utiliserons en installation locale avec [https://pixi.prefix.dev/ Pixi]&lt;br /&gt;
* et occasionnellement des cartes Arduinio ou Rpi Pico.&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
* Guide de travail avec Python&lt;br /&gt;
&amp;lt;br/&amp;gt;&lt;br /&gt;
Liste des TP :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Gnuradio|TP1 - Fondamentaux Gnuradio]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_FxTransfertStabilité|TP2 - Fonction de transfert et stabilité]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Implementations|TP3 - Implémentations de filtres]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitImage|TP4 - Traitements d'images numériques]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitAudioEtDemodulation| TP5 - Traitements audio et démodulation]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_ TpsReel |TP6 - Temps réel -  Codage d'un bloc en Python]]&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Évaluation de Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Énoncés :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_eval1|Sujet n°1]]&lt;br /&gt;
&lt;br /&gt;
Documents autorisés :&lt;br /&gt;
* accès au wiki&lt;br /&gt;
* documents de cours&lt;br /&gt;
* notes personnelles&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS_TP_TpsReel&amp;diff=20963</id>
		<title>Cours:Elen4 TNS TP TpsReel</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS_TP_TpsReel&amp;diff=20963"/>
				<updated>2026-05-04T14:53:44Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Cours:Elen4_TNS|{{Rouge|'''Retour à la page du cours'''}}]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP6 : Temps réel : Codage d'un bloc en Python / démodulation &amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''EN COURS DE RÉÉCRITURE (C++ vers Python)'''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Le but de ce TP est de réaliser l'implémentation temps-réel en C++ de filtres IIR. Cette implémentation sera réalisée en créant un bloc Gnuradio qui execute un traitement codé en C++&lt;br /&gt;
&lt;br /&gt;
=== Codage de bloc Gnuradio en c++ ===&lt;br /&gt;
&lt;br /&gt;
Ce guide est inspirée du guide [https://wiki.gnuradio.org/index.php?title=Creating_C%2B%2B_OOT_with_gr-modtool Creating an OOT (C++ block example)] en l'adaptant pour nos besoins :&lt;br /&gt;
&lt;br /&gt;
Nous allons créer un simple bloc qui recopie son entrée (en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;) sur sa sortie. Donc le filtre caractérisé par ''y(n)'' = ''x(n)'' ou encore ''H(z)''=1. Il s'agit donc d'une sorte de filtre passe-tout.&lt;br /&gt;
&lt;br /&gt;
'''Suivez bien toutes les étapes, sans aller trop rapidement'''&lt;br /&gt;
&lt;br /&gt;
* Vous pourrez utiliser &amp;lt;code&amp;gt;gedit&amp;lt;/code&amp;gt; pour éditer les divers fichiers.&lt;br /&gt;
&lt;br /&gt;
==== Création d'un module externe ====&lt;br /&gt;
&lt;br /&gt;
Dans les lignes qui suivent, le &amp;lt;code&amp;gt;$&amp;lt;/code&amp;gt; indique que l'on saisi une commande dans un terminal. Le &amp;lt;code&amp;gt;$&amp;lt;/code&amp;gt; n'est pas à saisir.&lt;br /&gt;
&lt;br /&gt;
* Ouvrez un terminal et placez-vous à la racine de votre dossier personnel :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ cd $HOME&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Créez un dossier de travail dédié à gnuradio et déplacez-vous à l'intérieur :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ mkdir gnuradio&lt;br /&gt;
$ cd gnuradio&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* GNU Radio est livré avec gr_modtool, un utilitaire qui permet de créer des modules externes (OOT). Créons un module &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ gr_modtool newmod monModule&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Le dossier &amp;lt;code&amp;gt;gr-monModule&amp;lt;/code&amp;gt; est créé et contient tout le code squelette d'un module OOT, mais il n'a pas encore de blocs. Déplacez-vous dans &amp;lt;code&amp;gt;gr-monModule&amp;lt;/code&amp;gt; :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ cd gr-monModule&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Vous pouvez afficher le contenu de ce dossier par&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ ls&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Création d'un bloc dans ce module ====&lt;br /&gt;
&lt;br /&gt;
* Ajoutez un nouveau bloc nommé &amp;lt;code&amp;gt;passetout&amp;lt;/code&amp;gt; :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ gr_modtool add passetout&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Quelques questions permettent alors de spécifier le type de bloc&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
GNU Radio module name identified: passetout&lt;br /&gt;
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Saisissez &amp;lt;code&amp;gt;sync&amp;lt;/code&amp;gt; comme type de bloc, car le bloc que nous écrivons produira le même nombre d'échantillons en sortie que le nombre d'échantillons consommés depuis l'entrée.&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Enter block type: sync&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Spécifiez &amp;lt;code&amp;gt;cpp&amp;lt;/code&amp;gt; comme langage :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Language (python/cpp): cpp&lt;br /&gt;
Language: C++&lt;br /&gt;
Block/code identifier: passetout&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Spécifiez un nom pour le copyright :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Please specify the copyright holder: Bibi&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Il est possible de spécifier des paramètres d'entrée qui peuvent permettre de régler le fonctionnement du bloc. Ce ne sera pas notre cas, ne rien saisir sur cette question :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Enter valid argument list, including default arguments: &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* De même, on ne donnera pas de code permettant de tester le bon fonctionnement :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Add Python QA code? [Y/n] n&lt;br /&gt;
Add C++ QA code? [Y/n] n&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Plusieurs fichers sont alors crées :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Adding file 'lib/passetout_impl.h'...&lt;br /&gt;
Adding file 'lib/passetout_impl.cc'...&lt;br /&gt;
Adding file 'include/gnuradio/monModule/passetout.h'...&lt;br /&gt;
Adding file 'python/monModule/bindings/docstrings/passetout_pydoc_template.h'...&lt;br /&gt;
Adding file 'python/monModule/bindings/passetout_python.cc'...&lt;br /&gt;
Adding file 'grc/monModule_passetout.block.yml'...&lt;br /&gt;
Editing grc/CMakeLists.txt...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quelques indications :&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;passetout_impl.h&amp;lt;/code&amp;gt; contient l'entête de la classe de notre bloc&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;passetout_impl.cc&amp;lt;/code&amp;gt; contient l'implémentation de la classe de notre bloc&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;monModule_passetout.block.yml&amp;lt;/code&amp;gt; va contenir des informations faisant le lien entre notre code c++ et le code python utilisé en interne par Gnuradio&lt;br /&gt;
* Le tout sera compilé par un &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; généré par &amp;lt;code&amp;gt;cmake&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;passetout_impl.h&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Le code utile généré est le suivant :&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
class passetout_impl : public passetout&lt;br /&gt;
{&lt;br /&gt;
private:&lt;br /&gt;
    // Nothing to declare in this block.&lt;br /&gt;
&lt;br /&gt;
public:&lt;br /&gt;
    passetout_impl();&lt;br /&gt;
    ~passetout_impl();&lt;br /&gt;
&lt;br /&gt;
    // Where all the action really happens&lt;br /&gt;
    int work(int noutput_items,&lt;br /&gt;
             gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
             gr_vector_void_star&amp;amp; output_items);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour se réveiller les neurones sur la POO, qu'est-ce que sont&lt;br /&gt;
* &amp;lt;code&amp;gt;passetout_impl()&amp;lt;/code&amp;gt;,&lt;br /&gt;
* &amp;lt;code&amp;gt;~passetout_impl()&amp;lt;/code&amp;gt;,&lt;br /&gt;
* &amp;lt;code&amp;gt;private&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;public&amp;lt;/code&amp;gt; ?&lt;br /&gt;
&lt;br /&gt;
Le travail sera réalisé dans la méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt;, mais nous n'avons pas de modification à apporter dans les déclarations par défaut.&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;passetout_impl.cc&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Le code utile généré est le suivant :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=passetout_impl.cc}}&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
namespace monModule {&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using input_type = float;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using output_type = float;&lt;br /&gt;
passetout::sptr passetout::make() { return gnuradio::make_block_sptr&amp;lt;passetout_impl&amp;gt;(); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The private constructor&lt;br /&gt;
 */&lt;br /&gt;
passetout_impl::passetout_impl()&lt;br /&gt;
    : gr::sync_block(&amp;quot;passetout&amp;quot;,&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Our virtual destructor.&lt;br /&gt;
 */&lt;br /&gt;
passetout_impl::~passetout_impl() {}&lt;br /&gt;
&lt;br /&gt;
int passetout_impl::work(int noutput_items,&lt;br /&gt;
                         gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
                         gr_vector_void_star&amp;amp; output_items)&lt;br /&gt;
{&lt;br /&gt;
    auto in = static_cast&amp;lt;const input_type*&amp;gt;(input_items[0]);&lt;br /&gt;
    auto out = static_cast&amp;lt;output_type*&amp;gt;(output_items[0]);&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;Implement the signal processing in your block and remove this warning&amp;quot;)&lt;br /&gt;
    // Do &amp;lt;+signal processing+&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Tell runtime system how many output items we produced.&lt;br /&gt;
    return noutput_items;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
} /* namespace monModule */&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
Détaillons :&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using input_type = float;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using output_type = float;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{Todos| On spécifie ici le type des données d'entrée et de sortie. Pas de modification à apporter, il suffit de supprimer les deux &amp;lt;code&amp;gt;#pragma&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
passetout_impl::passetout_impl()&lt;br /&gt;
    : gr::sync_block(&amp;quot;passetout&amp;quot;,&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| On spécifie ici le nombre d'entrées (1) et le nombre de sorties (1). Pas de modification non plus.}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Expliquez la syntaxe &amp;lt;code&amp;gt;passetout_impl::passetout_impl() : gr::sync_block(...)&amp;quot;&amp;lt;/code&amp;gt; }}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
int passetout_impl::work(int noutput_items,&lt;br /&gt;
                         gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
                         gr_vector_void_star&amp;amp; output_items)&lt;br /&gt;
{&lt;br /&gt;
    auto in = static_cast&amp;lt;const input_type*&amp;gt;(input_items[0]);&lt;br /&gt;
    auto out = static_cast&amp;lt;output_type*&amp;gt;(output_items[0]);&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;Implement the signal processing in your block and remove this warning&amp;quot;)&lt;br /&gt;
    // Do &amp;lt;+signal processing+&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Tell runtime system how many output items we produced.&lt;br /&gt;
    return noutput_items;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cette méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt; implémente véritablement le filtrage.&lt;br /&gt;
* Les tableaux &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; contiennent respectivement les données en entrée et les données en sortie. Il faut donc calculer &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; en fonction de &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt;&lt;br /&gt;
* Les données d'entrée arrivent par paquets dans un buffer. La variable &amp;lt;code&amp;gt;noutput_items&amp;lt;/code&amp;gt; contient le nombre de données disponibles en entrées.&lt;br /&gt;
* à la fin de la méthode, on renvoie le nombre de données en sortie que l'on a calculé (entre 1 et &amp;lt;code&amp;gt;noutput_items&amp;lt;/code&amp;gt;). Les données d'entrées éventuellement non consommées seront conservées dans le buffer pour le prochain appel de &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Todos| On souhaite ici seulement recopier l'entrée sur la sortie, donc }}&lt;br /&gt;
# Enlever la ligne &amp;lt;code&amp;gt;#pragma&amp;lt;/code&amp;gt;&lt;br /&gt;
# ajouter le code &amp;lt;code&amp;gt; out[0] = in[0];&amp;lt;/code&amp;gt; (on recopie le premier échantillon disponible en entrée)&lt;br /&gt;
# changer &amp;lt;code&amp;gt;return noutput_items;&amp;lt;/code&amp;gt; en &amp;lt;code&amp;gt;return 1;&amp;lt;/code&amp;gt; (un seul échantillon a été consommé).&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;monModule_passetout.block.yml&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Ce fichier au format YAML (''yet another markup language'') fait le lien entre le code c++ et le code python utilisé en interne par GnuRadio.&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=monModule_passetout.block.yml}}&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
id: monModule_passetout&lt;br /&gt;
label: passetout&lt;br /&gt;
category: '[monModule]'&lt;br /&gt;
&lt;br /&gt;
templates:&lt;br /&gt;
  imports: from gnuradio import monModule&lt;br /&gt;
  make: monModule.passetout()&lt;br /&gt;
&lt;br /&gt;
#  Make one 'parameters' list entry for every parameter you want settable from the GUI.&lt;br /&gt;
#     Keys include:&lt;br /&gt;
#     * id (makes the value accessible as keyname, e.g. in the make entry)&lt;br /&gt;
#     * label (label shown in the GUI)&lt;br /&gt;
#     * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)&lt;br /&gt;
#     * default&lt;br /&gt;
parameters:&lt;br /&gt;
- id: parametername_replace_me&lt;br /&gt;
  label: FIX ME:&lt;br /&gt;
  dtype: string&lt;br /&gt;
  default: You need to fill in your grc/monModule_passetout.block.yaml&lt;br /&gt;
#- id: ...&lt;br /&gt;
#  label: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
&lt;br /&gt;
#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.&lt;br /&gt;
#  Keys include:&lt;br /&gt;
#      * label (an identifier for the GUI)&lt;br /&gt;
#      * domain (optional - stream or message. Default is stream)&lt;br /&gt;
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)&lt;br /&gt;
#      * vlen (optional - data stream vector length. Default is 1)&lt;br /&gt;
#      * optional (optional - set to 1 for optional inputs. Default is 0)&lt;br /&gt;
inputs:&lt;br /&gt;
#- label: ...&lt;br /&gt;
#  domain: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
#  vlen: ...&lt;br /&gt;
#  optional: ...&lt;br /&gt;
&lt;br /&gt;
outputs:&lt;br /&gt;
#- label: ...&lt;br /&gt;
#  domain: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
#  vlen: ...&lt;br /&gt;
#  optional: ...&lt;br /&gt;
&lt;br /&gt;
#  'file_format' specifies the version of the GRC yml format used in the file&lt;br /&gt;
#  and should usually not be changed.&lt;br /&gt;
file_format: 1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;parameters&amp;lt;/code&amp;gt; (pas de paramètres, on commente) :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
#parameters:&lt;br /&gt;
#- id: parametername_replace_me&lt;br /&gt;
#  label: FIX ME:&lt;br /&gt;
#  dtype: string&lt;br /&gt;
#  default: You need to fill in your grc/customModule_testMod.block.yaml&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;inputs&amp;lt;/code&amp;gt; :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
inputs:&lt;br /&gt;
- label: in&lt;br /&gt;
  domain: stream&lt;br /&gt;
  dtype: float&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
outputs:&lt;br /&gt;
- label: out&lt;br /&gt;
  domain: stream&lt;br /&gt;
  dtype: float&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Compilation et installation ====&lt;br /&gt;
&lt;br /&gt;
Voici les étapes :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
$ cd CHEMIN-DE-VOTRE-DOSSIER/gr-monModule&lt;br /&gt;
$ mkdir build&lt;br /&gt;
$ cd build&lt;br /&gt;
$ cmake ..&lt;br /&gt;
$ make&lt;br /&gt;
$ make install&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cmake ..&amp;lt;/code&amp;gt; lance la création des fichiers &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; qui permettent la compilation et l'installation&lt;br /&gt;
* &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; lance la compilation&lt;br /&gt;
* &amp;lt;code&amp;gt;make install&amp;lt;/code&amp;gt; lance l'installation (copie des fichiers dans les dossiers gnuradio)&lt;br /&gt;
&lt;br /&gt;
{{Todos| Exécuter chacune de ces lignes, en contrôlant l'absence d'erreur. Dans le cas contraire, contrôler les fichiers &amp;lt;code&amp;gt;.h&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.cc&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;.yaml&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| '''Après ces étapes, et avant de lancer gnuRadio, appeler l'enseignant qui va mettre à jours les bibliothèques partagées.'''}}. Sinon tenter :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH&lt;br /&gt;
$ gnuradio-companion &amp;amp;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation du bloc et test ====&lt;br /&gt;
&lt;br /&gt;
* Dans Gnuradio, rafraichir la liste des bloc (bouton ''reload'')&lt;br /&gt;
* le bloc &amp;lt;code&amp;gt;passetout&amp;lt;/code&amp;gt; doit être présent dans la section &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester le bon fonctionnement de ce bloc en lui envoyant une sinusoïde que vous devez observer identique à la sortie du bloc. Vérifier avec des graphes.&lt;br /&gt;
&lt;br /&gt;
=== Démodulation ===&lt;br /&gt;
&lt;br /&gt;
En reprenant le signal modulé en amplitude et la fonction de transfert du filtre passe-bas du TP précédent :&lt;br /&gt;
&lt;br /&gt;
* Obtenir l'équation aux différence du filtre, ''ie'' écrire ''y(n)='' en fonction de ''x(n)'', ''x(n-1)'', ''x(n-2)'', ''y(n-1)'' et ''y(n-2)''.&lt;br /&gt;
* Créer un nouveau bloc C++ (similaire au passe-tout précédent) dans le module &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt; :&lt;br /&gt;
** de type &amp;lt;code&amp;gt;sync&amp;lt;/code&amp;gt;&lt;br /&gt;
** une entrée en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;&lt;br /&gt;
** une sortie en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;&lt;br /&gt;
* Écrire le code permettant d'obtenir ''y(n)'' dans la méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt; :&lt;br /&gt;
** ''x(n)'' est donné par &amp;lt;code&amp;gt;in0[0]&amp;lt;/code&amp;gt;&lt;br /&gt;
** il sera nécessaire d'affecter &amp;lt;code&amp;gt;out0[0]&amp;lt;/code&amp;gt; pour produire ''y(n)''.&lt;br /&gt;
** il sera nécessaire de stocker dans des '''attributs''' les valeurs de &amp;lt;code&amp;gt;in[0]&amp;lt;/code&amp;gt; afin de disposer de ''x(n-1)'', ''x(n-2)'', ''y(n-1)'' et ''y(n-2)''.&lt;br /&gt;
** On rappelle que les attributs doivent être déclarées dans le fichier &amp;lt;code&amp;gt;.h&amp;lt;/code&amp;gt; et initialisés dans le constructeur de la classe.&lt;br /&gt;
&lt;br /&gt;
{{Todos| Réaliser cette implémentation du passe-bas et contrôler que la démodulation est identique à celle obtenue au TP précédent}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Modifier votre code pour implémenter le filtre complet de démodulation}}&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS_TP_TpsReel&amp;diff=20962</id>
		<title>Cours:Elen4 TNS TP TpsReel</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS_TP_TpsReel&amp;diff=20962"/>
				<updated>2026-05-04T14:52:12Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Cours:Elen4_TNS|{{Rouge|'''Retour à la page du cours'''}}]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP6 : Temps réel : Codage d'un bloc en Python / démodulation &amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le but de ce TP est de réaliser l'implémentation temps-réel en C++ de filtres IIR. Cette implémentation sera réalisée en créant un bloc Gnuradio qui execute un traitement codé en C++&lt;br /&gt;
&lt;br /&gt;
=== Codage de bloc Gnuradio en c++ ===&lt;br /&gt;
&lt;br /&gt;
Ce guide est inspirée du guide [https://wiki.gnuradio.org/index.php?title=Creating_C%2B%2B_OOT_with_gr-modtool Creating an OOT (C++ block example)] en l'adaptant pour nos besoins :&lt;br /&gt;
&lt;br /&gt;
Nous allons créer un simple bloc qui recopie son entrée (en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;) sur sa sortie. Donc le filtre caractérisé par ''y(n)'' = ''x(n)'' ou encore ''H(z)''=1. Il s'agit donc d'une sorte de filtre passe-tout.&lt;br /&gt;
&lt;br /&gt;
'''Suivez bien toutes les étapes, sans aller trop rapidement'''&lt;br /&gt;
&lt;br /&gt;
* Vous pourrez utiliser &amp;lt;code&amp;gt;gedit&amp;lt;/code&amp;gt; pour éditer les divers fichiers.&lt;br /&gt;
&lt;br /&gt;
==== Création d'un module externe ====&lt;br /&gt;
&lt;br /&gt;
Dans les lignes qui suivent, le &amp;lt;code&amp;gt;$&amp;lt;/code&amp;gt; indique que l'on saisi une commande dans un terminal. Le &amp;lt;code&amp;gt;$&amp;lt;/code&amp;gt; n'est pas à saisir.&lt;br /&gt;
&lt;br /&gt;
* Ouvrez un terminal et placez-vous à la racine de votre dossier personnel :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ cd $HOME&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Créez un dossier de travail dédié à gnuradio et déplacez-vous à l'intérieur :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ mkdir gnuradio&lt;br /&gt;
$ cd gnuradio&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* GNU Radio est livré avec gr_modtool, un utilitaire qui permet de créer des modules externes (OOT). Créons un module &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ gr_modtool newmod monModule&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Le dossier &amp;lt;code&amp;gt;gr-monModule&amp;lt;/code&amp;gt; est créé et contient tout le code squelette d'un module OOT, mais il n'a pas encore de blocs. Déplacez-vous dans &amp;lt;code&amp;gt;gr-monModule&amp;lt;/code&amp;gt; :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ cd gr-monModule&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Vous pouvez afficher le contenu de ce dossier par&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ ls&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Création d'un bloc dans ce module ====&lt;br /&gt;
&lt;br /&gt;
* Ajoutez un nouveau bloc nommé &amp;lt;code&amp;gt;passetout&amp;lt;/code&amp;gt; :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ gr_modtool add passetout&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Quelques questions permettent alors de spécifier le type de bloc&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
GNU Radio module name identified: passetout&lt;br /&gt;
('sink', 'source', 'sync', 'decimator', 'interpolator', 'general', 'tagged_stream', 'hier', 'noblock')&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Saisissez &amp;lt;code&amp;gt;sync&amp;lt;/code&amp;gt; comme type de bloc, car le bloc que nous écrivons produira le même nombre d'échantillons en sortie que le nombre d'échantillons consommés depuis l'entrée.&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Enter block type: sync&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Spécifiez &amp;lt;code&amp;gt;cpp&amp;lt;/code&amp;gt; comme langage :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Language (python/cpp): cpp&lt;br /&gt;
Language: C++&lt;br /&gt;
Block/code identifier: passetout&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Spécifiez un nom pour le copyright :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Please specify the copyright holder: Bibi&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Il est possible de spécifier des paramètres d'entrée qui peuvent permettre de régler le fonctionnement du bloc. Ce ne sera pas notre cas, ne rien saisir sur cette question :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Enter valid argument list, including default arguments: &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* De même, on ne donnera pas de code permettant de tester le bon fonctionnement :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Add Python QA code? [Y/n] n&lt;br /&gt;
Add C++ QA code? [Y/n] n&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Plusieurs fichers sont alors crées :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
Adding file 'lib/passetout_impl.h'...&lt;br /&gt;
Adding file 'lib/passetout_impl.cc'...&lt;br /&gt;
Adding file 'include/gnuradio/monModule/passetout.h'...&lt;br /&gt;
Adding file 'python/monModule/bindings/docstrings/passetout_pydoc_template.h'...&lt;br /&gt;
Adding file 'python/monModule/bindings/passetout_python.cc'...&lt;br /&gt;
Adding file 'grc/monModule_passetout.block.yml'...&lt;br /&gt;
Editing grc/CMakeLists.txt...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Quelques indications :&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;passetout_impl.h&amp;lt;/code&amp;gt; contient l'entête de la classe de notre bloc&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;passetout_impl.cc&amp;lt;/code&amp;gt; contient l'implémentation de la classe de notre bloc&lt;br /&gt;
* le fichier &amp;lt;code&amp;gt;monModule_passetout.block.yml&amp;lt;/code&amp;gt; va contenir des informations faisant le lien entre notre code c++ et le code python utilisé en interne par Gnuradio&lt;br /&gt;
* Le tout sera compilé par un &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; généré par &amp;lt;code&amp;gt;cmake&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;passetout_impl.h&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Le code utile généré est le suivant :&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
class passetout_impl : public passetout&lt;br /&gt;
{&lt;br /&gt;
private:&lt;br /&gt;
    // Nothing to declare in this block.&lt;br /&gt;
&lt;br /&gt;
public:&lt;br /&gt;
    passetout_impl();&lt;br /&gt;
    ~passetout_impl();&lt;br /&gt;
&lt;br /&gt;
    // Where all the action really happens&lt;br /&gt;
    int work(int noutput_items,&lt;br /&gt;
             gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
             gr_vector_void_star&amp;amp; output_items);&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pour se réveiller les neurones sur la POO, qu'est-ce que sont&lt;br /&gt;
* &amp;lt;code&amp;gt;passetout_impl()&amp;lt;/code&amp;gt;,&lt;br /&gt;
* &amp;lt;code&amp;gt;~passetout_impl()&amp;lt;/code&amp;gt;,&lt;br /&gt;
* &amp;lt;code&amp;gt;private&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;public&amp;lt;/code&amp;gt; ?&lt;br /&gt;
&lt;br /&gt;
Le travail sera réalisé dans la méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt;, mais nous n'avons pas de modification à apporter dans les déclarations par défaut.&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;passetout_impl.cc&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Le code utile généré est le suivant :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=passetout_impl.cc}}&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
namespace monModule {&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using input_type = float;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using output_type = float;&lt;br /&gt;
passetout::sptr passetout::make() { return gnuradio::make_block_sptr&amp;lt;passetout_impl&amp;gt;(); }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * The private constructor&lt;br /&gt;
 */&lt;br /&gt;
passetout_impl::passetout_impl()&lt;br /&gt;
    : gr::sync_block(&amp;quot;passetout&amp;quot;,&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * Our virtual destructor.&lt;br /&gt;
 */&lt;br /&gt;
passetout_impl::~passetout_impl() {}&lt;br /&gt;
&lt;br /&gt;
int passetout_impl::work(int noutput_items,&lt;br /&gt;
                         gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
                         gr_vector_void_star&amp;amp; output_items)&lt;br /&gt;
{&lt;br /&gt;
    auto in = static_cast&amp;lt;const input_type*&amp;gt;(input_items[0]);&lt;br /&gt;
    auto out = static_cast&amp;lt;output_type*&amp;gt;(output_items[0]);&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;Implement the signal processing in your block and remove this warning&amp;quot;)&lt;br /&gt;
    // Do &amp;lt;+signal processing+&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Tell runtime system how many output items we produced.&lt;br /&gt;
    return noutput_items;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
} /* namespace monModule */&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
Détaillons :&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using input_type = float;&lt;br /&gt;
#pragma message(&amp;quot;set the following appropriately and remove this warning&amp;quot;)&lt;br /&gt;
using output_type = float;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{Todos| On spécifie ici le type des données d'entrée et de sortie. Pas de modification à apporter, il suffit de supprimer les deux &amp;lt;code&amp;gt;#pragma&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
passetout_impl::passetout_impl()&lt;br /&gt;
    : gr::sync_block(&amp;quot;passetout&amp;quot;,&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min inputs */, 1 /* max inputs */, sizeof(input_type)),&lt;br /&gt;
                     gr::io_signature::make(&lt;br /&gt;
                         1 /* min outputs */, 1 /*max outputs */, sizeof(output_type)))&lt;br /&gt;
{&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| On spécifie ici le nombre d'entrées (1) et le nombre de sorties (1). Pas de modification non plus.}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Expliquez la syntaxe &amp;lt;code&amp;gt;passetout_impl::passetout_impl() : gr::sync_block(...)&amp;quot;&amp;lt;/code&amp;gt; }}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
int passetout_impl::work(int noutput_items,&lt;br /&gt;
                         gr_vector_const_void_star&amp;amp; input_items,&lt;br /&gt;
                         gr_vector_void_star&amp;amp; output_items)&lt;br /&gt;
{&lt;br /&gt;
    auto in = static_cast&amp;lt;const input_type*&amp;gt;(input_items[0]);&lt;br /&gt;
    auto out = static_cast&amp;lt;output_type*&amp;gt;(output_items[0]);&lt;br /&gt;
&lt;br /&gt;
#pragma message(&amp;quot;Implement the signal processing in your block and remove this warning&amp;quot;)&lt;br /&gt;
    // Do &amp;lt;+signal processing+&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    // Tell runtime system how many output items we produced.&lt;br /&gt;
    return noutput_items;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cette méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt; implémente véritablement le filtrage.&lt;br /&gt;
* Les tableaux &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; contiennent respectivement les données en entrée et les données en sortie. Il faut donc calculer &amp;lt;code&amp;gt;out&amp;lt;/code&amp;gt; en fonction de &amp;lt;code&amp;gt;in&amp;lt;/code&amp;gt;&lt;br /&gt;
* Les données d'entrée arrivent par paquets dans un buffer. La variable &amp;lt;code&amp;gt;noutput_items&amp;lt;/code&amp;gt; contient le nombre de données disponibles en entrées.&lt;br /&gt;
* à la fin de la méthode, on renvoie le nombre de données en sortie que l'on a calculé (entre 1 et &amp;lt;code&amp;gt;noutput_items&amp;lt;/code&amp;gt;). Les données d'entrées éventuellement non consommées seront conservées dans le buffer pour le prochain appel de &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt;&lt;br /&gt;
{{Todos| On souhaite ici seulement recopier l'entrée sur la sortie, donc }}&lt;br /&gt;
# Enlever la ligne &amp;lt;code&amp;gt;#pragma&amp;lt;/code&amp;gt;&lt;br /&gt;
# ajouter le code &amp;lt;code&amp;gt; out[0] = in[0];&amp;lt;/code&amp;gt; (on recopie le premier échantillon disponible en entrée)&lt;br /&gt;
# changer &amp;lt;code&amp;gt;return noutput_items;&amp;lt;/code&amp;gt; en &amp;lt;code&amp;gt;return 1;&amp;lt;/code&amp;gt; (un seul échantillon a été consommé).&lt;br /&gt;
&lt;br /&gt;
==== Fichier &amp;lt;code&amp;gt;monModule_passetout.block.yml&amp;lt;/code&amp;gt; ====&lt;br /&gt;
&lt;br /&gt;
Ce fichier au format YAML (''yet another markup language'') fait le lien entre le code c++ et le code python utilisé en interne par GnuRadio.&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=monModule_passetout.block.yml}}&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
id: monModule_passetout&lt;br /&gt;
label: passetout&lt;br /&gt;
category: '[monModule]'&lt;br /&gt;
&lt;br /&gt;
templates:&lt;br /&gt;
  imports: from gnuradio import monModule&lt;br /&gt;
  make: monModule.passetout()&lt;br /&gt;
&lt;br /&gt;
#  Make one 'parameters' list entry for every parameter you want settable from the GUI.&lt;br /&gt;
#     Keys include:&lt;br /&gt;
#     * id (makes the value accessible as keyname, e.g. in the make entry)&lt;br /&gt;
#     * label (label shown in the GUI)&lt;br /&gt;
#     * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)&lt;br /&gt;
#     * default&lt;br /&gt;
parameters:&lt;br /&gt;
- id: parametername_replace_me&lt;br /&gt;
  label: FIX ME:&lt;br /&gt;
  dtype: string&lt;br /&gt;
  default: You need to fill in your grc/monModule_passetout.block.yaml&lt;br /&gt;
#- id: ...&lt;br /&gt;
#  label: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
&lt;br /&gt;
#  Make one 'inputs' list entry per input and one 'outputs' list entry per output.&lt;br /&gt;
#  Keys include:&lt;br /&gt;
#      * label (an identifier for the GUI)&lt;br /&gt;
#      * domain (optional - stream or message. Default is stream)&lt;br /&gt;
#      * dtype (e.g. int, float, complex, byte, short, xxx_vector, ...)&lt;br /&gt;
#      * vlen (optional - data stream vector length. Default is 1)&lt;br /&gt;
#      * optional (optional - set to 1 for optional inputs. Default is 0)&lt;br /&gt;
inputs:&lt;br /&gt;
#- label: ...&lt;br /&gt;
#  domain: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
#  vlen: ...&lt;br /&gt;
#  optional: ...&lt;br /&gt;
&lt;br /&gt;
outputs:&lt;br /&gt;
#- label: ...&lt;br /&gt;
#  domain: ...&lt;br /&gt;
#  dtype: ...&lt;br /&gt;
#  vlen: ...&lt;br /&gt;
#  optional: ...&lt;br /&gt;
&lt;br /&gt;
#  'file_format' specifies the version of the GRC yml format used in the file&lt;br /&gt;
#  and should usually not be changed.&lt;br /&gt;
file_format: 1&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;parameters&amp;lt;/code&amp;gt; (pas de paramètres, on commente) :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
#parameters:&lt;br /&gt;
#- id: parametername_replace_me&lt;br /&gt;
#  label: FIX ME:&lt;br /&gt;
#  dtype: string&lt;br /&gt;
#  default: You need to fill in your grc/customModule_testMod.block.yaml&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;inputs&amp;lt;/code&amp;gt; :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
inputs:&lt;br /&gt;
- label: in&lt;br /&gt;
  domain: stream&lt;br /&gt;
  dtype: float&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Todos| Mettre à jour la section &amp;lt;code&amp;gt;output&amp;lt;/code&amp;gt; :}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=yaml&amp;gt;&lt;br /&gt;
outputs:&lt;br /&gt;
- label: out&lt;br /&gt;
  domain: stream&lt;br /&gt;
  dtype: float&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Compilation et installation ====&lt;br /&gt;
&lt;br /&gt;
Voici les étapes :&lt;br /&gt;
&amp;lt;source lang=bash&amp;gt;&lt;br /&gt;
$ cd CHEMIN-DE-VOTRE-DOSSIER/gr-monModule&lt;br /&gt;
$ mkdir build&lt;br /&gt;
$ cd build&lt;br /&gt;
$ cmake ..&lt;br /&gt;
$ make&lt;br /&gt;
$ make install&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;cmake ..&amp;lt;/code&amp;gt; lance la création des fichiers &amp;lt;code&amp;gt;Makefile&amp;lt;/code&amp;gt; qui permettent la compilation et l'installation&lt;br /&gt;
* &amp;lt;code&amp;gt;make&amp;lt;/code&amp;gt; lance la compilation&lt;br /&gt;
* &amp;lt;code&amp;gt;make install&amp;lt;/code&amp;gt; lance l'installation (copie des fichiers dans les dossiers gnuradio)&lt;br /&gt;
&lt;br /&gt;
{{Todos| Exécuter chacune de ces lignes, en contrôlant l'absence d'erreur. Dans le cas contraire, contrôler les fichiers &amp;lt;code&amp;gt;.h&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.cc&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;.yaml&amp;lt;/code&amp;gt;}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| '''Après ces étapes, et avant de lancer gnuRadio, appeler l'enseignant qui va mettre à jours les bibliothèques partagées.'''}}. Sinon tenter :&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
$ export LD_LIBRARY_PATH=/usr/local/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH&lt;br /&gt;
$ gnuradio-companion &amp;amp;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Utilisation du bloc et test ====&lt;br /&gt;
&lt;br /&gt;
* Dans Gnuradio, rafraichir la liste des bloc (bouton ''reload'')&lt;br /&gt;
* le bloc &amp;lt;code&amp;gt;passetout&amp;lt;/code&amp;gt; doit être présent dans la section &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester le bon fonctionnement de ce bloc en lui envoyant une sinusoïde que vous devez observer identique à la sortie du bloc. Vérifier avec des graphes.&lt;br /&gt;
&lt;br /&gt;
=== Démodulation ===&lt;br /&gt;
&lt;br /&gt;
En reprenant le signal modulé en amplitude et la fonction de transfert du filtre passe-bas du TP précédent :&lt;br /&gt;
&lt;br /&gt;
* Obtenir l'équation aux différence du filtre, ''ie'' écrire ''y(n)='' en fonction de ''x(n)'', ''x(n-1)'', ''x(n-2)'', ''y(n-1)'' et ''y(n-2)''.&lt;br /&gt;
* Créer un nouveau bloc C++ (similaire au passe-tout précédent) dans le module &amp;lt;code&amp;gt;monModule&amp;lt;/code&amp;gt; :&lt;br /&gt;
** de type &amp;lt;code&amp;gt;sync&amp;lt;/code&amp;gt;&lt;br /&gt;
** une entrée en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;&lt;br /&gt;
** une sortie en &amp;lt;code&amp;gt;float&amp;lt;/code&amp;gt;&lt;br /&gt;
* Écrire le code permettant d'obtenir ''y(n)'' dans la méthode &amp;lt;code&amp;gt;work()&amp;lt;/code&amp;gt; :&lt;br /&gt;
** ''x(n)'' est donné par &amp;lt;code&amp;gt;in0[0]&amp;lt;/code&amp;gt;&lt;br /&gt;
** il sera nécessaire d'affecter &amp;lt;code&amp;gt;out0[0]&amp;lt;/code&amp;gt; pour produire ''y(n)''.&lt;br /&gt;
** il sera nécessaire de stocker dans des '''attributs''' les valeurs de &amp;lt;code&amp;gt;in[0]&amp;lt;/code&amp;gt; afin de disposer de ''x(n-1)'', ''x(n-2)'', ''y(n-1)'' et ''y(n-2)''.&lt;br /&gt;
** On rappelle que les attributs doivent être déclarées dans le fichier &amp;lt;code&amp;gt;.h&amp;lt;/code&amp;gt; et initialisés dans le constructeur de la classe.&lt;br /&gt;
&lt;br /&gt;
{{Todos| Réaliser cette implémentation du passe-bas et contrôler que la démodulation est identique à celle obtenue au TP précédent}}&lt;br /&gt;
&lt;br /&gt;
{{Todos| Modifier votre code pour implémenter le filtre complet de démodulation}}&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20961</id>
		<title>Cours:Elen4 TNS</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20961"/>
				<updated>2026-05-04T14:51:58Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Travaux Pratiques */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;S4 - ESE - Traitement numérique du signal&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Description générale ==&lt;br /&gt;
&lt;br /&gt;
=== Contenu (Programme National) ===&lt;br /&gt;
&lt;br /&gt;
Filtrage numérique :&lt;br /&gt;
* Équation de récurrence, opérations élémentaires, fonction de transfert en Z, passage à un programme informatique;&lt;br /&gt;
* Filtre numérique : ﬁltres récursif et non récursif, réponses temporelles (impulsionnelle, indicielle, signal quelconque), réponse fréquentielle, stabilité;&lt;br /&gt;
* Synthèse de ﬁltres numériques simples (approximation d’Euler);&lt;br /&gt;
* Utilisation d’outils de synthèse des ﬁltres numériques RIF et RII (Matlab, Labview, GNU radio...) et comparaison des performances des ﬁltres RIF et RII;&lt;br /&gt;
* Présentation des différentes structures des ﬁltres numériques (direct, cascade, parallèle);&lt;br /&gt;
* Implantation d’un traitement numérique sur cible : ﬁltrage numérique simple, asservissement numérique simple (robot mobile, drone...), traitement de données.&lt;br /&gt;
&lt;br /&gt;
=== Volumes horaire ===&lt;br /&gt;
&lt;br /&gt;
* CM : 4 séances d'1,5h&lt;br /&gt;
* TD : 6 séances d'1,5h + une évaluation d'1,5h&lt;br /&gt;
* TP : 6 séances de 3h + une évaluation de 2h&lt;br /&gt;
&lt;br /&gt;
=== Évaluations ===&lt;br /&gt;
&lt;br /&gt;
* une évaluation sur table (derniere séance de CM/TD - 1,5h)&lt;br /&gt;
* une évaluation pratique (dernière séance de TP - 2h)&lt;br /&gt;
&lt;br /&gt;
== Cours Magistraux ==&lt;br /&gt;
&lt;br /&gt;
Contenu :&lt;br /&gt;
* Signaux et systèmes numériques&lt;br /&gt;
* Filtres numériques&lt;br /&gt;
* Quelques filtres RIF&lt;br /&gt;
* Synthèse de filtres numériques&lt;br /&gt;
* PID numérique &lt;br /&gt;
* Élements sur le traitement des images numériques&lt;br /&gt;
* Élements sur le temps-fréquence&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Documents :&lt;br /&gt;
* [[Media:TNS-Support.pdf|Support de cours en pdf]]&lt;br /&gt;
* [[Media:TNS-diapos.pdf|Diapositives de cours en pdf]]&lt;br /&gt;
&lt;br /&gt;
== Travaux Dirigés ==&lt;br /&gt;
&lt;br /&gt;
Textes en pdf :&lt;br /&gt;
&lt;br /&gt;
* [[Media:TNS-td1.FxTransfert.pdf|TD1 : Fonctions de transfert]]&lt;br /&gt;
* [[Media:TNS-td2.Stabilité.pdf|TD2 : Causabilité et stabilité]]&lt;br /&gt;
* [[Media:TNS-td3.RepFreq.pdf|TD3 : Réponses fréquentielles]]&lt;br /&gt;
* [[Media:TNS-td4.Facteurs1et2.pdf|TD4 : Facteurs du premier et du second ordre]]&lt;br /&gt;
* [[Media:TNS-td5-Structures.pdf|TD5 : Structures de réalisation]]&lt;br /&gt;
* [[Media:TNS-td6-ConceptionFiltresRII.pdf|TD6 : Conception de filtres récursifs]]&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Les TP utiliserons principalement :&lt;br /&gt;
* Gnuradio (cf premier TP)&lt;br /&gt;
* Matlab, que vous pourrez utiliser en ligne avec votre compte universitaire : [https://fr.mathworks.com/products/matlab-online.html Matlab online]&lt;br /&gt;
* et occasionnellement des cartes Arduinio ou Rpi Pico.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Liste des TP :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Gnuradio|TP1 - Fondamentaux Gnuradio]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_FxTransfertStabilité|TP2 - Fonction de transfert et stabilité]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Implementations|TP3 - Implémentations de filtres]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitImage|TP4 - Traitements d'images numériques]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitAudioEtDemodulation| TP5 - Traitements audio et démodulation]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_ TpsReel |TP6 - Temps réel -  Codage d'un bloc en Python]]&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Évaluation de Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Énoncés :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_eval1|Sujet n°1]]&lt;br /&gt;
&lt;br /&gt;
Documents autorisés :&lt;br /&gt;
* accès au wiki&lt;br /&gt;
* documents de cours&lt;br /&gt;
* notes personnelles&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20960</id>
		<title>Cours:Elen4 TNS</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Elen4_TNS&amp;diff=20960"/>
				<updated>2026-05-04T14:51:50Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Travaux Pratiques */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;S4 - ESE - Traitement numérique du signal&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Description générale ==&lt;br /&gt;
&lt;br /&gt;
=== Contenu (Programme National) ===&lt;br /&gt;
&lt;br /&gt;
Filtrage numérique :&lt;br /&gt;
* Équation de récurrence, opérations élémentaires, fonction de transfert en Z, passage à un programme informatique;&lt;br /&gt;
* Filtre numérique : ﬁltres récursif et non récursif, réponses temporelles (impulsionnelle, indicielle, signal quelconque), réponse fréquentielle, stabilité;&lt;br /&gt;
* Synthèse de ﬁltres numériques simples (approximation d’Euler);&lt;br /&gt;
* Utilisation d’outils de synthèse des ﬁltres numériques RIF et RII (Matlab, Labview, GNU radio...) et comparaison des performances des ﬁltres RIF et RII;&lt;br /&gt;
* Présentation des différentes structures des ﬁltres numériques (direct, cascade, parallèle);&lt;br /&gt;
* Implantation d’un traitement numérique sur cible : ﬁltrage numérique simple, asservissement numérique simple (robot mobile, drone...), traitement de données.&lt;br /&gt;
&lt;br /&gt;
=== Volumes horaire ===&lt;br /&gt;
&lt;br /&gt;
* CM : 4 séances d'1,5h&lt;br /&gt;
* TD : 6 séances d'1,5h + une évaluation d'1,5h&lt;br /&gt;
* TP : 6 séances de 3h + une évaluation de 2h&lt;br /&gt;
&lt;br /&gt;
=== Évaluations ===&lt;br /&gt;
&lt;br /&gt;
* une évaluation sur table (derniere séance de CM/TD - 1,5h)&lt;br /&gt;
* une évaluation pratique (dernière séance de TP - 2h)&lt;br /&gt;
&lt;br /&gt;
== Cours Magistraux ==&lt;br /&gt;
&lt;br /&gt;
Contenu :&lt;br /&gt;
* Signaux et systèmes numériques&lt;br /&gt;
* Filtres numériques&lt;br /&gt;
* Quelques filtres RIF&lt;br /&gt;
* Synthèse de filtres numériques&lt;br /&gt;
* PID numérique &lt;br /&gt;
* Élements sur le traitement des images numériques&lt;br /&gt;
* Élements sur le temps-fréquence&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Documents :&lt;br /&gt;
* [[Media:TNS-Support.pdf|Support de cours en pdf]]&lt;br /&gt;
* [[Media:TNS-diapos.pdf|Diapositives de cours en pdf]]&lt;br /&gt;
&lt;br /&gt;
== Travaux Dirigés ==&lt;br /&gt;
&lt;br /&gt;
Textes en pdf :&lt;br /&gt;
&lt;br /&gt;
* [[Media:TNS-td1.FxTransfert.pdf|TD1 : Fonctions de transfert]]&lt;br /&gt;
* [[Media:TNS-td2.Stabilité.pdf|TD2 : Causabilité et stabilité]]&lt;br /&gt;
* [[Media:TNS-td3.RepFreq.pdf|TD3 : Réponses fréquentielles]]&lt;br /&gt;
* [[Media:TNS-td4.Facteurs1et2.pdf|TD4 : Facteurs du premier et du second ordre]]&lt;br /&gt;
* [[Media:TNS-td5-Structures.pdf|TD5 : Structures de réalisation]]&lt;br /&gt;
* [[Media:TNS-td6-ConceptionFiltresRII.pdf|TD6 : Conception de filtres récursifs]]&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Les TP utiliserons principalement :&lt;br /&gt;
* Gnuradio (cf premier TP)&lt;br /&gt;
* Matlab, que vous pourrez utiliser en ligne avec votre compte universitaire : [https://fr.mathworks.com/products/matlab-online.html Matlab online]&lt;br /&gt;
* et occasionnellement des cartes Arduinio ou Rpi Pico.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
Liste des TP :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Gnuradio|TP1 - Fondamentaux Gnuradio]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_FxTransfertStabilité|TP2 - Fonction de transfert et stabilité]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_Implementations|TP3 - Implémentations de filtres]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitImage|TP4 - Traitements d'images numériques]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_TraitAudioEtDemodulation| TP5 - Traitements audio et démodulation]]&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_ TpsReel |TP6 - Temps réel -  Codage d'un bloc en Python]] --&amp;gt;&lt;br /&gt;
* Évaluation&lt;br /&gt;
&lt;br /&gt;
== Évaluation de Travaux Pratiques ==&lt;br /&gt;
&lt;br /&gt;
Énoncés :&lt;br /&gt;
* [[Cours:Elen4_TNS_TP_eval1|Sujet n°1]]&lt;br /&gt;
&lt;br /&gt;
Documents autorisés :&lt;br /&gt;
* accès au wiki&lt;br /&gt;
* documents de cours&lt;br /&gt;
* notes personnelles&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20959</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20959"/>
				<updated>2026-04-29T14:39:51Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Avec estimation de confiance ET hiérarchie */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules (pour les hiérarchiser), par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;poids_lidar + poids_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = poids_lidar&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = poids_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de chaque mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
La confiance peut être estimée à partir de la distance, ou bien de la largeur de la réponse.&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ET hiérarchie ===&lt;br /&gt;
&lt;br /&gt;
On peut évidemment combiner les deux. On aurait donc :&lt;br /&gt;
* Une pondération générale des deux modules :&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
* Une confiance pour chaque mesures :&lt;br /&gt;
** &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
* Et évidemment nos deux mesures :&lt;br /&gt;
** &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; &lt;br /&gt;
** &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit alors de calculer des score en multipliant les poids (et en s'assurant que la somme des scores vaut 1) :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = lidar.confiance*poids_lidar/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = pixi.confiance*poids_pixi/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La feuille de calcul suivante illustre cette fusion, vous pouvez la télécharger pour tester : https://docs.google.com/spreadsheets/d/16FwxqFmsJsAHjfXUKSS-HSLFPvuHmSxn/edit?usp=sharing&amp;amp;ouid=103791012157110322336&amp;amp;rtpof=true&amp;amp;sd=true&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20958</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20958"/>
				<updated>2026-04-29T14:37:44Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Avec estimation de confiance ET hiérarchie */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules (pour les hiérarchiser), par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;poids_lidar + poids_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = poids_lidar&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = poids_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de chaque mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
La confiance peut être estimée à partir de la distance, ou bien de la largeur de la réponse.&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ET hiérarchie ===&lt;br /&gt;
&lt;br /&gt;
On peut évidemment combiner les deux. On aurait donc :&lt;br /&gt;
* Une pondération générale des deux modules :&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
* Une confiance pour chaque mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
* Et évidemment nos deux mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit alors de calculer des score en multipliant les poids (et en s'assurant que la somme des scores vaut 1) :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = lidar.confiance*poids_lidar/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = pixi.confiance*poids_pixi/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La feuille de calcul suivante illustre cette fusion, vous pouvez la télécharger pour tester : https://docs.google.com/spreadsheets/d/16FwxqFmsJsAHjfXUKSS-HSLFPvuHmSxn/edit?usp=sharing&amp;amp;ouid=103791012157110322336&amp;amp;rtpof=true&amp;amp;sd=true&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20957</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20957"/>
				<updated>2026-04-29T14:36:19Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Avec estimation de confiance ET hiérarchie */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules (pour les hiérarchiser), par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;poids_lidar + poids_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = poids_lidar&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = poids_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de chaque mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
La confiance peut être estimée à partir de la distance, ou bien de la largeur de la réponse.&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ET hiérarchie ===&lt;br /&gt;
&lt;br /&gt;
On peut évidemment combiner les deux. On aurait donc :&lt;br /&gt;
* Une pondération générale des deux modules :&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
* Une confiance pour chaque mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
* Et évidemment nos deux mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit alors de calculer des score en multipliant les poids (et en s'assurant que la somme des scores vaut 1) :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = lidar.confiance*poids_lidar/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = pixi.confiance*poids_pixi/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La feuille de calcul suivante illustre cette fusion : https://docs.google.com/spreadsheets/d/16FwxqFmsJsAHjfXUKSS-HSLFPvuHmSxn/edit?usp=sharing&amp;amp;ouid=103791012157110322336&amp;amp;rtpof=true&amp;amp;sd=true&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20956</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20956"/>
				<updated>2026-04-29T14:36:06Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Avec estimation de confiance ET hiérarchie = */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules (pour les hiérarchiser), par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;poids_lidar + poids_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = poids_lidar&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = poids_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de chaque mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
La confiance peut être estimée à partir de la distance, ou bien de la largeur de la réponse.&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Avec estimation de confiance ET hiérarchie ==&lt;br /&gt;
&lt;br /&gt;
On peut évidemment combiner les deux. On aurait donc :&lt;br /&gt;
* Une pondération générale des deux modules :&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
* Une confiance pour chaque mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
* Et évidemment nos deux mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit alors de calculer des score en multipliant les poids (et en s'assurant que la somme des scores vaut 1) :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = lidar.confiance*poids_lidar/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = pixi.confiance*poids_pixi/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La feuille de calcul suivante illustre cette fusion : https://docs.google.com/spreadsheets/d/16FwxqFmsJsAHjfXUKSS-HSLFPvuHmSxn/edit?usp=sharing&amp;amp;ouid=103791012157110322336&amp;amp;rtpof=true&amp;amp;sd=true&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20955</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20955"/>
				<updated>2026-04-29T14:30:23Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules (pour les hiérarchiser), par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;poids_lidar + poids_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = poids_lidar&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = poids_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de confiance ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de chaque mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
La confiance peut être estimée à partir de la distance, ou bien de la largeur de la réponse.&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Avec estimation de confiance ET hiérarchie ===&lt;br /&gt;
&lt;br /&gt;
On peut évidemment combiner les deux. On aurait donc :&lt;br /&gt;
* Une pondération générale des deux modules :&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
** &amp;lt;code&amp;gt;poids_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
* Une confiance pour chaque mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
* Et évidemment nos deux mesures :&lt;br /&gt;
* &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; &lt;br /&gt;
* &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Il suffit alors de calculer des score en multipliant les poids (et en s'assurant que la somme des scores vaut 1) :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = lidar.confiance*poids_lidar/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = pixi.confiance*poids_pixi/(lidar.confiance*poids_lidar + pixi.confiance*poids_pixi)&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20954</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20954"/>
				<updated>2026-04-29T14:14:03Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules, pour calculer un angle unique de cap à suivre.&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules, par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;score_lidar + score_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; * &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; * &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de fiabilité ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de la mesure, chaque module donne un angle et une confiance (entre 0 et 1). Donc par exemple:&lt;br /&gt;
* Lidar : &amp;lt;code&amp;gt;lidar.angle=10&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;lidar.confiance=0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
* Pixi : &amp;lt;code&amp;gt;pixi.angle=-5&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;pixi.confiance=0.3&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut donc faire également la somme pondérée des deux angles. Il faut s'assurer que la somme des scores vaut 1. Donc :&lt;br /&gt;
*&amp;lt;code&amp;gt;score_lidar = lidar.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;score_pixi = pixi.confiance/(lidar.confiance + pixi.confiance)&amp;lt;/code&amp;gt;&lt;br /&gt;
*&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20953</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20953"/>
				<updated>2026-04-29T14:07:47Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules&lt;br /&gt;
&lt;br /&gt;
=== Cas simple : pondération générale ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules, par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;score_lidar + score_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* On pourrait aussi décider que les poids changent en fonction de la situation. Par exemple :&lt;br /&gt;
** distance faible (infériure à un seuil) =&amp;gt; * &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
** distance élevée (supérieure au seuil) =&amp;gt; * &amp;lt;code&amp;gt;score_lidar = 0.3&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;score_pixi = 0.7&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Avec estimation de fiabilité ===&lt;br /&gt;
&lt;br /&gt;
Si on arrive à estimer une fiabilité de la mesure, chaque module&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20952</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20952"/>
				<updated>2026-04-29T14:02:50Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules&lt;br /&gt;
&lt;br /&gt;
=== Cas simple ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules, par exemple :&lt;br /&gt;
* &amp;lt;code&amp;gt;score_lidar = 0.6&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;score_pixi = 0.4&amp;lt;/code&amp;gt;&lt;br /&gt;
Ici on décide de privilégier la décision du lidar par rapport à celle de la caméra.&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;score_lidar + score_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20951</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20951"/>
				<updated>2026-04-29T14:01:55Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux modules founissent un angle qui indique le cap à suivre&lt;br /&gt;
* il s'agit donc de fusionner la décision de ces deux modules&lt;br /&gt;
&lt;br /&gt;
=== Cas simple ===&lt;br /&gt;
&lt;br /&gt;
On décide d'un poids général pour chacun des modules, par exemple :&lt;br /&gt;
* score_lidar = 0.6&lt;br /&gt;
* score_pixi = 0.4&lt;br /&gt;
&lt;br /&gt;
Il faut s'assurer que &amp;lt;code&amp;gt;score_lidar + score_pixi = 1&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
La fusion est la somme pondérée des deux décisions (donc des deux angles). Soit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;angle_final = lidar.angle * score_lidar + pixi.angle * score_pixi&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20950</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20950"/>
				<updated>2026-04-29T13:58:35Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Exemple concret */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;br /&gt;
&lt;br /&gt;
Prenons le Lidar et la caméra Pixi :&lt;br /&gt;
* ces deux détecteurs founissent un angle&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20949</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20949"/>
				<updated>2026-04-29T13:56:57Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme général ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Agrégation et Décision ===&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
=== Choix des poids ===&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
=== Point de vigilance ===&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;br /&gt;
&lt;br /&gt;
== Exemple concret ==&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20948</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20948"/>
				<updated>2026-04-29T09:40:29Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Agrégation et Décision */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ_i (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20947</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20947"/>
				<updated>2026-04-28T06:24:39Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Mécanisme du vote */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop, s_reculer]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20946</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20946"/>
				<updated>2026-04-28T06:24:17Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Mécanisme du vote */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20945</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20945"/>
				<updated>2026-04-27T13:08:06Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Mécanisme du vote */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 2 : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20944</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20944"/>
				<updated>2026-04-27T13:07:58Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Mécanisme du vote */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple 1 : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** Exemple2  : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20943</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20943"/>
				<updated>2026-04-27T13:07:19Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;Fusion de décision multicapteurs, par vote pondéré&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20942</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20942"/>
				<updated>2026-04-27T13:06:23Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Fusion de décision multicapteurs, par vote pondéré =&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20941</id>
		<title>Cours:SaeFusionDecision</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeFusionDecision&amp;diff=20941"/>
				<updated>2026-04-27T13:06:07Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : Page créée avec « == Mécanisme du vote pondéré pour la fusion de décisions ==  = Fusion de décision multicapteurs, par vote pondéré =  Le '''vote pondéré''' est particulièrement a... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Mécanisme du vote pondéré pour la fusion de décisions ==&lt;br /&gt;
&lt;br /&gt;
= Fusion de décision multicapteurs, par vote pondéré =&lt;br /&gt;
&lt;br /&gt;
Le '''vote pondéré''' est particulièrement adapté à une architecture multi-capteurs car il permet de traduire une hiérarchie de fiabilité tout en restant déterministe et interprétable, en restant léger en calculs&lt;br /&gt;
&lt;br /&gt;
== Mécanisme du vote ==&lt;br /&gt;
&lt;br /&gt;
Chaque module de perception (issu d'un capteur) doit renvoyer plus qu'une donnée brute, mais plutôt un '''vecteur de décision''' ou un score pour chaque action possible.&lt;br /&gt;
&lt;br /&gt;
Par exemple :&lt;br /&gt;
* '''Actions possibles''' : &amp;lt;code&amp;gt;Avancer&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Gauche&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Tourner_Droite&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Reculer&amp;lt;/code&amp;gt;.&lt;br /&gt;
* '''Sortie du capteur i''' : Un vecteur de scores &amp;lt;code&amp;gt;S_i = [s_avancer, s_gauche, s_droite, s_stop]&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : Le LIDAR voit un mur à 2m devant : &amp;lt;code&amp;gt;Stop: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Gauche: 0.8&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Droite: 0.1&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Avancer: 0.0&amp;lt;/code&amp;gt;.&lt;br /&gt;
** ''Exemple'' : La caméra voit la voie libre : &amp;lt;code&amp;gt;Avancer: 0.9&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Stop: 0.1&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Agrégation et Décision ==&lt;br /&gt;
&lt;br /&gt;
Le score final pour chaque action est la somme pondérée :&lt;br /&gt;
&lt;br /&gt;
'''Score_final(action) = Σ (W_i × s_action)''' pour i allant de 1 à N (N = nb de capteurs).&lt;br /&gt;
&lt;br /&gt;
Décision :&lt;br /&gt;
* L'action avec le score final le plus élevé est choisie, éventuellement à condition qu'elle dépasse un seuil de validité minimal (pour éviter de bouger si tous les capteurs sont incertains).&lt;br /&gt;
* Il est possible de mettre en place une sécurité. Par exemple Si le score &amp;lt;code&amp;gt;Stop_Urgence&amp;lt;/code&amp;gt; des capteurs de proximité (Ultrason/IR) dépasse un seuil critique (ex: 0.95), l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; est exécutée immédiatement, quel que soit le vote des autres.&lt;br /&gt;
&lt;br /&gt;
Filtrage :&lt;br /&gt;
* Pour éviter les oscillations (zigzag) si les scores sont proches, appliquez un lissage temporel&lt;br /&gt;
&lt;br /&gt;
== Choix des poids ==&lt;br /&gt;
&lt;br /&gt;
Les poids '''W_i''' peuvent être dynamiques et dépendre du contexte. Par exemple :&lt;br /&gt;
&lt;br /&gt;
'''W_i = C_base × F_contexte × F_confiance'''&lt;br /&gt;
&lt;br /&gt;
* '''C_base (Hiérarchie statique)''' : Priorité intrinsèque à décider en amont. Par exemple :&lt;br /&gt;
** ''Ultrason/IR'' : Poids très élevé pour l'action &amp;lt;code&amp;gt;Stop&amp;lt;/code&amp;gt; (sécurité physique prioritaire).&lt;br /&gt;
** ''LIDAR'' : Poids élevé pour la navigation globale.&lt;br /&gt;
** ''Caméra'' : Poids moyen (sensible à la lumière).&lt;br /&gt;
* '''F_contexte (Fiabilité environnementale)''' : Priorité qui peut dépendre de l'environnement, d'autres capteurs, ... Par exemple&lt;br /&gt;
** Si luminosité &amp;lt; seuil → Poids Caméra ≈ 0.&lt;br /&gt;
** Si surface absorbante (tapis noir) → Poids Ultrason/IR réduit.&lt;br /&gt;
** Si brouillard/poussière → Poids LIDAR/Caméra réduit.&lt;br /&gt;
* '''F_confiance (Auto-évaluation)''' : &lt;br /&gt;
** Chaque élément de détection peut renvoyer son propre score de confiance (ex: probabilité de la détection d'obstacle). Par exemple si la détection de la caméra est incertainte (confiance 0.4), son vote est automatiquement pondéré à la baisse.&lt;br /&gt;
&lt;br /&gt;
== Point de vigilance ==&lt;br /&gt;
&lt;br /&gt;
Il faut prévoir un cas par défaut si le vote est parfaitement équilibré ou si le score maximal est trop faible (incertitude globale trop haute). La décision par défaut doit être conservative : '''Arrêt sécurisé''' ou '''Recul prudent'''.&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeEvitementObstacles&amp;diff=20940</id>
		<title>Cours:SaeEvitementObstacles</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:SaeEvitementObstacles&amp;diff=20940"/>
				<updated>2026-04-27T12:48:31Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Fonctions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;https://docs.google.com/document/d/1lLrXOisqDBmTie8lAPcVor_al1TS7HhtI7Or8wFLcsg/edit?tab=t.0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
support de présentation :&lt;br /&gt;
 https://docs.google.com/presentation/d/1r9tSETGUbYgtk2Ip78lNhEWxcHYtDbIx4epwVoeI2hs/edit?slide=id.p#slide=id.p&lt;br /&gt;
&lt;br /&gt;
fonctions :&lt;br /&gt;
 https://docs.google.com/spreadsheets/d/1GX3zPpdC4umtqc4B8hLjQZWTMNHfw7GfKqrpxW5ORzc/edit?usp=sharing&lt;br /&gt;
&lt;br /&gt;
https://www.festivalrobotiquecachan.fr/&lt;br /&gt;
&lt;br /&gt;
https://www.festivalrobotiquecachan.fr/wp-content/uploads/Reglement_rencontres_de_robotique_GEII_BUT-1-2-3.pdf&lt;br /&gt;
&lt;br /&gt;
=Découpage fonctionnel=&lt;br /&gt;
&lt;br /&gt;
[[Media:DecoupageFcts2026.pdf]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Travail à réaliser=&lt;br /&gt;
&lt;br /&gt;
==1ère partie (2 semaines)==&lt;br /&gt;
*travail individuel&lt;br /&gt;
**travail à réaliser pour fin de la 2ème semaine&lt;br /&gt;
**évaluation à la fin de la 2ème semaine (schéma/routage)&lt;br /&gt;
**sélection de la meilleure carte pour fabrication&lt;br /&gt;
***finalisation des pcbs pour le {{Rouge|jeudi 3/04 matin}}&lt;br /&gt;
*cartes :&lt;br /&gt;
**balise émettrice&lt;br /&gt;
***choix de la fréquence&lt;br /&gt;
**balise de réglage&lt;br /&gt;
***à l'opposé du terrain&lt;br /&gt;
***permet de régler l'élévation de la balise émettrice&lt;br /&gt;
***affiche le niveau de puissance&lt;br /&gt;
**carte réceptrice IR (filtre)&lt;br /&gt;
**carte filtrage (se plug sur la carte réceptrice IR)&lt;br /&gt;
***on change de carte pour changer la fréquence&lt;br /&gt;
**carte µc&lt;br /&gt;
***connectique pour e/s&lt;br /&gt;
***driver moteurs&lt;br /&gt;
***fourni alim symétrique&lt;br /&gt;
**banc test pour la carte filtre&lt;br /&gt;
**banc test pour la carte réceptrice complète&lt;br /&gt;
&lt;br /&gt;
==2ème partie (xx jours)==&lt;br /&gt;
&lt;br /&gt;
*Travail individuel&lt;br /&gt;
*montage des cartes&lt;br /&gt;
*vérification du fonctionnement&lt;br /&gt;
*réalisation programme : fonction à réaliser selon cdc&lt;br /&gt;
&lt;br /&gt;
==3ème partie (xx jours)==&lt;br /&gt;
&lt;br /&gt;
*travail en binôme ?&lt;br /&gt;
*programmation d'un robot&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=Modalités d'évaluation=&lt;br /&gt;
&lt;br /&gt;
==1 soutenance==&lt;br /&gt;
*Vendredi 17/05 matin&lt;br /&gt;
**20 minutes de présentation&lt;br /&gt;
**10 minutes de questions&lt;br /&gt;
*Présentation(introduction) de chaque &amp;quot;partie&amp;quot; en anglais&lt;br /&gt;
&lt;br /&gt;
==1 dossier==&lt;br /&gt;
*Analyse fonctionnelle&lt;br /&gt;
*Nomenclature&lt;br /&gt;
*Chiffrage&lt;br /&gt;
*Etude détaillée de chaque fonction&lt;br /&gt;
*Schémas électriques/algorithmes/simulations/courbes caractéristiques/fonctions de transfert ...&lt;br /&gt;
&lt;br /&gt;
==Démonstration(s)==&lt;br /&gt;
&lt;br /&gt;
* Démonstration du fonctionnement le 12/05 à 12h&lt;br /&gt;
* Participation au festival de robotique à Cachan&lt;br /&gt;
&lt;br /&gt;
==Note de résultat - Evaluation travaux de SAE==&lt;br /&gt;
*Groupe : accomplissement du projet&lt;br /&gt;
*Individuelle : en fonction de &lt;br /&gt;
**Difficulté technique&lt;br /&gt;
**Quantité de travail&lt;br /&gt;
**Qualité de la réalisation&lt;br /&gt;
**Investissement : &amp;lt;big&amp;gt;évalué chaque jour&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Réalisation d'une vidéo==&lt;br /&gt;
Vidéo à réaliser pour le concours EEA :&lt;br /&gt;
 https://clubeea.com/concours-mon-projet-en-5-minutes/&lt;br /&gt;
&lt;br /&gt;
=Fonctions=&lt;br /&gt;
[[Cours:SaeEvitementObstaclesFonctions]]&lt;br /&gt;
&lt;br /&gt;
[[Cours:SaeFusionDecision]]&lt;br /&gt;
&lt;br /&gt;
=Pages indispensables du wiki=&lt;br /&gt;
&lt;br /&gt;
*[[Cours:Oscillogbf|Utilisation de python et Qt pour la réalisation d'un banc de test]]&lt;br /&gt;
*[[Cours:PiPico|Programmation de la carte pico rp2040]]&lt;br /&gt;
*[[Cours:Esp01|Utilisation d'un esp-01 pour faire de la supervision]]&lt;br /&gt;
&lt;br /&gt;
=Ressources informatique=&lt;br /&gt;
&lt;br /&gt;
*[[Cours:pixyUartArduino|Pixy]]&lt;br /&gt;
*https://www.waveshare.com/wiki/RPLIDAR_C1&lt;br /&gt;
&lt;br /&gt;
=Ressources=&lt;br /&gt;
&lt;br /&gt;
*https://www.eeweb.com/level-shifting-techniques-in-i2c-bus-design/&lt;br /&gt;
*https://github.com/drankinatty/pico-mpu9250&lt;br /&gt;
&lt;br /&gt;
*vérifier la &amp;quot;programmabilité&amp;quot; du µcontroleur :&lt;br /&gt;
**dans un terminal&lt;br /&gt;
**avrdude -c usbasp -p nomDuMicro&lt;br /&gt;
**ex : avrdude -c usbasp -p t2313&lt;br /&gt;
**ex : avrdude -c usbasp -p m328p&lt;br /&gt;
&lt;br /&gt;
*modification des fusibles pour choisir fréquence d'horloge:&lt;br /&gt;
**pour les atmega328p, dans le logiciel graver le bootloader&lt;br /&gt;
**pour les attiny (utiliser '''arduino v1''' !! ) :&lt;br /&gt;
***installer attinycore : https://github.com/SpenceKonde/ATTinyCore/blob/v2.0.0-devThis-is-the-head-submit-PRs-against-this/Installation.md&lt;br /&gt;
***choisir le bon microcontroleur et la bonne source d'horloge&lt;br /&gt;
***graver la séquence d'initialisation&lt;br /&gt;
**pour les atmega2560, attention il faut modifier les fusibles par rapport au bootloader arduino :&lt;br /&gt;
***dans un terminal&lt;br /&gt;
***avrdude -v -patmega2560 -cusbasp -e -Ulock:w:0x3F:m -Uefuse:w:0xFD:m -Uhfuse:w:0xD9:m -Ulfuse:w:0xFF:m&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*http://raphael.candelier.fr/?blog=XL_320&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=contraintes de fabrication=&lt;br /&gt;
&lt;br /&gt;
==carte alimentation==&lt;br /&gt;
&lt;br /&gt;
==carte principale==&lt;br /&gt;
&lt;br /&gt;
*dimension maximum : 8cm x 13cm&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:PiPico&amp;diff=20938</id>
		<title>Cours:PiPico</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:PiPico&amp;diff=20938"/>
				<updated>2026-04-09T08:20:42Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Pico C++ SDK */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Brochage =&lt;br /&gt;
&lt;br /&gt;
https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf&lt;br /&gt;
&lt;br /&gt;
=utilisation de l'ide arduino=&lt;br /&gt;
&lt;br /&gt;
https://github.com/earlephilhower/arduino-pico&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=non recommandé : QtCreator=&lt;br /&gt;
&lt;br /&gt;
== Création assistée d'un projet ==&lt;br /&gt;
&lt;br /&gt;
À l'aide de ''pico_project.py'' :&lt;br /&gt;
* ''git clone https://github.com/raspberrypi/pico-project-generator.git''&lt;br /&gt;
* ''./pico_project.py --gui''&lt;br /&gt;
* ne pas cocher &amp;quot;create VSCode project&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Attention, ne pas oublier de spécifier le chemin vers le SDK :&lt;br /&gt;
* ''export PICO_SDK_PATH=../../pico-sdk'' en remplaçant ''../../pico-sdk'' par le chemin vers le SDK.&lt;br /&gt;
&lt;br /&gt;
== Ouverture avec QTCreator ==&lt;br /&gt;
&lt;br /&gt;
QtCreator est capable de lire et interpréter le fichier ''CMakeLists.txt'' comme descripteur de projet, il suffit donc&lt;br /&gt;
* d'ouvrir (comme projet) le fichier ''CMakeLists.txt''&lt;br /&gt;
&lt;br /&gt;
=VsCode=&lt;br /&gt;
&lt;br /&gt;
*installer VsCode&lt;br /&gt;
**https://doc.ubuntu-fr.org/visual_studio_code&lt;br /&gt;
*ajouter le plugin pi pico&lt;br /&gt;
**https://github.com/raspberrypi/pico-vscode&lt;br /&gt;
**https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf&lt;br /&gt;
&lt;br /&gt;
=Registres=&lt;br /&gt;
&lt;br /&gt;
==gpio==&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#include &amp;quot;pico/stdlib.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/regs/addressmap.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
unsigned int&amp;amp; gpioOUT = *(unsigned int*) (SIO_BASE + 0x10);&lt;br /&gt;
unsigned int&amp;amp; gpioOE  = *(unsigned int*) (SIO_BASE + 0x20);&lt;br /&gt;
&lt;br /&gt;
unsigned int&amp;amp; gpioCtrl0 = *(unsigned int *) (IO_BANK0_BASE+0x4);&lt;br /&gt;
//facultatif&lt;br /&gt;
unsigned int&amp;amp; padsBank0_GPIO0 = *(unsigned int *) (PADS_BANK0_BASE+0x4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
    // facultatif&lt;br /&gt;
    padsBank0_GPIO0|=(1&amp;lt;&amp;lt;6); // input enable&lt;br /&gt;
    padsBank0_GPIO0&amp;amp;=~(1&amp;lt;&amp;lt;7); // output disable&lt;br /&gt;
    // piloter la broche avec fonction SIO&lt;br /&gt;
    gpioCtrl0=5;&lt;br /&gt;
    // sortie enable&lt;br /&gt;
    gpioOE=1&amp;lt;&amp;lt;0;&lt;br /&gt;
    while (true)&lt;br /&gt;
    {&lt;br /&gt;
        gpioOUT&amp;amp;=~(1&amp;lt;&amp;lt;0);&lt;br /&gt;
        sleep_ms(200);&lt;br /&gt;
        gpioOUT|=(1&amp;lt;&amp;lt;0);&lt;br /&gt;
        sleep_ms(10);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=usage des périphériques au travers du sdk=&lt;br /&gt;
&lt;br /&gt;
== Référence en PDF du Pico C++ SDK ==&lt;br /&gt;
&lt;br /&gt;
[https://pip-assets.raspberrypi.com/categories/609-microcontroller-boards/documents/RP-009085-KB-1-raspberry-pi-pico-c-sdk.pdf Raspberry Pi Pico-series C/C++ SDK Libraries and tools for C/C++ development on Raspberry Pi microcontrollers]&lt;br /&gt;
&lt;br /&gt;
==GPIO==&lt;br /&gt;
&lt;br /&gt;
https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_gpio&lt;br /&gt;
&lt;br /&gt;
==IO==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Syntaxe&lt;br /&gt;
||void gpio_init(uint gpio)&lt;br /&gt;
|-&lt;br /&gt;
! Paramètres&lt;br /&gt;
||&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| gpio || numéro de gpio&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
*gpio_put(LED_PIN, 1);&lt;br /&gt;
*gpio_set_dir(LED_PIN, GPIO_OUT);&lt;br /&gt;
*gpio_set_pulls (uint gpio, bool up, bool down)&lt;br /&gt;
&lt;br /&gt;
==interrupt==&lt;br /&gt;
&lt;br /&gt;
Il n'y a pas d'interruption dédiée à une broche particulière. Toutes les gpio déclenchent la même interruption. Il convient ensuite de regarder lesquelles ont déclenchées l'interruption :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void my_raw_interrupt_handler()&lt;br /&gt;
{&lt;br /&gt;
   const int32_t gpio;&lt;br /&gt;
   if (gpio_get_irq_event_mask(gpio) &amp;amp; (GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL))&lt;br /&gt;
    {&lt;br /&gt;
        if (gpio_get_irq_event_mask(gpio) &amp;amp; (GPIO_IRQ_EDGE_RISE)) // Front Montant&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
        } &lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
        }&lt;br /&gt;
        gpio_acknowledge_irq(gpio, GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
gpio_set_irq_enabled(2, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Pwm==&lt;br /&gt;
&lt;br /&gt;
https://raspberry-pi.developpez.com/actu/344283/Raspberry-Pi-Pico-moins-Apprendre-a-generer-des-signaux-PWM-un-billet-blog-de-f-leb/&lt;br /&gt;
&lt;br /&gt;
 Fmli = Fcpu / ( wrap * clkdiv ) // sur pi pico, Fcpu=125MHz&lt;br /&gt;
&lt;br /&gt;
*clkdiv:&lt;br /&gt;
**Le registre qui stocke le diviseur de fréquence comprend 8 bits pour la partie entière, et 4 bits pour la partie fractionnaire&lt;br /&gt;
**valeur max de clkdiv : 255 + 15/16&lt;br /&gt;
**ex : pwm_set_clkdiv_int_frac (slice, 38, 3); // diviseur de fréquence = 38 + 3/16 &lt;br /&gt;
&lt;br /&gt;
*gpio_set_function(PICO_DEFAULT_LED_PIN, GPIO_FUNC_PWM);&lt;br /&gt;
*uint slice_num = pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN);&lt;br /&gt;
*pwm_config config = pwm_get_default_config();&lt;br /&gt;
*pwm_config_set_clkdiv(&amp;amp;config, 4.f);&lt;br /&gt;
*pwm_init(slice_num, &amp;amp;config, true);&lt;br /&gt;
*pwm_set_gpio_level(PICO_DEFAULT_LED_PIN, fade * fade); // rapport cyclique 16 bits&lt;br /&gt;
&lt;br /&gt;
==I2C==&lt;br /&gt;
&lt;br /&gt;
==configuration cmake==&lt;br /&gt;
ajouter dans la liste target_link_libraries&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
target_link_libraries( &lt;br /&gt;
        ....&lt;br /&gt;
        hardware_i2c&lt;br /&gt;
        ....&lt;br /&gt;
        )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===i2c scanner===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;pico/stdlib.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/i2c.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/timer.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/clocks.h&amp;quot;&lt;br /&gt;
#include &amp;quot;pico/binary_info.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int64_t alarm_callback(alarm_id_t id, void *user_data) {&lt;br /&gt;
    // Put your timeout handler code in here&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool reserved_addr(uint8_t addr) {&lt;br /&gt;
    return (addr &amp;amp; 0x78) == 0 || (addr &amp;amp; 0x78) == 0x78;&lt;br /&gt;
}&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    stdio_init_all();&lt;br /&gt;
&lt;br /&gt;
    // I2C Initialisation. Using it at 400Khz.&lt;br /&gt;
    i2c_init(i2c0, 100*1000);&lt;br /&gt;
    &lt;br /&gt;
    gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);&lt;br /&gt;
    gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);&lt;br /&gt;
    gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);&lt;br /&gt;
    gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);&lt;br /&gt;
    // For more examples of I2C use see https://github.com/raspberrypi/pico-examples/tree/master/i2c&lt;br /&gt;
    bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));&lt;br /&gt;
&lt;br /&gt;
    // Timer example code - This example fires off the callback after 2000ms&lt;br /&gt;
    add_alarm_in_ms(2000, alarm_callback, NULL, false);&lt;br /&gt;
    // For more examples of timer use see https://github.com/raspberrypi/pico-examples/tree/master/timer&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;System Clock Frequency is %d Hz\n&amp;quot;, clock_get_hz(clk_sys));&lt;br /&gt;
    printf(&amp;quot;USB Clock Frequency is %d Hz\n&amp;quot;, clock_get_hz(clk_usb));&lt;br /&gt;
    // For more examples of clocks use see https://github.com/raspberrypi/pico-examples/tree/master/clocks&lt;br /&gt;
&lt;br /&gt;
    while (true) {&lt;br /&gt;
        printf(&amp;quot;\nI2C Bus Scan\n&amp;quot;);&lt;br /&gt;
        printf(&amp;quot;   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
        for (int addr = 0; addr &amp;lt; (1 &amp;lt;&amp;lt; 7); ++addr) {&lt;br /&gt;
            if (addr % 16 == 0) {&lt;br /&gt;
                printf(&amp;quot;%02x &amp;quot;, addr);&lt;br /&gt;
            }&lt;br /&gt;
    &lt;br /&gt;
            // Perform a 1-byte dummy read from the probe address. If a slave&lt;br /&gt;
            // acknowledges this address, the function returns the number of bytes&lt;br /&gt;
            // transferred. If the address byte is ignored, the function returns&lt;br /&gt;
            // -1.&lt;br /&gt;
    &lt;br /&gt;
            // Skip over any reserved addresses.&lt;br /&gt;
            int ret;&lt;br /&gt;
            uint8_t rxdata;&lt;br /&gt;
            if (reserved_addr(addr))&lt;br /&gt;
                ret = PICO_ERROR_GENERIC;&lt;br /&gt;
            else&lt;br /&gt;
                ret = i2c_read_blocking(i2c_default, addr, &amp;amp;rxdata, 1, false);&lt;br /&gt;
    &lt;br /&gt;
            printf(ret &amp;lt; 0 ? &amp;quot;.&amp;quot; : &amp;quot;@&amp;quot;);&lt;br /&gt;
            printf(addr % 16 == 15 ? &amp;quot;\n&amp;quot; : &amp;quot;  &amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        printf(&amp;quot;Done.\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
        sleep_ms(1000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===read/write register===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
void writeI2cReg(uint8_t targetAddress, uint8_t regAddress, uint8_t regValue)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t data[2]={regAddress,regValue};&lt;br /&gt;
    i2c_write_blocking(i2c0,targetAddress,data,2,false);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t readI2cReg(uint8_t targetAddress, uint8_t regAddress)&lt;br /&gt;
{&lt;br /&gt;
    i2c_write_blocking(i2c0,targetAddress,&amp;amp;regAddress,1,true);&lt;br /&gt;
    uint8_t regValue;&lt;br /&gt;
    i2c_read_blocking(i2c0,targetAddress,&amp;amp;regValue,1,false);&lt;br /&gt;
    return regValue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==adc==&lt;br /&gt;
&lt;br /&gt;
==uart==&lt;br /&gt;
*https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_uart&lt;br /&gt;
&lt;br /&gt;
==pio==&lt;br /&gt;
&lt;br /&gt;
===ws2812===&lt;br /&gt;
https://github.com/ForsakenNGS/PicoLED&lt;br /&gt;
&lt;br /&gt;
=liens=&lt;br /&gt;
*https://www.gibbard.me/using_the_raspberry_pi_pico_on_ubuntu/&lt;br /&gt;
*https://tutoduino.fr/en/pico-platformio/&lt;br /&gt;
*visual studio code :&lt;br /&gt;
**https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf partie 7.1&lt;br /&gt;
**https://www.electronicshub.org/program-raspberry-pi-pico-with-visual-studio-code/&lt;br /&gt;
*https://github.com/raspberrypi/picotool&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:PiPico&amp;diff=20936</id>
		<title>Cours:PiPico</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:PiPico&amp;diff=20936"/>
				<updated>2026-04-09T08:13:04Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* usage des périphériques au travers du sdk */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Brochage =&lt;br /&gt;
&lt;br /&gt;
https://datasheets.raspberrypi.com/pico/Pico-R3-A4-Pinout.pdf&lt;br /&gt;
&lt;br /&gt;
=utilisation de l'ide arduino=&lt;br /&gt;
&lt;br /&gt;
https://github.com/earlephilhower/arduino-pico&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=non recommandé : QtCreator=&lt;br /&gt;
&lt;br /&gt;
== Création assistée d'un projet ==&lt;br /&gt;
&lt;br /&gt;
À l'aide de ''pico_project.py'' :&lt;br /&gt;
* ''git clone https://github.com/raspberrypi/pico-project-generator.git''&lt;br /&gt;
* ''./pico_project.py --gui''&lt;br /&gt;
* ne pas cocher &amp;quot;create VSCode project&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Attention, ne pas oublier de spécifier le chemin vers le SDK :&lt;br /&gt;
* ''export PICO_SDK_PATH=../../pico-sdk'' en remplaçant ''../../pico-sdk'' par le chemin vers le SDK.&lt;br /&gt;
&lt;br /&gt;
== Ouverture avec QTCreator ==&lt;br /&gt;
&lt;br /&gt;
QtCreator est capable de lire et interpréter le fichier ''CMakeLists.txt'' comme descripteur de projet, il suffit donc&lt;br /&gt;
* d'ouvrir (comme projet) le fichier ''CMakeLists.txt''&lt;br /&gt;
&lt;br /&gt;
=VsCode=&lt;br /&gt;
&lt;br /&gt;
*installer VsCode&lt;br /&gt;
**https://doc.ubuntu-fr.org/visual_studio_code&lt;br /&gt;
*ajouter le plugin pi pico&lt;br /&gt;
**https://github.com/raspberrypi/pico-vscode&lt;br /&gt;
**https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf&lt;br /&gt;
&lt;br /&gt;
=Registres=&lt;br /&gt;
&lt;br /&gt;
==gpio==&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#include &amp;quot;pico/stdlib.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/regs/addressmap.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
unsigned int&amp;amp; gpioOUT = *(unsigned int*) (SIO_BASE + 0x10);&lt;br /&gt;
unsigned int&amp;amp; gpioOE  = *(unsigned int*) (SIO_BASE + 0x20);&lt;br /&gt;
&lt;br /&gt;
unsigned int&amp;amp; gpioCtrl0 = *(unsigned int *) (IO_BANK0_BASE+0x4);&lt;br /&gt;
//facultatif&lt;br /&gt;
unsigned int&amp;amp; padsBank0_GPIO0 = *(unsigned int *) (PADS_BANK0_BASE+0x4);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
    // facultatif&lt;br /&gt;
    padsBank0_GPIO0|=(1&amp;lt;&amp;lt;6); // input enable&lt;br /&gt;
    padsBank0_GPIO0&amp;amp;=~(1&amp;lt;&amp;lt;7); // output disable&lt;br /&gt;
    // piloter la broche avec fonction SIO&lt;br /&gt;
    gpioCtrl0=5;&lt;br /&gt;
    // sortie enable&lt;br /&gt;
    gpioOE=1&amp;lt;&amp;lt;0;&lt;br /&gt;
    while (true)&lt;br /&gt;
    {&lt;br /&gt;
        gpioOUT&amp;amp;=~(1&amp;lt;&amp;lt;0);&lt;br /&gt;
        sleep_ms(200);&lt;br /&gt;
        gpioOUT|=(1&amp;lt;&amp;lt;0);&lt;br /&gt;
        sleep_ms(10);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=usage des périphériques au travers du sdk=&lt;br /&gt;
&lt;br /&gt;
== Pico C++ SDK ==&lt;br /&gt;
&lt;br /&gt;
Raspberry Pi Pico-series C/C++ SDK&lt;br /&gt;
Libraries and tools for C/C++ development on Raspberry Pi&lt;br /&gt;
microcontrollers : &lt;br /&gt;
https://pip-assets.raspberrypi.com/categories/609-microcontroller-boards/documents/RP-009085-KB-1-raspberry-pi-pico-c-sdk.pdf&lt;br /&gt;
&lt;br /&gt;
==GPIO==&lt;br /&gt;
&lt;br /&gt;
https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#hardware_gpio&lt;br /&gt;
&lt;br /&gt;
==IO==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! Syntaxe&lt;br /&gt;
||void gpio_init(uint gpio)&lt;br /&gt;
|-&lt;br /&gt;
! Paramètres&lt;br /&gt;
||&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| gpio || numéro de gpio&lt;br /&gt;
|}&lt;br /&gt;
|}&lt;br /&gt;
*gpio_put(LED_PIN, 1);&lt;br /&gt;
*gpio_set_dir(LED_PIN, GPIO_OUT);&lt;br /&gt;
*gpio_set_pulls (uint gpio, bool up, bool down)&lt;br /&gt;
&lt;br /&gt;
==interrupt==&lt;br /&gt;
&lt;br /&gt;
Il n'y a pas d'interruption dédiée à une broche particulière. Toutes les gpio déclenchent la même interruption. Il convient ensuite de regarder lesquelles ont déclenchées l'interruption :&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void my_raw_interrupt_handler()&lt;br /&gt;
{&lt;br /&gt;
   const int32_t gpio;&lt;br /&gt;
   if (gpio_get_irq_event_mask(gpio) &amp;amp; (GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL))&lt;br /&gt;
    {&lt;br /&gt;
        if (gpio_get_irq_event_mask(gpio) &amp;amp; (GPIO_IRQ_EDGE_RISE)) // Front Montant&lt;br /&gt;
        {&lt;br /&gt;
&lt;br /&gt;
        } &lt;br /&gt;
        else&lt;br /&gt;
        {&lt;br /&gt;
        }&lt;br /&gt;
        gpio_acknowledge_irq(gpio, GPIO_IRQ_EDGE_RISE|GPIO_IRQ_EDGE_FALL);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
...&lt;br /&gt;
gpio_set_irq_enabled(2, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Pwm==&lt;br /&gt;
&lt;br /&gt;
https://raspberry-pi.developpez.com/actu/344283/Raspberry-Pi-Pico-moins-Apprendre-a-generer-des-signaux-PWM-un-billet-blog-de-f-leb/&lt;br /&gt;
&lt;br /&gt;
 Fmli = Fcpu / ( wrap * clkdiv ) // sur pi pico, Fcpu=125MHz&lt;br /&gt;
&lt;br /&gt;
*clkdiv:&lt;br /&gt;
**Le registre qui stocke le diviseur de fréquence comprend 8 bits pour la partie entière, et 4 bits pour la partie fractionnaire&lt;br /&gt;
**valeur max de clkdiv : 255 + 15/16&lt;br /&gt;
**ex : pwm_set_clkdiv_int_frac (slice, 38, 3); // diviseur de fréquence = 38 + 3/16 &lt;br /&gt;
&lt;br /&gt;
*gpio_set_function(PICO_DEFAULT_LED_PIN, GPIO_FUNC_PWM);&lt;br /&gt;
*uint slice_num = pwm_gpio_to_slice_num(PICO_DEFAULT_LED_PIN);&lt;br /&gt;
*pwm_config config = pwm_get_default_config();&lt;br /&gt;
*pwm_config_set_clkdiv(&amp;amp;config, 4.f);&lt;br /&gt;
*pwm_init(slice_num, &amp;amp;config, true);&lt;br /&gt;
*pwm_set_gpio_level(PICO_DEFAULT_LED_PIN, fade * fade); // rapport cyclique 16 bits&lt;br /&gt;
&lt;br /&gt;
==I2C==&lt;br /&gt;
&lt;br /&gt;
===i2c scanner===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;quot;pico/stdlib.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/i2c.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/timer.h&amp;quot;&lt;br /&gt;
#include &amp;quot;hardware/clocks.h&amp;quot;&lt;br /&gt;
#include &amp;quot;pico/binary_info.h&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
int64_t alarm_callback(alarm_id_t id, void *user_data) {&lt;br /&gt;
    // Put your timeout handler code in here&lt;br /&gt;
    return 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
bool reserved_addr(uint8_t addr) {&lt;br /&gt;
    return (addr &amp;amp; 0x78) == 0 || (addr &amp;amp; 0x78) == 0x78;&lt;br /&gt;
}&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    stdio_init_all();&lt;br /&gt;
&lt;br /&gt;
    // I2C Initialisation. Using it at 400Khz.&lt;br /&gt;
    i2c_init(i2c0, 100*1000);&lt;br /&gt;
    &lt;br /&gt;
    gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);&lt;br /&gt;
    gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);&lt;br /&gt;
    gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);&lt;br /&gt;
    gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);&lt;br /&gt;
    // For more examples of I2C use see https://github.com/raspberrypi/pico-examples/tree/master/i2c&lt;br /&gt;
    bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));&lt;br /&gt;
&lt;br /&gt;
    // Timer example code - This example fires off the callback after 2000ms&lt;br /&gt;
    add_alarm_in_ms(2000, alarm_callback, NULL, false);&lt;br /&gt;
    // For more examples of timer use see https://github.com/raspberrypi/pico-examples/tree/master/timer&lt;br /&gt;
&lt;br /&gt;
    printf(&amp;quot;System Clock Frequency is %d Hz\n&amp;quot;, clock_get_hz(clk_sys));&lt;br /&gt;
    printf(&amp;quot;USB Clock Frequency is %d Hz\n&amp;quot;, clock_get_hz(clk_usb));&lt;br /&gt;
    // For more examples of clocks use see https://github.com/raspberrypi/pico-examples/tree/master/clocks&lt;br /&gt;
&lt;br /&gt;
    while (true) {&lt;br /&gt;
        printf(&amp;quot;\nI2C Bus Scan\n&amp;quot;);&lt;br /&gt;
        printf(&amp;quot;   0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
        for (int addr = 0; addr &amp;lt; (1 &amp;lt;&amp;lt; 7); ++addr) {&lt;br /&gt;
            if (addr % 16 == 0) {&lt;br /&gt;
                printf(&amp;quot;%02x &amp;quot;, addr);&lt;br /&gt;
            }&lt;br /&gt;
    &lt;br /&gt;
            // Perform a 1-byte dummy read from the probe address. If a slave&lt;br /&gt;
            // acknowledges this address, the function returns the number of bytes&lt;br /&gt;
            // transferred. If the address byte is ignored, the function returns&lt;br /&gt;
            // -1.&lt;br /&gt;
    &lt;br /&gt;
            // Skip over any reserved addresses.&lt;br /&gt;
            int ret;&lt;br /&gt;
            uint8_t rxdata;&lt;br /&gt;
            if (reserved_addr(addr))&lt;br /&gt;
                ret = PICO_ERROR_GENERIC;&lt;br /&gt;
            else&lt;br /&gt;
                ret = i2c_read_blocking(i2c_default, addr, &amp;amp;rxdata, 1, false);&lt;br /&gt;
    &lt;br /&gt;
            printf(ret &amp;lt; 0 ? &amp;quot;.&amp;quot; : &amp;quot;@&amp;quot;);&lt;br /&gt;
            printf(addr % 16 == 15 ? &amp;quot;\n&amp;quot; : &amp;quot;  &amp;quot;);&lt;br /&gt;
        }&lt;br /&gt;
        printf(&amp;quot;Done.\n&amp;quot;);&lt;br /&gt;
    &lt;br /&gt;
        sleep_ms(1000);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===read/write register===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
void writeI2cReg(uint8_t targetAddress, uint8_t regAddress, uint8_t regValue)&lt;br /&gt;
{&lt;br /&gt;
    uint8_t data[2]={regAddress,regValue};&lt;br /&gt;
    i2c_write_blocking(i2c0,targetAddress,data,2,false);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
uint8_t readI2cReg(uint8_t targetAddress, uint8_t regAddress)&lt;br /&gt;
{&lt;br /&gt;
    i2c_write_blocking(i2c0,targetAddress,&amp;amp;regAddress,1,true);&lt;br /&gt;
    uint8_t regValue;&lt;br /&gt;
    i2c_read_blocking(i2c0,targetAddress,&amp;amp;regValue,1,false);&lt;br /&gt;
    return regValue;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==adc==&lt;br /&gt;
&lt;br /&gt;
==uart==&lt;br /&gt;
*https://www.raspberrypi.com/documentation/pico-sdk/hardware.html#group_hardware_uart&lt;br /&gt;
&lt;br /&gt;
==pio==&lt;br /&gt;
&lt;br /&gt;
===ws2812===&lt;br /&gt;
https://github.com/ForsakenNGS/PicoLED&lt;br /&gt;
&lt;br /&gt;
=liens=&lt;br /&gt;
*https://www.gibbard.me/using_the_raspberry_pi_pico_on_ubuntu/&lt;br /&gt;
*https://tutoduino.fr/en/pico-platformio/&lt;br /&gt;
*visual studio code :&lt;br /&gt;
**https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf partie 7.1&lt;br /&gt;
**https://www.electronicshub.org/program-raspberry-pi-pico-with-visual-studio-code/&lt;br /&gt;
*https://github.com/raspberrypi/picotool&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Acces:Prof&amp;diff=20935</id>
		<title>Acces:Prof</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Acces:Prof&amp;diff=20935"/>
				<updated>2026-04-08T14:47:20Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;*bjacquot&lt;br /&gt;
*AgnesD&lt;br /&gt;
*f.sisternas&lt;br /&gt;
*sl&lt;br /&gt;
*bernar03&lt;br /&gt;
*Gmillon&lt;br /&gt;
*Remy02&lt;br /&gt;
*Woznyrob&lt;br /&gt;
*penant&lt;br /&gt;
*fredmn&lt;br /&gt;
*vuille01&lt;br /&gt;
*nawal&lt;br /&gt;
*Tarek&lt;br /&gt;
*Abuzlukov&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Acces:Prof&amp;diff=20934</id>
		<title>Acces:Prof</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Acces:Prof&amp;diff=20934"/>
				<updated>2026-04-08T14:06:23Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;*bjacquot&lt;br /&gt;
*AgnesD&lt;br /&gt;
*f.sisternas&lt;br /&gt;
*sl&lt;br /&gt;
*bernar03&lt;br /&gt;
*Gmillon&lt;br /&gt;
*Remy02&lt;br /&gt;
*Woznyrob&lt;br /&gt;
*penant&lt;br /&gt;
*fredmn&lt;br /&gt;
*vuille01&lt;br /&gt;
*nawal&lt;br /&gt;
*Tarek&lt;br /&gt;
*abuzlukov&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=LLM&amp;diff=20902</id>
		<title>LLM</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=LLM&amp;diff=20902"/>
				<updated>2026-03-17T13:50:06Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Guide pour un usage raisonnable et raisonné des LLM en programmation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Guide pour un usage raisonnable et raisonné des LLM en programmation ==&lt;br /&gt;
&lt;br /&gt;
* Rappel LLM vs IA&lt;br /&gt;
** Fonctionnement d'un LLM&lt;br /&gt;
** &amp;quot;IA&amp;quot; ou IA&lt;br /&gt;
* Ollama : LLM en cloud ou local&lt;br /&gt;
* Aide à la programmation&lt;br /&gt;
** LLM en mode question/réponse&lt;br /&gt;
** Utiliser un agent&lt;br /&gt;
*** Claude Code&lt;br /&gt;
*** OpenCode&lt;br /&gt;
*** mode planification&lt;br /&gt;
* Quels modèles de LLM ?&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=LLM&amp;diff=20877</id>
		<title>LLM</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=LLM&amp;diff=20877"/>
				<updated>2026-03-08T07:44:43Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : Page créée avec « == Guide pour un usage raisonnable et raisonné des LLM en programmation ==  * Rappel LLM vs IA ** Fonctionnement d'un LLM ** &amp;quot;IA&amp;quot; ou IA * Ollama : LLM en cloud ou local *... »&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Guide pour un usage raisonnable et raisonné des LLM en programmation ==&lt;br /&gt;
&lt;br /&gt;
* Rappel LLM vs IA&lt;br /&gt;
** Fonctionnement d'un LLM&lt;br /&gt;
** &amp;quot;IA&amp;quot; ou IA&lt;br /&gt;
* Ollama : LLM en cloud ou local&lt;br /&gt;
* Aide à la programmation&lt;br /&gt;
** LLM en mode question/réponse&lt;br /&gt;
** Claude Code&lt;br /&gt;
*** mode planification&lt;br /&gt;
* Quels modèles de LLM ?&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:XR207_tp_digicode&amp;diff=20843</id>
		<title>Cours:XR207 tp digicode</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:XR207_tp_digicode&amp;diff=20843"/>
				<updated>2026-02-12T08:46:34Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Indice de ligne */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt; &amp;lt;big&amp;gt;{{Rouge|******************************************}}&amp;lt;/big&amp;gt;&lt;br /&gt;
 &amp;lt;big&amp;gt;{{Rouge|*** Apporter la malette d'informatique ***}}&amp;lt;/big&amp;gt;&lt;br /&gt;
 &amp;lt;big&amp;gt;{{Rouge|******************************************}}&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Cours:CoursM2103|{{Rouge|&amp;lt;big&amp;gt;'''Fiche résumé'''&amp;lt;/big&amp;gt;}}]]&lt;br /&gt;
&lt;br /&gt;
[[Cours:TPs_2103|{{Bleu|&amp;lt;big&amp;gt;'''Retour à la liste des Tps'''&amp;lt;/big&amp;gt;}}]]&lt;br /&gt;
&lt;br /&gt;
[[Cours:TPS_2103_tp3_corrige|{{Vert|&amp;lt;big&amp;gt;'''Éléments de correction'''&amp;lt;/big&amp;gt;}}]]&lt;br /&gt;
&lt;br /&gt;
[[Fichier:Arduino-nano-pinout.png|600px|droite]]&lt;br /&gt;
&lt;br /&gt;
Vous allez développer un système de digicode architecturé autour d'un atmega328p (le µcontrôleur présent sur les cartes arduino).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;big&amp;gt;'''Pour ce TP, nous utilisons une carte arduino {{Rouge|sans le shieldinfo}}'''&amp;lt;/big&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=objectifs=&lt;br /&gt;
*numération binaire (affichage avec des leds de la valeur d'un entier signé ou non )&lt;br /&gt;
*e/s + pullup&lt;br /&gt;
*utilisation de fonctions&lt;br /&gt;
*déclaration de fonctions&lt;br /&gt;
*algorithme simple et implémentation&lt;br /&gt;
&lt;br /&gt;
=Matériel=&lt;br /&gt;
&lt;br /&gt;
Nous utiliserons :&lt;br /&gt;
*1 carte arduino NANO&lt;br /&gt;
*1 plaque à essais&lt;br /&gt;
*8 leds pour afficher une valeur&lt;br /&gt;
*1 résistance de 330Ω par led&lt;br /&gt;
*1 clavier matriciel&lt;br /&gt;
*fils M-M&lt;br /&gt;
*fils M-F&lt;br /&gt;
&lt;br /&gt;
=Informations lumineuses=&lt;br /&gt;
&lt;br /&gt;
==Câblage==&lt;br /&gt;
&lt;br /&gt;
{{Todo|Câbler sur une plaque à essais les 8 leds :}}&lt;br /&gt;
*utiliser des résistances de 330Ω pour régler l'intensité lumineuse&lt;br /&gt;
*PD0 à PD7&lt;br /&gt;
*mettre les leds dans &amp;quot;l'ordre&amp;quot; des broches&lt;br /&gt;
*montage dit &amp;quot;cathodes communes&amp;quot;&lt;br /&gt;
&lt;br /&gt;
==Utilisation des 8 leds==&lt;br /&gt;
&lt;br /&gt;
 Nous utiliserons dans nos programmes [[Cours:CoursM2103#Fixed_width_integer_types|les types de variable dont la taille est explicitement donnée]]&lt;br /&gt;
&lt;br /&gt;
Le {{Rouge|débogage}} d'un programme est fondamental, il existe différentes façons de le faire. Ici nous nous contenterons {{Rouge|d'utiliser des leds}} pour afficher des informations sur l'exécution du programme.&lt;br /&gt;
&lt;br /&gt;
 Principe :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
#include &amp;lt;avr/io.h&amp;gt;&lt;br /&gt;
#include &amp;lt;util/delay.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void clignote()&lt;br /&gt;
{&lt;br /&gt;
    PORTD=0;&lt;br /&gt;
    for (uint8_t i=0;i&amp;lt;5;i++)&lt;br /&gt;
    {&lt;br /&gt;
         PORTD ^= 0xFF;&lt;br /&gt;
         _delay_ms(50);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main()&lt;br /&gt;
{&lt;br /&gt;
    // config des sorties&lt;br /&gt;
    DDRD = 0xFF; // les 8  broches des leds en sortie&lt;br /&gt;
&lt;br /&gt;
    // qques variables&lt;br /&gt;
    uint8_t val1 = 0;&lt;br /&gt;
    int8_t val2 = -1;&lt;br /&gt;
    uint8_t val3 = 255;&lt;br /&gt;
    uint16_t val4 = 256;&lt;br /&gt;
&lt;br /&gt;
    while(1)&lt;br /&gt;
    {&lt;br /&gt;
        PORTD = val1;&lt;br /&gt;
        _delay_ms(2000);&lt;br /&gt;
        clignote();&lt;br /&gt;
        PORTD = val2;&lt;br /&gt;
        _delay_ms(2000);&lt;br /&gt;
        clignote();&lt;br /&gt;
        PORTD = val3;&lt;br /&gt;
        _delay_ms(2000);&lt;br /&gt;
        clignote();&lt;br /&gt;
        PORTD = val4;&lt;br /&gt;
        _delay_ms(2000);&lt;br /&gt;
        for (int i=0;i&amp;lt;10;i++) clignote();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Question|Indiquer l'affichage réalisé par chacune des lignes}} PORTD=valx&lt;br /&gt;
&lt;br /&gt;
 Les nombres négatifs sont représentés pas la méthodes des compléments à deux : https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_deux ]&lt;br /&gt;
&lt;br /&gt;
=Decodage d'un clavier=&lt;br /&gt;
&lt;br /&gt;
==Câblage==&lt;br /&gt;
&lt;br /&gt;
Le tableau suivant résume la disposition physique du clavier avec la position physique sur le connecteur des lignes (L0 à L3) et colonnes (C0 à C2) en vue de dessus :&lt;br /&gt;
{|align=&amp;quot;center&amp;quot; border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;20&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
{|cellpadding=&amp;quot;0&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
{|align=&amp;quot;center&amp;quot; border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;3&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| PC2 || PC3 || PC4 || PC5 || PB0 || PB1 || PB2&lt;br /&gt;
|-&lt;br /&gt;
| L0 || L1 || L2 || L3 || C0 || C1 || C2&lt;br /&gt;
|}&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
[[Fichier:ArduinoClavier.png]]&lt;br /&gt;
|}&lt;br /&gt;
||&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
! L/C !! C0 !! C1 !! C2&lt;br /&gt;
|-&lt;br /&gt;
! L0 || 1 || 2 || 3&lt;br /&gt;
|-&lt;br /&gt;
! L1 || 4 || 5 || 6&lt;br /&gt;
|-&lt;br /&gt;
! L2 || 7 || 8 || 9&lt;br /&gt;
|-&lt;br /&gt;
! L3 || * || 0 || #&lt;br /&gt;
|}&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:ClavierMatricielPrincipe.png|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Todo|Câbler sur une plaque à essais le clavier 12 touches que l'on vous donne. Les {{Rouge|colonnes}} seront reliées sur le port {{Rouge|B}} et les {{Vert|lignes}} sur le port {{Vert|C}} (cf indications ci dessus)}}&lt;br /&gt;
&lt;br /&gt;
'''Remarque :'''  On ne câblera aucune résistance de tirage, on utilisera les résistances internes au µcontrôleur.&lt;br /&gt;
&lt;br /&gt;
{{Aide|pullUp}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2 étapes sont nécessaires pour activer une résistance de pullUp sur une broche :&lt;br /&gt;
*Configurer la broche en entrée&lt;br /&gt;
**pour une seule broche : DDRB &amp;amp;=~ (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
**pour un groupe        : DDRB &amp;amp;=~ ((1&amp;lt;&amp;lt;PB1)|(1&amp;lt;&amp;lt;PB2)|(1&amp;lt;&amp;lt;PB3) ...);&lt;br /&gt;
*Activer ensuite la/les résistance(s) de pullUp&lt;br /&gt;
**pour une seule broche : PORTB |= (1&amp;lt;&amp;lt;PB1);&lt;br /&gt;
**pour un groupe        : PORTB |= (1&amp;lt;&amp;lt;PB1)|(1&amp;lt;&amp;lt;PB2)|(1&amp;lt;&amp;lt;PB3) ...;&lt;br /&gt;
{{finAide}}&lt;br /&gt;
&lt;br /&gt;
=={{Bleu|Principe de décodage}}==&lt;br /&gt;
&lt;br /&gt;
En utilisant un clavier matriciel, chaque touche n'est pas associé à une broche du µcontrôleur. Dans le cas présent, on passe de :&lt;br /&gt;
*12 entrées nécessaires si chaque touche était connectée directement&lt;br /&gt;
*7 broches nécessaires avec le clavier matriciel&lt;br /&gt;
&lt;br /&gt;
L'intérêt évident est donc une {{Rouge|réduction du nombre de broches}} nécessaires à l'utilisation du clavier.&lt;br /&gt;
&lt;br /&gt;
L'inconvénient qui en résulte est la {{Rouge|complexité relative du code}} pour trouver quelle touche est appuyée.&lt;br /&gt;
&lt;br /&gt;
Le principe de décodage est {{Rouge|symétrique}} entre lignes et colonnes. Détaillons donc le fonctionnement pour trouver sur quelle ligne une touche est appuyée :&lt;br /&gt;
&lt;br /&gt;
*On configure les broches {{Rouge|colonnes en sortie}}.&lt;br /&gt;
*On configure ces sorties à {{Rouge|l'état 0}}&lt;br /&gt;
*Les broches {{Rouge|lignes sont en entrée}}&lt;br /&gt;
*l'état des lignes dépend de l'appui sur un bouton :&lt;br /&gt;
**si {{Rouge|aucun bouton}} appuyé, les résistances de tirage donnent {{Rouge|un état 1}} sur les lignes&lt;br /&gt;
**si on appui sur un bouton de {{Rouge|la ligne0}} (key 1/2/3), alors l'entrée ligne PC2 passe à {{Rouge|l'état 0}}.&lt;br /&gt;
**la valeur change suivant la ligne sur laquelle un bouton est appuyé&lt;br /&gt;
*il suffit donc d'observer les 4 bits PC5..PC2 (que l'on note ici etat), et ainsi :&lt;br /&gt;
**si PC5..PC2 = 1111 , pas de touche&lt;br /&gt;
**si PC5..PC2 = 1110 , au moins une touche parmi 1/2/3&lt;br /&gt;
**si PC5..PC2 = 1101 , au moins une touche parmi 4/5/6&lt;br /&gt;
**...&lt;br /&gt;
&lt;br /&gt;
==Indice de ligne==&lt;br /&gt;
&lt;br /&gt;
{{Question|Compléter et vérifier le programme lecture_ligne() qui renvoie le numéro de ligne sur laquelle un bouton est appuyé}}&lt;br /&gt;
*La validation se fera en utilisant des leds connectées sur le port C :&lt;br /&gt;
**On utilisera les 8 leds du PORTD pour afficher le résultat&lt;br /&gt;
**par ex : PORTD = lecture_ligne();&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=cpp&amp;gt;&lt;br /&gt;
int8_t lectureLigne()&lt;br /&gt;
{&lt;br /&gt;
  int8_t etatEntrees;&lt;br /&gt;
  DDRB  |= ( (1&amp;lt;&amp;lt;PB2)|(1&amp;lt;&amp;lt;PB1)|(1&amp;lt;&amp;lt;PB0) ) ;    // commençons par lister les sorties sur le port B&lt;br /&gt;
  DDRC  &amp;amp;=~ ( (1&amp;lt;&amp;lt;PC5)|(1&amp;lt;&amp;lt;PC4)|(1&amp;lt;&amp;lt;PC3)|(1&amp;lt;&amp;lt;PC2) )  ;    // puis les entrées sur le port C&lt;br /&gt;
  PORTB &amp;amp;=~ ( (1&amp;lt;&amp;lt;PB2)|(1&amp;lt;&amp;lt;PB1)|(1&amp;lt;&amp;lt;PB0) ) ;    // on place les sortie à l'état 0&lt;br /&gt;
  PORTC |= ( (1&amp;lt;&amp;lt;PC5)|(1&amp;lt;&amp;lt;PC4)|(1&amp;lt;&amp;lt;PC3)|(1&amp;lt;&amp;lt;PC2) ) ;    // on active les résistances de pull-up sur les entrées&lt;br /&gt;
&lt;br /&gt;
  _delay_ms(1);        // un délai est nécessaire pour l'activation des pull-ups&lt;br /&gt;
&lt;br /&gt;
  etatEntrees = PINC &amp;amp; 0b00111100; // on récupère ensuite l'état des entrées en cachant les bits non utiles&lt;br /&gt;
  switch (etatEntrees)&lt;br /&gt;
  {&lt;br /&gt;
         // PPPPPPPP&lt;br /&gt;
         // CCCCCCCC&lt;br /&gt;
         // ..5432..&lt;br /&gt;
     case 0b00111000 : return 0; // L0 (appui sur l'une des touches 1/2/3 =&amp;gt; {{Rouge|entrée PC2 à 0}} )&lt;br /&gt;
     case 0b00110100 : return 1; // L1 =&amp;gt; {{Rouge|entrée PC3 à 0}} &lt;br /&gt;
     case  : return 2; // L2&lt;br /&gt;
     case  : return 3; // L3&lt;br /&gt;
     // si autre cas, pas de touches deux touches ou autre&lt;br /&gt;
     default : return -1;&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=={{Bleu|Indice de colonne}}==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Question|De la même façon, écrire et tester la fonction lecture_colonne() qui permettra d'obtenir le numéro de colonne sur laquelle un bouton est appuyé.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Touche appuyée==&lt;br /&gt;
Nous souhaitons maintenant écrire une fonction qui renverra une valeur tel que décrit ci dessous :&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;source lang=c&amp;gt;&lt;br /&gt;
const int8_t touches[4][3] = { // tableau de int8_t à 2 dimensions&lt;br /&gt;
                               { 1, 2, 3},&lt;br /&gt;
                               { 4, 5, 6},&lt;br /&gt;
                               { 7, 8, 9},&lt;br /&gt;
                               {10, 0,11}&lt;br /&gt;
                             };&lt;br /&gt;
&lt;br /&gt;
int8_t getTouche();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|| La valeur retournée sera :&lt;br /&gt;
* -1 si pas de touches (ou plusieurs) appuyées&lt;br /&gt;
* la valeur correspondant à la touche pour les chiffres&lt;br /&gt;
* 11 pour le #&lt;br /&gt;
* 10 pour l' *&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
{{Question|Écrire cette fonction en vous servant bien évidemment des 2 fonctions précédentes et pourquoi pas du tableau &amp;quot;touches&amp;quot; :}}&lt;br /&gt;
&lt;br /&gt;
*c'est un tableau de 4 lignes et 3 colonnes&lt;br /&gt;
*la valeur dans la case [0,0] correspond à la valeur de la touche pour ligne=0 et colonne=0  =&amp;gt; 1&lt;br /&gt;
*la valeur dans la case [1,2] correspond à la valeur de la touche pour ligne=1 et colonne=2  =&amp;gt; 6&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=c&amp;gt;&lt;br /&gt;
int8_t getTouche()&lt;br /&gt;
{&lt;br /&gt;
   ....&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=réalisation d'un digicode=&lt;br /&gt;
&lt;br /&gt;
Nous allons utiliser la fonction {{Rouge|getTouche()}} pour réaliser un programme de digicode.&lt;br /&gt;
&lt;br /&gt;
==contraintes==&lt;br /&gt;
Commençons pour définir les contraintes :&lt;br /&gt;
&lt;br /&gt;
*On appuie sur la touche '*' pour démarrer la saisie du code =&amp;gt; la led rouge s'allume brièvement&lt;br /&gt;
*L'utilisateur doit alors saisir le code sur 4 chiffres&lt;br /&gt;
*On valide le code par la touche '#'&lt;br /&gt;
*Si le code est bon une led verte s'allume pendant 10s, sinon une led rouge clignote pendant 10s&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==codez !==&lt;br /&gt;
&lt;br /&gt;
{{Question|Ecrire un programme répondant au cahier des charges}}&lt;br /&gt;
&lt;br /&gt;
*vous pouvez vous servir d'un LLM&lt;br /&gt;
**https://claude.ai/new&lt;br /&gt;
**[https://gemini.google.com/app?utm_source=deepmind.google&amp;amp;utm_medium=referral&amp;amp;utm_campaign=gdm&amp;amp;utm_content= gemini]&lt;br /&gt;
*il suffit de mémoriser les 6 dernières touches appuyées&lt;br /&gt;
**déclarer un tableau&lt;br /&gt;
**faire un décalage du tableau&lt;br /&gt;
**rentrer la nouvelle valeur&lt;br /&gt;
**comparer le tableau par rapport au code&lt;br /&gt;
&lt;br /&gt;
==Améliorations==&lt;br /&gt;
&lt;br /&gt;
{{Question|Ajouter une procédure de changement du code.}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Question|Faire en sorte que le temps maximum de saisie du code soit de 4s}}&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20839</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20839"/>
				<updated>2026-02-05T08:23:43Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Livrable et évaluation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Livrable et évaluation ===&lt;br /&gt;
&lt;br /&gt;
* En fin de période : démonstration de l'état d'avancement&lt;br /&gt;
* Un envoi par mail pour le 20/02/2026 (23h59), contenant :&lt;br /&gt;
** une archive de votre projet (fonctionnel, doit pouvoir être testé), incluant un fichier README.TXT décrivant l'utilisation du code&lt;br /&gt;
** un compte rendu en pdf :&lt;br /&gt;
*** description des problèmes traités&lt;br /&gt;
*** description des solutions implémentées : approche méthodologique, implémentation pratique&lt;br /&gt;
*** description des performancess obtenues (pour chaque solution)&lt;br /&gt;
*** conclusion sur ces performances et sur le système&lt;br /&gt;
** pas de contrainte de nombre de pages, mais raisonnablement entre 5 et 15.&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20838</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20838"/>
				<updated>2026-02-05T08:22:18Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Livrable et évaluation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Livrable et évaluation ===&lt;br /&gt;
&lt;br /&gt;
* En fin de période : démonstration de l'état d'avancement&lt;br /&gt;
* Un envoi par mail pour le 20/02/2026 (23h59), contenant :&lt;br /&gt;
** une archive de votre projet (fonctionnel, doit pouvoir être testé), incluant un fichier README.TXT décrivant l'utilisation du code&lt;br /&gt;
** un compte rendu en pdf :&lt;br /&gt;
*** description du problème traité&lt;br /&gt;
*** description de la solution implémentée : approche méthodologique, implémentation pratique&lt;br /&gt;
*** description des performances obtenue&lt;br /&gt;
*** conclusion sur ces performances et sur le système&lt;br /&gt;
** pas de contrainte de nombre de pages, mais raisonnablement entre 5 et 15.&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20837</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20837"/>
				<updated>2026-02-05T08:21:23Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Livrable */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Livrable et évaluation ===&lt;br /&gt;
&lt;br /&gt;
* En fin de période : démonstration de l'état d'avancement&lt;br /&gt;
* un envoir par mail pour le 20/02/2026 (23h59), contenant :&lt;br /&gt;
** une archive de votre projet (fonctionnel, doit pouvoir être testé), incluant un fichier README.TXT décrivant l'utilisation du code&lt;br /&gt;
** un compte rendu en pdf :&lt;br /&gt;
*** description du problème traité&lt;br /&gt;
*** description de la solution implémentée (méthodologiquement et pratiquement)&lt;br /&gt;
*** description des performances obtenue&lt;br /&gt;
*** conclusion sur ces performances et sur le système&lt;br /&gt;
** pas de contrainte de nombre de pages, mais raisonnablement entre 5 et 15.&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20836</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20836"/>
				<updated>2026-02-05T08:21:09Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Livrable ===&lt;br /&gt;
&lt;br /&gt;
* En fin de période : démonstration de l'état d'avancement&lt;br /&gt;
* un envoir par mail pour le 20/02/2026 (23h59), contenant :&lt;br /&gt;
** une archive de votre projet (fonctionnel, doit pouvoir être testé), incluant un fichier README.TXT décrivant l'utilisation du code&lt;br /&gt;
** un compte rendu en pdf :&lt;br /&gt;
*** description du problème traité&lt;br /&gt;
*** description de la solution implémentée (méthodologiquement et pratiquement)&lt;br /&gt;
*** description des performances obtenue&lt;br /&gt;
*** conclusion sur ces performances et sur le système&lt;br /&gt;
** pas de contrainte de nombre de pages, mais raisonnablement entre 5 et 15.&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20835</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20835"/>
				<updated>2026-02-05T07:40:16Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20834</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20834"/>
				<updated>2026-02-05T07:39:36Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Références */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Source d'inspiration :&lt;br /&gt;
* [https://github.com/360er0/awesome-lego-machine-learning ''Awesome LEGO Machine Learning'']&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20833</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20833"/>
				<updated>2026-02-04T15:52:13Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Connexion à la Rpi et test d'acquisition en ligne de commande */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Il semble nécessaire de désactiver l'écran tactile pour éviter les pertes de connexion avec la caméra : &amp;lt;code&amp;gt;rmmod edt_ft5x06&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20832</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20832"/>
				<updated>2026-02-04T13:55:09Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer deux classes d'objets (jeton long, jeton rond)&lt;br /&gt;
* Réaliser une série d'acquisition / description&lt;br /&gt;
* Caractériser les performances du systèmes par une courbe ROC. En fixant un taux de faux positifs, choisir un point de fonctionnement et donner la matrice de confusion liée.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20811</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20811"/>
				<updated>2026-02-02T14:19:31Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20810</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20810"/>
				<updated>2026-02-02T14:18:10Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Bouts de code Python */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|capture_image.py]]}}&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20809</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20809"/>
				<updated>2026-02-02T14:17:14Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Bouts de code Python */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|Media:capture_image.py]]}}&lt;br /&gt;
import time&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
&lt;br /&gt;
def capture_image_with_retries(&lt;br /&gt;
    max_attempts: int = 5,&lt;br /&gt;
    warmup_s: float = 0.3,&lt;br /&gt;
    retry_delay_s: float = 0.5,&lt;br /&gt;
):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    Initialise Picamera2 + capture une image.&lt;br /&gt;
    Si échec, réessaie en recréant complètement la caméra.&lt;br /&gt;
    Retourne un numpy array (image) ou lève RuntimeError après max_attempts.&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    last_err = None&lt;br /&gt;
&lt;br /&gt;
    for attempt in range(1, max_attempts + 1):&lt;br /&gt;
        picam = None&lt;br /&gt;
        try:&lt;br /&gt;
            picam = Picamera2()&lt;br /&gt;
&lt;br /&gt;
            # Optionnel mais souvent utile: choisir explicitement une config &amp;quot;preview&amp;quot;&lt;br /&gt;
            config = picam.create_preview_configuration(main={&amp;quot;size&amp;quot;: (1280, 720)})&lt;br /&gt;
            picam.configure(config)&lt;br /&gt;
&lt;br /&gt;
            picam.start()&lt;br /&gt;
&lt;br /&gt;
            # Laisse le temps à l'auto-exposition/auto-gain de se stabiliser&lt;br /&gt;
            time.sleep(warmup_s)&lt;br /&gt;
&lt;br /&gt;
            # Capture (guillemet manquant corrigé)&lt;br /&gt;
            img = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Vérifs basiques pour détecter une &amp;quot;non acquisition&amp;quot;&lt;br /&gt;
            if img is None:&lt;br /&gt;
                raise RuntimeError(&amp;quot;capture_array a renvoyé None&amp;quot;)&lt;br /&gt;
            if getattr(img, &amp;quot;size&amp;quot;, 0) == 0:&lt;br /&gt;
                raise RuntimeError(&amp;quot;image vide (size=0)&amp;quot;)&lt;br /&gt;
            if len(getattr(img, &amp;quot;shape&amp;quot;, ())) &amp;lt; 2:&lt;br /&gt;
                raise RuntimeError(f&amp;quot;shape invalide: {getattr(img, 'shape', None)}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
            # Succès&lt;br /&gt;
            return img&lt;br /&gt;
&lt;br /&gt;
        except Exception as e:&lt;br /&gt;
            last_err = e&lt;br /&gt;
            print(f&amp;quot;[Tentative {attempt}/{max_attempts}] Échec acquisition: {e}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
        finally:&lt;br /&gt;
            # Nettoyage propre pour pouvoir repartir clean&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.stop()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
            try:&lt;br /&gt;
                if picam is not None:&lt;br /&gt;
                    picam.close()&lt;br /&gt;
            except Exception:&lt;br /&gt;
                pass&lt;br /&gt;
&lt;br /&gt;
        time.sleep(retry_delay_s)&lt;br /&gt;
&lt;br /&gt;
    raise RuntimeError(f&amp;quot;Impossible de capturer une image après {max_attempts} tentatives. Dernière erreur: {last_err}&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
if __name__ == &amp;quot;__main__&amp;quot;:&lt;br /&gt;
    img = capture_image_with_retries(max_attempts=10)&lt;br /&gt;
    print(&amp;quot;Capture OK:&amp;quot;, img.shape, img.dtype)&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20808</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20808"/>
				<updated>2026-02-02T14:16:55Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Bouts de code Python */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* Acquisition d'image en recommançant en cas d'échec :&lt;br /&gt;
&lt;br /&gt;
{{boîte déroulante/début|titre=[[Media:capture_image.py|Media:capture_image.py]]}}&lt;br /&gt;
test&lt;br /&gt;
{{boîte déroulante/fin}}&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20805</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20805"/>
				<updated>2026-02-02T09:54:57Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs de Fourier&lt;br /&gt;
** On transforme le contour d’un objet (suite de points) en un signal périodique, puis on le décompose en harmoniques avec la transformée de Fourier. Les premiers coefficients décrivent la forme globale et, une fois normalisés, ils permettent de comparer/reconnaître des formes indépendamment de la position, de la taille et de la rotation.&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20804</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20804"/>
				<updated>2026-02-02T09:52:25Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
# Descripteurs de Fourier&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20803</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20803"/>
				<updated>2026-02-02T09:52:07Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
# Normalisation&lt;br /&gt;
fd = np.squeeze(fd)&lt;br /&gt;
&lt;br /&gt;
# Assure forme (nbFD, 2)&lt;br /&gt;
if fd.ndim == 1 and fd.shape[0] == 2*nbFD:&lt;br /&gt;
    fd = fd.reshape(nbFD, 2)&lt;br /&gt;
&lt;br /&gt;
re = fd[:, 0].astype(np.float32)&lt;br /&gt;
im = fd[:, 1].astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
mag = np.sqrt(re*re + im*im)  # rotation-invariant&lt;br /&gt;
mag[0] = 0.0  # enlève la composante continue (liée à la translation)&lt;br /&gt;
&lt;br /&gt;
# normalisation d'échelle (et normalisation globale)&lt;br /&gt;
norm = np.linalg.norm(mag) + 1e-12&lt;br /&gt;
feat = mag / norm&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20802</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20802"/>
				<updated>2026-02-02T09:49:18Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Descripteur et Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
** Il sera nécessaire de normaliser ces descripteurs : https://dsp.stackexchange.com/questions/19982/fourier-descriptors-trying-to-classify-objects&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20801</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20801"/>
				<updated>2026-02-02T09:41:11Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Descripteur et Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	<entry>
		<id>http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20800</id>
		<title>Cours:Classif</title>
		<link rel="alternate" type="text/html" href="http://wikigeii.iut-troyes.univ-reims.fr//index.php?title=Cours:Classif&amp;diff=20800"/>
				<updated>2026-02-02T09:40:39Z</updated>
		
		<summary type="html">&lt;p&gt;Fredmn : /* Classifieur plus évolué */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;center&amp;gt;&lt;br /&gt;
'''&amp;lt;big&amp;gt;TP Classification : détection d'objet en temps réel par vision&amp;lt;/big&amp;gt;'''&lt;br /&gt;
&amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le travail de cette étape va consister à &lt;br /&gt;
* analyser des images acquises en &amp;quot;temps réel&amp;quot; afin de détecter et identifier des objets&lt;br /&gt;
* les objets seront&lt;br /&gt;
** dans un premier temps des jetons de nain jaune&lt;br /&gt;
** dans un second temps des briques lego.&lt;br /&gt;
&lt;br /&gt;
=== Technos matérielles et logicielles ===&lt;br /&gt;
&lt;br /&gt;
Vous utiliserez :&lt;br /&gt;
* Une Rpi 4 que vous programmerez depuis des postes utilisés en terminaux connectés par &amp;lt;code&amp;gt;ssh&amp;lt;/code&amp;gt;, avec redirection graphique  (option &amp;lt;code&amp;gt;- X&amp;lt;/code&amp;gt;).&lt;br /&gt;
* Une camera PiCam Wide (grand angle)&lt;br /&gt;
* Le langage Python accompagné de &lt;br /&gt;
** la librairie libcamera pour les acquisitions :&lt;br /&gt;
*** https://github.com/raspberrypi/libcamera&lt;br /&gt;
*** https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline&lt;br /&gt;
** PIL et numpy pour les traitements bas niveaux&lt;br /&gt;
** les librairies opencv et dlib pour la classification et la reconnaissance :&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/index.html&lt;br /&gt;
*** https://docs.opencv.org/4.5.1/d6/d00/tutorial_py_root.html&lt;br /&gt;
*** https://dlib.net/&lt;br /&gt;
&lt;br /&gt;
Étapes :&lt;br /&gt;
* Connexion à la Rpi et test d'acquisition en ligne de commande&lt;br /&gt;
* Capture d'image et affichage en temps réel, avec Python&lt;br /&gt;
* Prétraitement&lt;br /&gt;
* Reconnaissance simple d'un seul objet, avec descripteurs géométriques&lt;br /&gt;
* Classifieur plus évolué (knn, svm)&lt;br /&gt;
* Plusieurs objets&lt;br /&gt;
&lt;br /&gt;
=== Connexion à la Rpi et test d'acquisition en ligne de commande ===&lt;br /&gt;
&lt;br /&gt;
* Connecter (si cela n'est pas fait) la PiCam à la Rpi4&lt;br /&gt;
* Dans un terminal, se connecter à la Rpi en ssh : &amp;lt;code&amp;gt;ssh -X root@10.98.33.XX&amp;lt;/code&amp;gt;&lt;br /&gt;
* Tester la PiCam avec &amp;lt;code&amp;gt;libcamera-hello&amp;lt;/code&amp;gt; (la capture en video doit s'afficher sur l'écran de la Rpi). Avec les informations affichées, identifier :&lt;br /&gt;
** le modèle du capteur,&lt;br /&gt;
** ses caractéristiques (résolution, format, cadence, etc ...).&lt;br /&gt;
* Tester l'acquisition d'image avec l'éxecutable &amp;lt;code&amp;gt;libcamera-still&amp;lt;/code&amp;gt;&lt;br /&gt;
** Explorer les options de cette application (&amp;lt;code&amp;gt;libcamera-still -h&amp;lt;/code&amp;gt;), en particulier &amp;lt;code&amp;gt;-n&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--immediate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--width&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;--height&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;-o&amp;lt;/code&amp;gt;&lt;br /&gt;
** Voir la page suivante pour le détails des options possibles : https://www.raspberrypi.com/documentation/computers/camera_software.html&lt;br /&gt;
&lt;br /&gt;
=== Capture d'image et affichage en temps réel ===&lt;br /&gt;
&lt;br /&gt;
En exploitant la documentation [https://pip-assets.raspberrypi.com/categories/652-raspberry-pi-camera-module-2/documents/RP-008156-DS-2-picamera2-manual.pdf?disposition=inline Picamera2] (principalement section 6 - ''Capturing images and requests'')&lt;br /&gt;
* Tester les deux exemples ''Capturing arrays'' et ''Capturing PIL images''&lt;br /&gt;
** PIL fait référence à [https://www.pythonware.com/products/pil/ Python Imaging Library] : une bibliothèque Python de traitement d'images.&lt;br /&gt;
* Écrire un script Python qui :&lt;br /&gt;
** initialise la camera&lt;br /&gt;
** affiche en continu son image&lt;br /&gt;
** sur l'appui d'une touche, réalise une capture (dans un objet &amp;lt;code&amp;gt;array&amp;lt;/code&amp;gt; ou &amp;lt;code&amp;gt;PIL&amp;lt;/code&amp;gt;) et sauvegarde l'image dans un fichier&lt;br /&gt;
&lt;br /&gt;
En pratique :&lt;br /&gt;
* Vous pouvez lancer un interpréteur Python dans le terminal pour tester des choses&lt;br /&gt;
* Vous pouvez accéder aux dossiers de la Rpi depuis votre PC fixe, depuis le navigateur Dolphin avec comme url &amp;lt;code&amp;gt;sftp://root@10.98.33.83:22/&amp;lt;/code&amp;gt;. Ce qui vous permettra par exemple d'éditer le fichier script depuis votre PC fixe.&lt;br /&gt;
* Dans le terminal, &amp;lt;code&amp;gt;python monscript.py&amp;lt;/code&amp;gt; pour executer votre script&lt;br /&gt;
* On peut facilement attendre l'appui d'une touche avec &amp;lt;code&amp;gt;cv2.waitkey()&amp;lt;/code&amp;gt;&lt;br /&gt;
* Référence Python :&lt;br /&gt;
** [https://docs.python.org/fr/3/tutorial/ Le tutoriel Python]&lt;br /&gt;
&lt;br /&gt;
=== Prétraitement ===&lt;br /&gt;
&lt;br /&gt;
* Modification éventuelle de la zone de capture de la camera (''crop'').&lt;br /&gt;
* Conversion en image niveaux de gris, sur 8 bits.&lt;br /&gt;
* Binarisation (en mettant l'objet à 255, le fond à 0).&lt;br /&gt;
&lt;br /&gt;
=== Reconnaissance simple d'un seul objet, avec descripteurs géométriques élémentaires ===&lt;br /&gt;
&lt;br /&gt;
* En ne plaçant qu'un seul objet dans le champ de la camera, calculer et afficher ses descripteurs de forme : longueur et largeur de l'objet, cf https://raphael.candelier.fr/?blog=Image%20Moments (calcul de ''l'' et ''w'')&lt;br /&gt;
* Construire une décision idoine à l'aide de la largeur et de la longueur de l'ellipse englobante, afin de discrimer trois classes d'objets (par exemple : jeton court, jeton long, jeton rond)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Exemple de calcul de &amp;lt;code&amp;gt;l&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;w&amp;lt;/code&amp;gt; à partir d'une image binaire représentée dans un tableau &amp;lt;code&amp;gt;numpy&amp;lt;/code&amp;gt; (&amp;lt;code&amp;gt;arr&amp;lt;/code&amp;gt; de type &amp;lt;code&amp;gt;ndarray&amp;lt;/code&amp;gt;) :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
img = arr.astype(np.float64, copy=False)&lt;br /&gt;
H, W = img.shape&lt;br /&gt;
yy, xx = np.indices((H, W))&lt;br /&gt;
&lt;br /&gt;
M00 = float(np.sum(img))&lt;br /&gt;
M10 = float(np.sum(xx * img))&lt;br /&gt;
M01 = float(np.sum(yy * img))&lt;br /&gt;
M11 = float(np.sum(xx * yy * img))&lt;br /&gt;
M20 = float(np.sum((xx * xx) * img))&lt;br /&gt;
M02 = float(np.sum((yy * yy) * img))&lt;br /&gt;
&lt;br /&gt;
xm = int(M10 / M00)&lt;br /&gt;
ym = int(M01 / M00)&lt;br /&gt;
&lt;br /&gt;
mu20 = M20 / M00 - xm * xm&lt;br /&gt;
mu02 = M02 / M00 - ym * ym&lt;br /&gt;
mu11 = M11 / M00 - xm * ym&lt;br /&gt;
&lt;br /&gt;
t = math.sqrt(4.0 * mu11 * mu11 + (mu20 - mu02) * (mu20 - mu02))&lt;br /&gt;
l = int(math.sqrt(8.0 * (mu20 + mu02 + t)))&lt;br /&gt;
w = int(math.sqrt(8.0 * (mu20 + mu02 - t)))&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Classifieur plus évolué ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* Descripteurs&lt;br /&gt;
** Méthodologie :&lt;br /&gt;
*** image en niveau de gris (éventuellement binarisation) : &amp;lt;code&amp;gt;cv2.cvtColor()&amp;lt;/code&amp;gt;&lt;br /&gt;
*** détection des contours : &amp;lt;code&amp;gt;cv2.Canny()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;v2.findContours()&amp;lt;/code&amp;gt; &lt;br /&gt;
*** description des contours : &amp;lt;code&amp;gt;cv2.ximgproc.fourierDescriptor()&amp;lt;/code&amp;gt;&lt;br /&gt;
** Références :&lt;br /&gt;
*** https://docs.opencv.org/4.x/d4/d73/tutorial_py_contours_begin.html&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#gadf1ad6a0b82947fa1fe3c3d497f260e0&lt;br /&gt;
*** https://docs.opencv.org/4.x/d3/dc0/group__imgproc__shape.html#ga819779b9857cc2f8601e6526a3a5bc71&lt;br /&gt;
** Exemple :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
from picamera2 import Picamera2&lt;br /&gt;
import numpy as np&lt;br /&gt;
import cv2&lt;br /&gt;
&lt;br /&gt;
picam = Picamera2()&lt;br /&gt;
picam.start()&lt;br /&gt;
a = picam.capture_array(&amp;quot;main&amp;quot;)&lt;br /&gt;
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)&lt;br /&gt;
edges = cv2.Canny(gray, 100, 200)&lt;br /&gt;
contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)&lt;br /&gt;
cnt = max(contours, key=cv2.contourArea)&lt;br /&gt;
cnt = cnt.astype(np.float32)&lt;br /&gt;
&lt;br /&gt;
# Paramètres utiles :&lt;br /&gt;
# nbElt = nombre de points ré-échantillonnés sur le contour&lt;br /&gt;
# nbFD  = nombre de descripteurs gardés&lt;br /&gt;
fd = cv2.ximgproc.fourierDescriptor(cnt, nbElt=128, nbFD=16)&lt;br /&gt;
&lt;br /&gt;
print(fd.shape, fd.dtype)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* KNN&lt;br /&gt;
** https://docs.opencv.org/4.5.1/d5/d26/tutorial_py_knn_understanding.html&lt;br /&gt;
* Redressement de la perspective&lt;br /&gt;
** Détecteur de coin de Harris : https://docs.opencv.org/4.5.1/dc/d0d/tutorial_py_features_harris.html&lt;br /&gt;
** &amp;lt;code&amp;gt;cv2.findHomography()&amp;lt;/code&amp;gt; et &amp;lt;code&amp;gt;c2.warpPerspective()&amp;lt;/code&amp;gt;&lt;br /&gt;
** ou https://docs.opencv.org/4.5.1/da/d6e/tutorial_py_geometric_transformations.html&lt;br /&gt;
* SVM&lt;br /&gt;
&lt;br /&gt;
=== Plusieurs objets ===&lt;br /&gt;
&lt;br /&gt;
* Segmentation nécessaire pour séparer les objets.&lt;br /&gt;
* Puis une labellisation pour les numéroter.&lt;br /&gt;
&lt;br /&gt;
=== Bouts de code Python ===&lt;br /&gt;
&lt;br /&gt;
* Convertir une image couleur img (4 canaux) en image OpenCV :&lt;br /&gt;
&amp;lt;source lang=python&amp;gt;&lt;br /&gt;
import cv2&lt;br /&gt;
im_cv = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Références  ===&lt;br /&gt;
&lt;br /&gt;
OpenCV :&lt;br /&gt;
* [https://docs.opencv.org/4.x/d9/df8/tutorial_root.html ''OpenCV Tutorials''] &lt;br /&gt;
* [https://docs.opencv.org/4.x/examples.html Exemples OpenCV en c++]&lt;br /&gt;
* [https://docs.opencv.org/4.x/d0/d72/tutorial_py_knn_index.html ''K-Nearest Neighbour'']&lt;br /&gt;
* [https://docs.opencv.org/4.x/d3/db4/tutorial_py_watershed.html ''Image Segmentation with Watershed Algorithm'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/d3/dc0/group__imgproc__shape.html#gaedef8c7340499ca391d459122e51bef5 ''connected Components''] et son exemple en cpp : https://docs.opencv.org/3.4/de/d01/samples_2cpp_2connected_components_8cpp-example.html#a3&lt;br /&gt;
* [https://docs.opencv.org/4.x/d1/d73/tutorial_introduction_to_svm.html ''Introduction to Support Vector Machines'']&lt;br /&gt;
* [https://docs.opencv.org/3.4/dd/ddc/group__ximgproc__fourier.html ''Fourier Descriptors''], voir également [https://docs.opencv.org/3.4/da/d32/samples_2cpp_2contours2_8cpp-example.html ''Contours example''] pour obtenir les contours d'un objet.&lt;br /&gt;
&lt;br /&gt;
Archives de cette page :&lt;br /&gt;
* [[Cours:Classif_archive]]&lt;/div&gt;</summary>
		<author><name>Fredmn</name></author>	</entry>

	</feed>