But
Le but est de démontrer le fonctionnement de base d’un réseau de neurones artificiels. Cette démonstration passe par la lecture de l’affichage du détecteur de qualité de l’air JSM-131 SE à partir d’un ordinateur et d’une caméra. Les données capturées sont enregistrées dans un fichier texte. Le formatage du fichier permettra d’importer ces données dans Excel pour analyse détaillée.
L’appareil ne fait que présenter les données sur un écran LCD. Une caméra capture ces données. Un programme, en Python, basé sur les réseaux de neurones artificiels interprète l’affichage des segments clairs, les transformant en chaîne de caractères compatibles à l’importation dans Excel.
La caméra se place à courte distance de détecteur, l’image ne doit pas être déformée. Les caméras web communes produisent une image en barillet (bombée), ce qui n’est pas adéquat. La caméra de marque ELP, modèle ELP-USBFHD01M-MFV, placée à 15 cm devant le détecteur, génère une image très peu déformée, de bonne qualité, facilement traitable. L’ouverture du diaphragme de la caméra s’opère manuellement, ce qui offre un ajustement de la luminosité parfaite et stable.
Équipements
Détecteur de qualité de l’air JSM-131 SE
VIVOSUN Moniteur de qualité de l’air – Testeur de CO2 – Détecteur de pollution intérieur – HCHO/TVOC/PM2.5/PM10 – Testeur avec données en temps réel et enregistrement de valeur moyenne pour la maison et le bureau : Amazon.ca: Commerce, Industrie et Science
Caméra
Mini webcam USB à objectif varifocal 2,8-12 mm haute définition USB avec caméra, mise au point manuelle, compatible avec la plupart des OS, mise au point réglable, webcam USB 2.0 haute vitesse : Amazon.ca: Électronique
Référence
Pour s’initier au traitement par réseau de neurones, je vous suggère de visionner le diaporama que j’ai produit sur ce sujet. Voir la partie « Simulation d’un réseau de neurones» :
https://espacerm.com/Rnia/rnia_cible.php?numero_image=001
Fonctionnement du réseau de neurones artificiels
Nous allons simuler, par programmation, un réseau de neurones précablé, fixe, configuré pour exécuter une tâche bien particulière. Ce réseau sera le plus simple possible, ayant une seule couche.
Programmation
Le traitement des images se fait à partir de la bibliothèque de fonctionnalités d’OpenCV.
Examinons, par étape, la programmation
# Défini le port de capture vidéo cap = cv2.VideoCapture(0)
# Lecture en boucle de l'image vidéo while True: _, frame = cap.read()
L’image contenue dans «frame» est encodée en valeurs RGB (Rouge-Green-Blue) ou RVB (Rouge-Vert-Bleu)
Pour le traitement ultérieur, il faut que l’image soit encodée en valeurs HSV (Hue-Saturation-value) ou TSV (Teinte-Saturation-Valeur)
# Conversion de l'image RGB en HSV hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
Création d’un masque qui servira à différentier les segments allumés du reste de l’image. Comme l’écran est en majorité de couleur bleue et que les segments allumés sont blancs, nous allons convertir ce qui est bleu (selon une certaine marge) en blanc, ce qui est blanc en noir.
lower_blue = np.array([38, 112, 87]) upper_blue = np.array([179, 255, 255]) mask = cv2.inRange(hsv, lower_blue, upper_blue)
Affichage à l’écran de l’image originale et de l’image masque
cv2.imshow("Frame", frame) cv2.imshow("Mask", mask)
Recherche de la position des segments
Maintenant, il s’agit de situer la position des segments dans l’image. Nous découpons la fenêtre en partie. Chaque partie correspond à une valeur de la mesure; HCHO, TVOC, PM2.5, PM10, Co2. Nous cherchons à quel pixel correspond le coin supérieur gauche du premier chiffre de la mesure. Par la méthode essais-erreurs, nous trouvons pour HCHO, le pixel y=69, x= 185 que nous pouvons aussi définir comme ligne 69, colonne 185.
""""""""""""""""""""""""""""""""" crop_img = source[y: y+h, x: x+w] """"""""""""""""""""""""""""""""" HCHO = mask[69: 125:, 185: 480] cv2.imshow("HCHO", HCHO)
Par cet affichage, nous pouvons constater aussi que la caméra est bien alignée par rapport au détecteur. L’image ne penche pas, le haut des segments sont tous alignés sur l’horizontal.
Nous trouvons pour TVOC, le pixel y=127, x= 185.
TVOC = mask[127: 183:, 185: 480] cv2.imshow("TVOC", TVOC)
Pour PM2.5, le pixel y=186, x= 185.
PM2_5 = mask[186: 242:, 185: 480] cv2.imshow("PM2_5", PM2_5)
Pour PM10, le pixel y=246, x= 185.
PM10 = mask[246: 302:, 185: 480] cv2.imshow("PM10", PM10)
Pour Co2, le pixel y=305, x= 185.
CO2 = mask[305: 361:, 185: 480] cv2.imshow("CO2", CO2)
Recherche de la position des points décimaux
Il reste à trouver la position des points décimaux.
Pour le HCHO, le pixel 104, 220. Comme la valeur du HCHO sur cet appareil se situe entre 0 et 1.999, il n’y a qu’une position pour le point décimal.
# HCHO maximum = 1.999 HCHO_dot1 = mask[104: 109:, 220: 226] cv2.imshow("HCHO_dot1", HCHO_dot1)
Pour le TVOC, sa valeur se situe entre 0 et 12, il y a deux positions possibles pour le point décimal.
Première position du point décimal TVOC, pixel y=162, x=220 et pour la deuxième position, pixel y=162, x=272
# TVOC maximum = 12 TVOC_dot1 = mask[162: 168:, 220: 226] cv2.imshow("TVOC_dot1", TVOC_dot1) TVOC_dot2 = mask[162: 168:, 272: 278] cv2.imshow("TVOC_dot2", TVOC_dot2)
Il n’y a pas de point décimal pour les autres mesures; PM2.5, PM10, Co2.
Vue globale sur l’écran de l’ordinateur
Le mot «digit» sera employé dans la suite plutôt que «chiffre» pour référer à l’ensemble des 7 segments qui composent un chiffre. Cela a pour but de différentier entre chiffre la valeur 0,1,2,3,4,5,6,7,8,9 et chiffre en tant qu’unité, dizaine, centaine…
# Assignement mémoire pour 5 nombres de 4 «digits» number=np.array([[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]) # Position en y (ligne) du premier pixel de chaque nombre posNumber=np.array([69,127,186,246,305]) # Position en x (col) du premier pixel de chaque «digit» posDigit=np.array([185,239,289,340])
La position en x du premier pixel de chaque «digit» est déterminée encore par essais-erreurs.
La partie «réseau de neurones»
Deux boucles sont imbriquées pour retrouver chaque «digit» de chaque nombre qui correspond à une mesure.
for m in range(0, 5): # 5 Nombres (position 0,1,2,3,4) line=posNumber[m] for j in range(0, 4): # 4 «digits» (position 0,1,2,3) col=posDigit[j]
Chaque «digit» est composé de 7 segments. Les segments sont allumés selon le chiffre à afficher.
Chaque segment est analysé selon l’état de 4 pixels qui en font parti. Les segments sont nommés a,b,c,d,e,f,g.
Le pixel nommé a1 vaut 1 s’il est noir ( == 0) sur le mask sinon il vaut -1
# un pixel blanc sur le mask a comme valeur 255 # un pixel noir sur le mask a comme valeur 0 a1 = 1 if mask[line+1,col+9]== 0 else -1
Quatre pixels de chaque segment sont évalués pour éviter une fausse lecture si un pixel est brouillé par une poussière, un problème de caméra, de transmission, etc. C’est l’avantage du réseau de neurones, la lecture des chiffres est possible même si une partie de l’information est erronée.
# Assignement mémoire pour 5 nombres de 4 «digits» number=np.array([[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]) # Position en y (ligne) du premier pixel de chaque nombre posNumber=np.array([69,127,186,246,305]) # Position en x (col) du premier pixel de chaque «digit» posDigit=np.array([185,239,289,340]) for m in range(0, 5): # 5 Nombres (position 0,1,2,3,4) line=posNumber[m] for j in range(0, 4): # 4 «digits» (position 0,1,2,3) col=posDigit[j] # un pixel blanc sur le mask a comme valeur 255 # un pixel noir sur le mask a comme valeur 0 a1 = 1 if mask[line+1,col+9] == 0 else -1 a2 = 1 if mask[line+1,col+10] == 0 else -1 a3 = 1 if mask[line+1,col+11] == 0 else -1 a4 = 1 if mask[line+1,col+12] == 0 else -1
La position du pixel a1, du segment a, du premier nombre (HCH0), premier «digit» se calcul ainsi:
line = posNumber[0] égal 69 ; col=posDigit[0] égal 185
mask[line+1,col+9]
donc dans le mask[69+1,185+9], soit mask[ligne 70, colonne194]
a1 = 1 if mask[line+1,col+9]== 0 else -1
Chaque segment est évalué
a1 = 1 if mask[line+1,col+9] == 0 else -1 a2 = 1 if mask[line+1,col+10] == 0 else -1 a3 = 1 if mask[line+1,col+11] == 0 else -1 a4 = 1 if mask[line+1,col+12] == 0 else -1 b1 = 1 if mask[line+7,col+20] == 0 else -1 b2 = 1 if mask[line+8,col+20] == 0 else -1 b3 = 1 if mask[line+9,col+20] == 0 else -1 b4 = 1 if mask[line+10,col+20] == 0 else -1 c1 = 1 if mask[line+27,col+20] == 0 else -1 c2 = 1 if mask[line+28,col+20] == 0 else -1 c3 = 1 if mask[line+29,col+20] == 0 else -1 c4 = 1 if mask[line+30,col+20] == 0 else -1 d1 = 1 if mask[line+37,col+9] == 0 else -1 d2 = 1 if mask[line+37,col+10] == 0 else -1 d3 = 1 if mask[line+37,col+11] == 0 else -1 d4 = 1 if mask[line+37,col+12] == 0 else -1 e1 = 1 if mask[line+27,col+0] == 0 else -1 e2 = 1 if mask[line+28,col+0] == 0 else -1 e3 = 1 if mask[line+29,col+0] == 0 else -1 e4 = 1 if mask[line+30,col+0] == 0 else -1 f1 = 1 if mask[line+7,col+0] == 0 else -1 f2 = 1 if mask[line+8,col+0] == 0 else -1 f3 = 1 if mask[line+9,col+0] == 0 else -1 f4 = 1 if mask[line+10,col+0] == 0 else -1 g1 = 1 if mask[line+18,col+9] == 0 else -1 g2 = 1 if mask[line+18,col+10] == 0 else -1 g3 = 1 if mask[line+18,col+11] == 0 else -1 g4 = 1 if mask[line+18,col+12] == 0 else -1
La somme de la valeur trouvée pour chaque pixel pour chaque segment est calculée. Cette somme sera donc comprise entre 4 et -4.
sa=a1+a2+a3+a4 # Segment a sb=b1+b2+b3+b4 # Segment b sc=c1+c2+c3+c4 # Segment c sd=d1+d2+d3+d4 # Segment d se=e1+e2+e3+e4 # Segment e sf=f1+f2+f3+f4 # Segment f sg=g1+g2+g3+g4 # Segment g
La valeur de sortie de chaque neurone est calculée. Exemple pour le neurone «1», il faut additionner la valeur des segments «b» et «c».
# Assignement mémoire pour chaque chiffre à représenter r=np.array([0,1,2,3,4,5,6,7,8,9]) r[0]=sa+sb+sc+sd+se+sf # valeur de sortie du neurone «0» r[1]=sb+sc # valeur de sortie du neurone «1» r[2]=sa+sb+sg+se+sd # valeur de sortie du neurone «2» r[3]=sa+sb+sg+sc+sd # valeur de sortie du neurone «3» r[4]=sb+sc+sf+sg # valeur de sortie du neurone «4» r[5]=sa+sf+sg+sc+sd # valeur de sortie du neurone «5» r[6]=sa+sf+sg+se+sd+sc # valeur de sortie du neurone «6» r[7]=sa+sb+sc # valeur de sortie du neurone «7» r[8]=sa+sb+sc+sd+se+sf+sg # valeur de sortie du neurone «8» r[9]=sa+sb+sc+sd+sf+sg # valeur de sortie du neurone «9»
Prochaine étape, il faut trouver la valeur maximum
mx=max(r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7],r[8],r[9])
Il reste à trouver qui possède la valeur maximum
for i in range(0, 10): if r[i] == mx: break number[m,j]=i
La valeur de i correspond au numéro du neurone qui a la valeur maximum de sortie. Le chiffre du «digit» traité correspond au numéro de neurone. Par exemple number[m,j]=i, si m=1, j=3 et i=7, number[1,3]=7, m=1 correspond à TVOC, j à la position du dernier «digit», celui complètement à droite, sa valeur affichée est 7.
Détection de la position du point décimal pour TVOC
if m == 1: tvocD1a = 1 if mask[166,222] == 0 else -1 tvocD1b = 1 if mask[166,223] == 0 else -1 tvocD1c = 1 if mask[167,222] == 0 else -1 tvocD1d = 1 if mask[167,223] == 0 else -1 tvocD1 = tvocD1a+tvocD1b+tvocD1c+tvocD1d tvocD2a = 1 if mask[166,274] == 0 else -1 tvocD2b = 1 if mask[166,274] == 0 else -1 tvocD2c = 1 if mask[167,275] == 0 else -1 tvocD2d = 1 if mask[167,275] == 0 else -1 tvocD2 = tvocD2a+tvocD2b+tvocD2c+tvocD2d
Impression du nombre trouvé dans la console Python. Attente de 1 seconde (1000 millisecondes) avant de traiter une autre capture de la caméra. Sortir de la boucle si la touche «esc», key==27, est appuyée. Terminaison de la capture et fermeture des fenêtres.
print(number[m]) key = cv2.waitKey(1000) if key == 27: break cap.release() cv2.destroyAllWindows()
Programme complet
# -*- coding: utf-8 -*- """ Créer le 2022-02-03 @auteur: Richard Morel Air Quality Monitor """ import time import cv2 import numpy as np # Défini le port de capture vidéo cap = cv2.VideoCapture(0) # Lecture en boucle de l'image vidéo while True: time.sleep(1) _, frame = cap.read() #print(frame.shape) # Conversion de l'image RGB en HSV hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_blue = np.array([38, 112, 87]) upper_blue = np.array([179, 255, 255]) mask = cv2.inRange(hsv, lower_blue, upper_blue) cv2.imshow("Frame", frame) cv2.imshow("Mask", mask) """"""""""""""""""""""""""""""""" crop_img = source[y: y+h, x: x+w] """"""""""""""""""""""""""""""""" colFin=480 HCHO = mask[69: 125:, 185: colFin] cv2.imshow("HCHO", HCHO) TVOC = mask[127: 183:, 185: colFin] cv2.imshow("TVOC", TVOC) PM2_5 = mask[186: 242:, 185: colFin] cv2.imshow("PM2_5", PM2_5) PM10 = mask[246: 302:, 185: colFin] cv2.imshow("PM10", PM10) CO2 = mask[304: 360:, 185: colFin] cv2.imshow("CO2", CO2) # HCHO maximum = 1.999 HCHO_dot1 = mask[104: 109:, 220: 226] cv2.imshow("HCHO_dot1", HCHO_dot1) # TVOC maximum = 12 TVOC_dot1 = mask[162: 168:, 220: 226] cv2.imshow("TVOC_dot1", TVOC_dot1) TVOC_dot2 = mask[162: 168:, 272: 278] cv2.imshow("TVOC_dot2", TVOC_dot2) # Assignement mémoire pour 5 nombres de 4 «digits» number=np.array([[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]) # Position en y (ligne) du premier pixel de chaque nombre posNumber=np.array([69,127,186,246,305]) # Position en x (col) du premier pixel de chaque «digit» posDigit=np.array([185,239,289,340]) print('-') for m in range(0, 5): # 5 Nombres (position 0,1,2,3,4) line=posNumber[m] for j in range(0, 4): # 4 «digits» (position 0,1,2,3) col=posDigit[j] #print(col) #print(line) # un pixel blanc sur le mask a comme valeur 255 # un pixel noir sur le mask a comme valeur 0 a1 = 1 if mask[line+1,col+9] == 0 else -1 a2 = 1 if mask[line+1,col+10] == 0 else -1 a3 = 1 if mask[line+1,col+11] == 0 else -1 a4 = 1 if mask[line+1,col+12] == 0 else -1 b1 = 1 if mask[line+7,col+20] == 0 else -1 b2 = 1 if mask[line+8,col+20] == 0 else -1 b3 = 1 if mask[line+9,col+20] == 0 else -1 b4 = 1 if mask[line+10,col+20] == 0 else -1 c1 = 1 if mask[line+27,col+20] == 0 else -1 c2 = 1 if mask[line+28,col+20] == 0 else -1 c3 = 1 if mask[line+29,col+20] == 0 else -1 c4 = 1 if mask[line+30,col+20] == 0 else -1 d1 = 1 if mask[line+37,col+9] == 0 else -1 d2 = 1 if mask[line+37,col+10] == 0 else -1 d3 = 1 if mask[line+37,col+11] == 0 else -1 d4 = 1 if mask[line+37,col+12] == 0 else -1 e1 = 1 if mask[line+27,col+0] == 0 else -1 e2 = 1 if mask[line+28,col+0] == 0 else -1 e3 = 1 if mask[line+29,col+0] == 0 else -1 e4 = 1 if mask[line+30,col+0] == 0 else -1 f1 = 1 if mask[line+7,col+0] == 0 else -1 f2 = 1 if mask[line+8,col+0] == 0 else -1 f3 = 1 if mask[line+9,col+0] == 0 else -1 f4 = 1 if mask[line+10,col+0] == 0 else -1 g1 = 1 if mask[line+18,col+9] == 0 else -1 g2 = 1 if mask[line+18,col+10] == 0 else -1 g3 = 1 if mask[line+18,col+11] == 0 else -1 g4 = 1 if mask[line+18,col+12] == 0 else -1 sa=a1+a2+a3+a4 # Segment a sb=b1+b2+b3+b4 # Segment b sc=c1+c2+c3+c4 # Segment c sd=d1+d2+d3+d4 # Segment d se=e1+e2+e3+e4 # Segment e sf=f1+f2+f3+f4 # Segment f sg=g1+g2+g3+g4 # Segment g # Assignement mémoire pour chaque «digit» à représenter r=np.array([0,1,2,3,4,5,6,7,8,9]) r[0]=sa+sb+sc+sd+se+sf # valeur de sortie du neurone «0» r[1]=sb+sc # valeur de sortie du neurone «1» r[2]=sa+sb+sg+se+sd # valeur de sortie du neurone «2» r[3]=sa+sb+sg+sc+sd # valeur de sortie du neurone «3» r[4]=sb+sc+sf+sg # valeur de sortie du neurone «4» r[5]=sa+sf+sg+sc+sd # valeur de sortie du neurone «5» r[6]=sa+sf+sg+se+sd+sc # valeur de sortie du neurone «6» r[7]=sa+sb+sc # valeur de sortie du neurone «7» r[8]=sa+sb+sc+sd+se+sf+sg # valeur de sortie du neurone «8» r[9]=sa+sb+sc+sd+sf+sg # valeur de sortie du neurone «9» mx=max(r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7],r[8],r[9]) #print (mx) for i in range(0, 10): if r[i] == mx: break number[m,j]=i # # HCHO_dot1 = mask[104: 109:, 220: 225] # if m == 1: tvocD1a = 1 if mask[166,222] == 0 else -1 tvocD1b = 1 if mask[166,223] == 0 else -1 tvocD1c = 1 if mask[167,222] == 0 else -1 tvocD1d = 1 if mask[167,223] == 0 else -1 tvocD1 = tvocD1a+tvocD1b+tvocD1c+tvocD1d tvocD2a = 1 if mask[166,274] == 0 else -1 tvocD2b = 1 if mask[166,274] == 0 else -1 tvocD2c = 1 if mask[167,275] == 0 else -1 tvocD2d = 1 if mask[167,275] == 0 else -1 tvocD2 = tvocD2a+tvocD2b+tvocD2c+tvocD2d print(tvocD1) print(tvocD2) print(number[m]) key = cv2.waitKey(100) #(curseur dans une des imshow) if key == 27: break cap.release() cv2.destroyAllWindows()
Essais
Il faut vérifier que chaque «digit» est bien décodé pour les valeurs de 0 à 9. Le truc rapide est de faire « inhaler » un peu de Purel au détecteur ou souffler sur la flamme d’une chandelle près du détecteur. Les «digits» HCH0, TVOC passe par tous les chiffres éventuellement. C’est quand même un bon test pour le Co2 et les autres.
Programme incluant l’écriture des données sur disque
Comme l’image peut tout de même être déformée par la caméra (les «digits» n’ont pas exactement la même dimension et le même espacement), il est préférable de trouver la position exacte de chaque «digit» par essais-erreurs et de modifier le programme en conséquence.
La caméra a été bougée, donc les repères initiaux sont aussi modifiés. Les changements importants sont :
posDigit=np.array([[186,236,285,335],[186,234,284,334],[185,235,285,335],[185,235,283,332],[184,233,283,334]])
col=posDigit[m,j]
Vue globale du début de la boucle
# Assignement mémoire pour 5 nombres de 4 «digits» number=np.array([[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]) # Position en y (ligne) du premier pixel de chaque nombre posNumber=np.array([70,125,181,238,294]) # Position en x (col) du premier pixel de chaque «digit» posDigit=np.array([[186,236,285,335], [186,234,284,334], [185,235,285,335], [185,235,283,332], [184,233,283,334]]) print('-') for m in range(0, 5): # 5 Nombres (position 0,1,2,3,4) line=posNumber[m] for j in range(0, 4): # 4 «digits» (position 0,1,2,3) col=posDigit[m,j]
Les données lues seront écrites dans un fichier «.txt». Le « ; » sert de délimiteur entre les données dans le fichier texte.
DATA_PATH = "C:/Documents/Programmation/Python/Travail/analyse d image/" dataFileName = DATA_PATH + "dataDir_00/dataFile_00.txt" dataFile = open(dataFileName, 'a') #mode «append», ajout #Entête des colonnes dataFile.write('Date;'+'Heure;'+'HCHO;'+'HCHO;'+'TVOC;'+'TVOC;'+'PM2.5;' +'PM2.5;'+'PM10;'+'PM10;'+'Co2;'+'Co2;'+'\r')
Nouveaux «masks» d’essais
Programme complet incluant l’écriture des données sur disque
# -*- coding: utf-8 -*- """ Créer le 2022-02-03 @auteur: Richard Morel Lecture de l'affichage du senseur de qualité de l'air Air Quality Monitor Rechargeable JSM-131 SE Camera ELP-USBFHD01M-MFV # HCHO maximum = 1.999 # TVOC maximum = 12 """ import time import datetime import cv2 import numpy as np DATA_PATH = "C:/Documents/Programmation/Python/Travail/analyse d image/" dataFileName = DATA_PATH + "dataDir_00/dataFile_00.txt" dataFile = open(dataFileName, 'a') #mode «append», ajout #Entête des colonnes dataFile.write('Date;'+'Heure;'+'HCHO;'+'HCHO;'+'TVOC;'+'TVOC;'+'PM2.5;' +'PM2.5;'+'PM10;'+'PM10;'+'Co2;'+'Co2;'+'\r') cap = cv2.VideoCapture(0) """"""""""""""""""""""""""""""""""""""""""""""""""" Lecture de l'affichage du JSM-131 SE """"""""""""""""""""""""""""""""""""""""""""""""""" while True: _, frame = cap.read() # print(frame.shape) hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV) lower_blue = np.array([38, 112, 87]) upper_blue = np.array([179, 255, 255]) mask = cv2.inRange(hsv, lower_blue, upper_blue) cv2.imshow("Frame", frame) cv2.imshow("Mask", mask) """"""""""""""""""""""""""""""""" crop_img = source[y: y+h, x: x+w] """"""""""""""""""""""""""""""""" colFin=480 # HCHO """"""""""""""""""""""""""""""""""""""" ligne 69: l+h= 69+42, colonne 186: 480 """"""""""""""""""""""""""""""""""""""" HCHO = mask[69: 111:, 186: 480] cv2.imshow("HCHO", HCHO) """"""""""""""""""""""""""""""""""""""" ligne 69: l+h= 69+42, colonne 186: c+26 """"""""""""""""""""""""""""""""""""""" g=21 d=38 HCHO1 = mask[69: 111:, 186: 212] cv2.imshow("HCHO1", HCHO1) HCHO2 = mask[69: 111:, 236: 260] cv2.imshow("HCHO2", HCHO2) HCHO3 = mask[69: 111:, 285: 310] cv2.imshow("HCHO3", HCHO3) HCHO4 = mask[69: 111:, 335: 358] cv2.imshow("HCHO4", HCHO4) HCHO4g = mask[69+g: 111:, 186: 358] cv2.imshow("HCHO4g", HCHO4g) HCHO4d = mask[69+d: 111:, 335: 358] cv2.imshow("HCHO4d", HCHO4d) # TVOC TVOC = mask[125: 167:, 186: colFin] cv2.imshow("TVOC", TVOC) TVOC1 = mask[125: 167:, 186: 210] cv2.imshow("TVOC1", TVOC1) TVOC2 = mask[125: 167:, 234: 260] cv2.imshow("TVOC2", TVOC2) TVOC3 = mask[125: 167:, 284: 308] cv2.imshow("TVOC3", TVOC3) TVOC4 = mask[125: 167:, 334: 358] cv2.imshow("TVOC4", TVOC4) TVOC4g = mask[125+g: 167:, 186: 358] cv2.imshow("TVOC4g", TVOC4g) TVOC4d = mask[125+d: 167:, 334: 358] cv2.imshow("TVOC4d", TVOC4d) PM2_5 = mask[181: 223:, 185: colFin] cv2.imshow("PM2_5", PM2_5) PM2_5_1 = mask[181: 223:, 185: 210] cv2.imshow("PM2_5_1", PM2_5_1) PM2_5_2 = mask[181: 223:, 235: 258] cv2.imshow("PM2_5_2", PM2_5_2) PM2_5_3 = mask[181: 223:, 285: 308] cv2.imshow("PM2_5_3", PM2_5_3) PM2_5_4 = mask[181: 223:, 335: 358] cv2.imshow("PM2_5_4", PM2_5_4) PM2_5_4g = mask[181+g: 223:, 185: 358] cv2.imshow("PM2_5_4g", PM2_5_4g) PM2_5_4d = mask[181+d: 223:, 335: 358] cv2.imshow("PM2_5_4d", PM2_5_4d) PM10 = mask[238: 281:, 185: colFin] cv2.imshow("PM10", PM10) PM10_1 = mask[238: 281:, 185: 208] cv2.imshow("PM10_1", PM10_1) PM10_2 = mask[238: 281:, 235: 258] cv2.imshow("PM10_2", PM10_2) PM10_3 = mask[238: 281:, 283: 308] cv2.imshow("PM10_3", PM10_3) PM10_4 = mask[238: 281:, 332: 358] cv2.imshow("PM10_4", PM10_4) PM10_4g = mask[238+g: 281:, 185: 358] cv2.imshow("PM10_4g", PM10_4g) PM10_4d = mask[238+d: 281:, 332: 358] cv2.imshow("PM10_4d", PM10_4d) CO2 = mask[294: 336:, 184: colFin] cv2.imshow("CO2", CO2) CO2_1 = mask[294: 336:, 184: 208] cv2.imshow("CO2_1", CO2_1) CO2_2 = mask[294: 336:, 233: 258] cv2.imshow("CO2_2", CO2_2) CO2_3 = mask[294: 336:, 283: 308] cv2.imshow("CO2_3", CO2_3) CO2_4 = mask[294: 336:, 333: 357] cv2.imshow("CO2_4", CO2_4) CO2_4g = mask[294+g: 336:, 184: 357] cv2.imshow("CO2_4g", CO2_4g) CO2_4d = mask[294+d: 336:, 333: 357] cv2.imshow("CO2_4d", CO2_4d) # HCHO maximum = 1.999 HCHO_dot1 = mask[103: 109:, 220: 226] cv2.imshow("HCHO_dot1", HCHO_dot1) # TVOC maximum = 12 TVOC_dot1 = mask[159: 168:, 220: 226] cv2.imshow("TVOC_dot1", TVOC_dot1) TVOC_dot2 = mask[159: 168:, 271: 277] cv2.imshow("TVOC_dot2", TVOC_dot2) # Assignement mémoire pour 5 nombres de 4 «digits» number=np.array([[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4],[1,2,3,4]]) # Position en y (ligne) du premier pixel de chaque nombre posNumber=np.array([70,125,181,238,294]) # Position en x (col) du premier pixel de chaque «digit» posDigit=np.array([[186,236,285,335], [186,234,284,334], [185,235,285,335], [185,235,283,332], [184,233,283,334]]) print('-') for m in range(0, 5): # 5 Nombres (position 0,1,2,3,4) line=posNumber[m] for j in range(0, 4): # 4 «digits» (position 0,1,2,3) col=posDigit[m,j] #print(line) #print(col) # un pixel blanc sur le mask a comme valeur 255 # un pixel noir sur le mask a comme valeur 0 a1 = 1 if mask[line+1,col+11] == 0 else -1 a2 = 1 if mask[line+1,col+12] == 0 else -1 a3 = 1 if mask[line+1,col+13] == 0 else -1 a4 = 1 if mask[line+1,col+14] == 0 else -1 b1 = 1 if mask[line+8,col+22] == 0 else -1 b2 = 1 if mask[line+9,col+22] == 0 else -1 b3 = 1 if mask[line+10,col+22] == 0 else -1 b4 = 1 if mask[line+11,col+22] == 0 else -1 c1 = 1 if mask[line+29,col+22] == 0 else -1 c2 = 1 if mask[line+30,col+22] == 0 else -1 c3 = 1 if mask[line+31,col+22] == 0 else -1 c4 = 1 if mask[line+32,col+22] == 0 else -1 d1 = 1 if mask[line+38,col+11] == 0 else -1 d2 = 1 if mask[line+38,col+12] == 0 else -1 d3 = 1 if mask[line+38,col+13] == 0 else -1 d4 = 1 if mask[line+38,col+14] == 0 else -1 e1 = 1 if mask[line+29,col+0] == 0 else -1 e2 = 1 if mask[line+30,col+0] == 0 else -1 e3 = 1 if mask[line+31,col+0] == 0 else -1 e4 = 1 if mask[line+32,col+0] == 0 else -1 f1 = 1 if mask[line+8,col+0] == 0 else -1 f2 = 1 if mask[line+9,col+0] == 0 else -1 f3 = 1 if mask[line+10,col+0] == 0 else -1 f4 = 1 if mask[line+11,col+0] == 0 else -1 g1 = 1 if mask[line+21,col+11] == 0 else -1 g2 = 1 if mask[line+21,col+12] == 0 else -1 g3 = 1 if mask[line+21,col+13] == 0 else -1 g4 = 1 if mask[line+21,col+14] == 0 else -1 sa=a1+a2+a3+a4 # Segment a sb=b1+b2+b3+b4 # Segment b sc=c1+c2+c3+c4 # Segment c sd=d1+d2+d3+d4 # Segment d se=e1+e2+e3+e4 # Segment e sf=f1+f2+f3+f4 # Segment f sg=g1+g2+g3+g4 # Segment g r=np.array([0,1,2,3,4,5,6,7,8,9]) r[0]=sa+sb+sc+sd+se+sf # digit 0 r[1]=sb+sc # digit 1 r[2]=sa+sb+sg+se+sd # digit 2 r[3]=sa+sb+sg+sc+sd # digit 3 r[4]=sb+sc+sf+sg # digit 4 r[5]=sa+sf+sg+sc+sd # digit 5 r[6]=sa+sf+sg+se+sd+sc # digit 6 r[7]=sa+sb+sc # digit 7 r[8]=sa+sb+sc+sd+se+sf+sg # digit 8 r[9]=sa+sb+sc+sd+sf+sg # digit 9 mx=max(r[0],r[1],r[2],r[3],r[4],r[5],r[6],r[7],r[8],r[9]) #print (mx) for i in range(0, 10): if r[i] == mx: break number[m,j]=i #print(number[m]) # Détection de la position de la virgule pour TVOC if m == 1: tvocD1a = 1 if mask[162,222] == 0 else -1 tvocD1b = 1 if mask[162,223] == 0 else -1 tvocD1c = 1 if mask[163,222] == 0 else -1 tvocD1d = 1 if mask[163,223] == 0 else -1 tvocD1 = tvocD1a+tvocD1b+tvocD1c+tvocD1d tvocD2a = 1 if mask[162,272] == 0 else -1 tvocD2b = 1 if mask[162,272] == 0 else -1 tvocD2c = 1 if mask[163,273] == 0 else -1 tvocD2d = 1 if mask[163,273] == 0 else -1 tvocD2 = tvocD2a+tvocD2b+tvocD2c+tvocD2d #print(tvocD1) #print(tvocD2) """"""""""""""""""""""""""""""""""""""""""""""""""" Enregistrement des données dans un fichier """"""""""""""""""""""""""""""""""""""""""""""""""" # Débuter les données par un horodatage if m == 0: timeStamp = datetime.datetime.now() date_time = timeStamp.strftime("%Y-%m-%d; %H:%M:%S") data = date_time + ';' # HCHO if m == 0: # Inclure l'ajout de la virgule pour HVCO stHCHO = str(number[m,0]) + '.' + str(number[m,1]) \ + str(number[m,2]) + str(number[m,3]) print(stHCHO) HCHO= float(stHCHO) #print(HCHO) if HCHO >= 0 and HCHO <=1.999: if HCHO >= 0 and HCHO <= 0.080: quality_HCHO = '***** HCHO Excellent;' if HCHO >= 0.081 and HCHO <= 0.100: quality_HCHO = '**** HCHO Good;' if HCHO >= 0.101 and HCHO <= 0.200: quality_HCHO = '*** HCHO Mild;' if HCHO >= 0.201 and HCHO <= 0.500: quality_HCHO = '** HCHO Moderate;' if HCHO >= 0.501 and HCHO <= 1.000: quality_HCHO = '* HCHO Severe;' if HCHO >= 1.001 and HCHO <= 1.999: quality_HCHO = 'HCHO Serious;' else: quality_HCHO = 'HCHO ?;' #print(quality_HCHO) data = data + stHCHO + ';' + quality_HCHO #TVOC if m == 1: stTVOC = str(number[m,0]) # ajout conditionnel de la virgule pour TVOC if tvocD1 == 4 and tvocD2 == -4: stTVOC = stTVOC + '.' stTVOC = stTVOC + str(number[m,1]) # ajout conditionnel de la virgule pour TVOC if tvocD2 == 4 and tvocD1 == -4: stTVOC = stTVOC + '.' stTVOC = stTVOC + str(number[m,2]) stTVOC = stTVOC + str(number[m,3]) print(stTVOC) TVOC= float(stTVOC) #print(TVOC) if TVOC >= 0 and TVOC <=12: if TVOC >= 0 and TVOC <= 0.5: quality_TVOC = '***** TVOC Excellent;' if TVOC >= 0.501 and TVOC <= 0.6: quality_TVOC = '**** TVOC Good;' if TVOC >= 0.601 and TVOC <= 1.5: quality_TVOC = '*** TVOC Mild;' if TVOC >= 1.501 and TVOC <= 3.0: quality_TVOC = '** TVOC Moderate;' if TVOC >= 3.001 and TVOC <= 6.0: quality_TVOC = '* TVOC Severe;' if TVOC >= 6.001 and TVOC <= 12.0: quality_TVOC = 'TVOC Serious;' else: quality_TVOC = 'TVOC ?;' #print(quality_TVOC) data = data + stTVOC + ';' + quality_TVOC # PM2.5 if m == 2: stPM2_5 = str(number[m,0]) + str(number[m,1]) \ + str(number[m,2]) + str(number[m,3]) print(stPM2_5) PM2_5= float(stPM2_5) #print(PM2_5) if PM2_5 >= 0 and PM2_5 <=1000: if PM2_5 >= 0 and PM2_5 <= 35: quality_PM2_5 = '***** PM2.5 Excellent;' if PM2_5 >= 36 and PM2_5 <= 75: quality_PM2_5 = '**** PM2.5 Good;' if PM2_5 >= 76 and PM2_5 <= 100: quality_PM2_5 = '*** PM2.5 Mild;' if PM2_5 >= 101 and PM2_5 <= 150: quality_PM2_5 = '** PM2.5 Moderate;' if PM2_5 >= 151 and PM2_5 <= 250: quality_PM2_5 = '* PM2.5 Severe;' if PM2_5 >= 251 and PM2_5 <=1000: quality_PM2_5 = 'PM2.5 Serious;' else: quality_PM2_5 = 'PM2_5 ?;' #print(quality_PM2_5) data = data + stPM2_5 + ';' + quality_PM2_5 # PM10 if m == 3: stPM10 = str(number[m,0]) + str(number[m,1]) \ + str(number[m,2]) + str(number[m,3]) print(stPM10) PM10= float(stPM10) #print(PM10) if PM10 >= 0 and PM10 <=2000: if PM10 >= 0 and PM10 <= 50: quality_PM10 = '***** PM10 Excellent;' if PM10 >= 51 and PM10 <= 100: quality_PM10 = '**** PM10 Good;' if PM10 >= 101 and PM10 <= 130: quality_PM10 = '*** PM10 Mild;' if PM10 >= 131 and PM10 <= 200: quality_PM10 = '** PM10 Moderate;' if PM10 >= 201 and PM10 <= 300: quality_PM10 = '* PM10 Severe;' if PM10 >= 301 and PM10 <=2000: quality_PM10 = 'PM10 Serious;' else: quality_PM10 = 'PM10 ?;' #print(quality_PM10) data = data + stPM10 + ';' + quality_PM10 # Co2 if m == 4: stCo2 = str(number[m,0]) + str(number[m,1]) \ + str(number[m,2]) + str(number[m,3]) print(stCo2) Co2= float(stCo2) #print(Co2) if Co2 >= 0 and Co2 <=5000: if Co2 >= 0 and Co2 <= 450: quality_Co2 = '***** Co2 Excellent' if Co2 >= 451 and Co2 <=1000: quality_Co2 = '**** Co2 Good' if Co2 >= 1001 and Co2 <=1500: quality_Co2 = '*** Co2 Mild' if Co2 >= 1501 and Co2 <=2000: quality_Co2 = '** Co2 Moderate' if Co2 >= 2001 and Co2 <=3000: quality_Co2 = '* Co2 Severe' if Co2 >= 3001 and Co2 <=5000: quality_Co2 = 'Co2 Serious' else: quality_Co2 = 'Co2 ?' #print(quality_Co2) data = data + stCo2 + ';' + quality_Co2 dataFile.write(data + '\r') data = '' print(quality_HCHO) print(quality_TVOC) print(quality_PM2_5) print(quality_PM10) print(quality_Co2) # en seconde time.sleep(0) # en milliseconde key = cv2.waitKey(100) #(Placer le curseur dans une des imshow) if key == 27: dataFile.close() break cap.release() cv2.destroyAllWindows()
Importation des données dans Excel
Sélectionner « Ouvrir », puis parcourir pour aller dans le répertoire du fichier « .txt ».
Sélectionner « Tous les fichiers (*.*) », puis le fichier « .txt ».
Suivre les étapes 1,2,3
Sélection du point comme séparateur décimal, puis Terminer
Résultats avec l’option « Filtrer» active
Fonction d’insertion de graphiques
Conclusion
En général, les lectures sont bonnes, mais comme la largeur des segments est mince , le moindre déplacement ou une vibration peut causer une erreur de lecture. La caméra doit être bien centrée par rapport à l’écran de l’appareil de mesure et bien perpendiculaire verticalement et horizontalement pour réduire la déformation de l’image. Une erreur de lecture peut aussi se produire lors de la lecture d’un «digit» pendant de sa phase de changement du chiffre. Ces erreurs sont en général simples à détecter, nous les voyons facilement sur les graphiques, elles sortent du lot.
L’utilisation du «deep learning» augmenterait la précision de la lecture, le système serait moins sensible aux déformations et vibrations, mais sa mise en oeuvre est plus complexe, elle demande plus de travail. C’est une avenue à regarder.
En assemblant la caméra et le détecteur de qualité de l’air dans un montage rigide, les lectures seraient suffisamment fiables et exploitables. Le réseau de neurones artificiel fixe, prédéterminer, à une seule couche est simple à mettre en oeuvre pour cette application et rempli son objectif.