#define IDLE 0 #define SCAN 1 #define ENGAGED 2 /*==================================================================defines===*/ typedef struct { s16 X; s16 Y; char trouve; } POS; /*==================================================================types=====*/ void threadTourelle(); POS scan(); void ServoAppuyerSurBoutonBluetooth(); void AllerAGauche(u16 delta); void AllerADroite(u16 delta); void AllerEnHaut(u16 delta); void AllerEnBas(u16 delta); /*=================================================================prototypes=*/ // beaucoup de globales mais aucun extern c'est comme ça que ça marche u8 mode = IDLE; u8 fermer = 0; s16 angleHorizontal = 0; s16 angleVertical = 0; /*=================================================================globales===*/ // si pas en mode IDLE, ne fait rien. // si mode SCAN, allume la caméra prend une image de la caméra, la scanne // la fonction de scan retourne une position X,Y du centre de la forme. // si cible identifiée, détermine par rapport au frame précédent void threadTourelle() { POS cible = {-1,-1, 0}; // position X,Y identifiée u8 it = 0; // nombre d'itérations de boucle en mode ENGAGED sans spot, pour revenir au mode SCAN PIX rouge; // image 1 channel résultat du scan // 1 bit par pixel ça serait mieux voir bits.c // init image tampon //lastframe.width = x_res; //lastframe.height = y_res; lastframe.nchannels = 3; lastframe.len = x_res*y_res; lastframe.pixels = (char*)malloc(lastframe.len * 3 + 1); rouge.nchannels = 1; rouge.len = lastframe.len / 3; //rouge.pixels = (char*)malloc(rouge.len + 1); printf("Thread tourelle\n"); while(1){ if(fermer) break; // flag activé via le réseau else if(mode==IDLE) { // je ne fais rien, je ne regarde même pas la caméra, je dors en attendant un ordre du réseau } else{ grabFrameFromCamera(); // prend une image de la camera ce qui rafraichit les pixels de lastframe //POS x = cible; POS x = scan(rouge.len); // cherche l'objet de couleur //u8 trouve = (x.X != -1); if(mode==SCAN){ if(x.trouve){ printf("cible spotted en X:%d Y:%d\n", x.X, x.Y); mode = ENGAGED; ServoAppuyerSurBoutonBluetooth(); // passe en mode enregistrement vidéo Nikon } // pas trouvé, continue de tourner. Angle de vision 65° donc doit tourner 6 fois pour faire un tour complet? else AllerADroite(10); } else if(mode==ENGAGED){ if(x.trouve){ // prédit où la cible va par rapport au frame précédent, pour la suivre // s'il est dans le carré central de 40X40 ne rien faire s16 delta = x.X - cible.X; if (delta > 40) AllerADroite(delta); // il va un peu trop à droite, je tourne à droite else if(delta < -40) AllerAGauche(delta); // il va à gauche, je tourne à gauche delta = x.Y - cible.Y; if (delta > 20) AllerEnBas(delta); // il va en bas, je descend else if(delta < -20) AllerEnHaut(delta); // il va en haut, je monte } else { // on est en mode ENGAGED et pas trouvé sur 20 itérations: arrête de filmer et repasse en mode SCAN it++; if(it == 20) {mode=SCAN; ServoAppuyerSurBoutonBluetooth(); CommonV4l2_deinit(&common_v4l2); it=0;} AllerADroite(10);// ENGAGED et pas trouvé, scanne } } cible.X = x.X; cible.Y = x.Y; } usleep(400000); //sleep microsecondes. ici 900ms entre les appels } //free(rouge.pixels); //free(lastframe.pixels); } /*============================================================================ Algo expérimental de détectiopn de forme qui assume que le processeur est plus doué pour faire des soustractions que des divisions, sans se soucier de ce que font les autres librairies OpenCV, motion etc. Survole l'image PPM 320x240 3 channels de la caméra en regardant le canal rouge. Ces pixels du canal rouge constituent une image PGM 320x240 noir et blanc 1 channel. Elle peut être écrite en local avec save_PGM(). (Elle pourrait être envoyée au client mais faut adapter le code client et envoi 1 octet par pixel ça serait 3 fois plus petit à transférer sur le réseau. Mais sans couleur et qualite moindre (1 seul canal sans moyenne des 3 canaux pour économiser le calcul). Pour la détection proprement dite, ces pixels PGM (3X moins de pixels) sont parcourus ligne par ligne en segments de 20 pixels horizontaux pour calculer la luminosité moyenne des 20 derniers pixels, afin de la comparer à la luminosité moyenne du segment précédent. Si delta de luminosité trop important, alors ces 20 pixels sont considérés comme suspects, donc scanne au même endroit les ligne en-dessous pour sortir la forme et la tracer en rouge sur l'image envoyée au client, pour voir ce que le programme fait. Les variables nombre de segments par ligne et delta devraient être exportés ou configurables en temps réel. Ceci fait détermine la position du centre de la forme pour que la fonction de scan retourne sa position. En X: (position X max - position X min) / 2 (Pour gagner du temps scanne d'abord le carré central, ou une bande centrale. si pas trouvé cherche autour la bande au-dessus puis la bande au-dessous. À ce stade le frame a été complètement scanné. Mais il a commencé à chercher dans la bande centrale, ce qui est plus judicieux sur une caméra 1080p pour trouver l'objet le plus rapidement possible en scannant le moins de pixels possible) */ POS scan(u32 nbredpixels){ static POS cible = {-1,-1, 0}; //u32 x, y; // (pour 3X moins de bande passante 228Ko ppm -> 76Ko pgm on s'en fout on envoie l'image couleur pour l'instant) // l'enregistre sur le disque et libère la mémoire, jusque là ça marche au poil. // faut l'envoyer, pour l'instant envoiUneImage() envoie lastframe.pixels. Le client et le scan aussi doivent capter le coup d'un seul channel // nouvelle image 320x240 1 channel noir et blanc qui ne contient que le canal rouge //u32 nbredpixels = lastframe.len / 3; // 1 pixel = 1 octet de 0 à 255 //u8* redpixels = (char*)malloc(nbredpixels+1); //u8 redpixels[x_res*y_res+1]; // parcourt les pixels ligne par ligne static s16 cptligne = 0; // jusqu'au 320e octet static u8 numligne = 0; // 240 max // en segments de 20 pixels horizontaux. Calcule la luminosité moyenne des 20 derniers pixels //u8 numpixels = 20; // nombre de pixels par segment static u8 luminositemoyenne = 0; static u8 luminositeancienne = 0; static u8 deltaImportant = 50; static u8 tailleEnPixelsDunSegment = 20; static u8 nsegs = 0; static u8 b = 0; // index dans le segment 0 à 20 static s16 total = 0; // pour moyenne static s32 n = 0; static s32 c = 0; // Parcourt les pixels de la petite image 1 channel du 1er au dernier // ici locke l'image avant modif car le réseau peut écrire en même temps? while (n < nbredpixels){ //redpixels[n] = lastframe.pixels[3*n]; // valeur du canal rouge //u8 pixel = lastframe.pixels[3*n]; // valeur du canal rouge u8 pixel = lastframe.pixels[c]; // valeur du canal rouge c += 3; // tous les 20 pixels calcule la luminosité moyenne des 20 derniers pixels if(b++ == 20){ luminositemoyenne = (u8)(total / 20); total = 0; //compare la luminosité au bloc d'avant //u8 delta = luminositemoyenne - luminositeancienne; u8 delta = (luminositemoyenne > luminositeancienne) ? luminositemoyenne - luminositeancienne: luminositeancienne - luminositemoyenne; if(nsegs++ > 0 && delta > deltaImportant) { printf("ligne %d segment %d moyenne/ancienne %d/%d écart de %d \n", numligne, nsegs, luminositemoyenne, luminositeancienne, delta); if(nsegs == 16) { // 320/20 = 16 segments par ligne numligne++; cptligne = nsegs = 0; } // ça devient intéressant: b = n-20; // revient 20 octets en arrière while(b < n){ // et trace les 20 suivants en rouge int k = 3*b; lastframe.pixels[k+0] = 255; // sur l'image originale qui est envoyée lastframe.pixels[k+1] = 0; lastframe.pixels[k+2] = 0; b++; } // un segment est suspect. fait pareil sur la ligne dessous // et compare le delta du X minimum/maximum. si delta faible alors on a un objet suspect sur 2 lignes // Forme identifiée. ici Sort du scan? cible.X = n - 10; cible.Y = numligne - 1; cible.trouve = 1; break; } luminositeancienne = luminositemoyenne; b=0; } else total += pixel; n++; } /*redpixels[n] = 0; static int numfic = 0; save_PGM(numfic++, x_res, y_res, redpixels, nbredpixels);*/ //free(redpixels); //memset(rouge.pixels, 0, rouge.len); /*for(y=0; y < y_res; y++){ for(x=0; x < x_res; x++) { t[x][y] } }*/ //redpixels[n]=0; /*while (n < rouge.len){ int k = 3*n; if(lastframe.pixels[k] > lastframe.pixels[k+1] && lastframe.pixels[k] > lastframe.pixels[k+2]){ // ce pixel est rouge rouge[n] = lastframe.pixels[k]; } else rouge[n] = 255; n+=3; }*/ //rouge.pixels[n]=0; /* // dans un premier temps scanne une bande horizontale centrale pour ne pas parcourir tous les pixels for(x=0; x < x_res; x++) { // regarde un rectangle au centre? for(y=40; y < y_res-40; y++) { // l'image fait 240 de haut, en enlevant 100 en bas et 100 en haut il ne reste pas grand chose! u8 *pixel = img->pixels + ((x + y * x_res) * img->nchannels); if(pixel[0] > pixel[1] && pixel[0] > pixel[2]){ // ce pixel est rouge trouve = 1; // regarde les pixels après et dessous // identifie la taille de la forme // et son centre et son contour pour tracer un rectangle autour ou 4 droites en cible cible.X = x; cible.Y = y; break; } //if(pixel[0] > 200 && pixel[1] > 200 && pixel[2] > 200) {trouve = 1; cible.X = x; cible.Y = y; break;} // objet blanc } if(trouve) break; } */ /* pas trouvé? regarde la bande du haut if(!trouve) { for(x=0; x<img->width; x++) { for(y=0; y<100; y++) { u8 *pixel = img->pixels + (x+ y * img->width) * img->nchannels; if(pixel[0] > 200 && pixel[1] > 200 && pixel[2] > 200) {trouve = 1; cible.X = x; cible.Y = y; break;} } if(trouve) break; } } // pas trouvé? regarde au-dessous de la bande if(!trouve) { for(x=0; x<img->width; x++) { for(y=img->height-100; y<img->height; y++) { u8 *pixel = img->pixels + (x+ y * img->width) * img->nchannels; if(pixel[0] > 200 && pixel[1] > 200 && pixel[2] > 200) {trouve = 1; cible.X = x; cible.Y = y; break;} } if(trouve) break; } }*/ return cible; } /*============================================================================*/ // actionne le moteur 1 void AllerAGauche(u16 delta){ angleHorizontal -= delta; printf("\taller à gauche %d\n", angleHorizontal); // envoi port série vers Arduino bip bip } void AllerADroite(u16 delta){ angleHorizontal += delta; printf("\taller à droite %d\n", angleHorizontal); // envoi port série bip bip } /*============================================================================*/ void ServoAppuyerSurBoutonBluetooth(){ printf("Appui sur le bouton de la télécommande bluetooth"); } /*============================================================================*/ // actionne le moteur 2 void AllerEnHaut(u16 delta){ // if(pos + delta > minRange) ... angleVertical += delta; printf("\taller en haut %d\n", angleVertical); } void AllerEnBas(u16 delta){ // if(pos + delta < maxRange) ... angleVertical -= delta; printf("\taller en bas %d\n", angleVertical); }
tourelle.h 163 lignes, 6002 octets