summaryrefslogtreecommitdiff
path: root/2004
diff options
context:
space:
mode:
authorgaillaro2004-08-04 18:46:55 +0000
committergaillaro2004-08-04 18:46:55 +0000
commitde8a31213c284ad6b8e0f46c78e58260a58e8816 (patch)
tree56ea8043fd3fc394ee203a4775f34125346249d2 /2004
parent2e1bc9aa83b1ac8139a4cbd2d54e1e4a5002ecc9 (diff)
Version été 2004 n°1
Diffstat (limited to '2004')
-rw-r--r--2004/i/nono/src/ovision/Makefile.defs23
-rw-r--r--2004/i/nono/src/ovision/adjust.h27
-rw-r--r--2004/i/nono/src/ovision/comm.cc59
-rw-r--r--2004/i/nono/src/ovision/comm.h40
-rw-r--r--2004/i/nono/src/ovision/convertImg.cc73
-rw-r--r--2004/i/nono/src/ovision/convertImg.h43
-rw-r--r--2004/i/nono/src/ovision/group.cc249
-rw-r--r--2004/i/nono/src/ovision/group.h41
-rw-r--r--2004/i/nono/src/ovision/img.cc81
-rw-r--r--2004/i/nono/src/ovision/img.h46
-rw-r--r--2004/i/nono/src/ovision/imgInterface.cc114
-rw-r--r--2004/i/nono/src/ovision/imgInterface.h51
-rw-r--r--2004/i/nono/src/ovision/mainui.h22
-rw-r--r--2004/i/nono/src/ovision/map.cc69
-rw-r--r--2004/i/nono/src/ovision/map.h27
-rw-r--r--2004/i/nono/src/ovision/mapOld.cc299
-rw-r--r--2004/i/nono/src/ovision/oconfig.cc220
-rw-r--r--2004/i/nono/src/ovision/oconfig.h68
-rw-r--r--2004/i/nono/src/ovision/optim.cc12
-rw-r--r--2004/i/nono/src/ovision/optimCam.cc105
-rw-r--r--2004/i/nono/src/ovision/ovision.cc104
-rw-r--r--2004/i/nono/src/ovision/ovision.h66
-rw-r--r--2004/i/nono/src/ovision/segmLearn.cc195
-rw-r--r--2004/i/nono/src/ovision/segmLearn.h56
-rw-r--r--2004/i/nono/src/ovision/segmNN.cc289
-rw-r--r--2004/i/nono/src/ovision/segmNN.h88
-rw-r--r--2004/i/nono/src/ovision/segmTable.cc109
-rw-r--r--2004/i/nono/src/ovision/segmTable.h55
-rw-r--r--2004/i/nono/src/ovision/space.cc3
-rw-r--r--2004/i/nono/src/ovision/space.h34
-rwxr-xr-x2004/i/nono/src/ovision/startui4
-rw-r--r--2004/i/nono/src/ovision/test.rgb1
-rw-r--r--2004/i/nono/src/ovision/test_dist.cc39
-rw-r--r--2004/i/nono/src/ovision/test_group.cc39
-rw-r--r--2004/i/nono/src/ovision/test_map.cc50
-rw-r--r--2004/i/nono/src/ovision/test_ovision.cc75
-rw-r--r--2004/i/nono/src/ovision/test_ovision_ogl.cc3
-rw-r--r--2004/i/nono/src/ovision/testcam.cc72
-rw-r--r--2004/i/nono/src/ovision/testsave.cc59
-rw-r--r--2004/i/nono/src/ovision/ui.cc104
-rw-r--r--2004/i/nono/src/ovision/ui.h33
41 files changed, 2320 insertions, 827 deletions
diff --git a/2004/i/nono/src/ovision/Makefile.defs b/2004/i/nono/src/ovision/Makefile.defs
index 7e5d67f..a563ac5 100644
--- a/2004/i/nono/src/ovision/Makefile.defs
+++ b/2004/i/nono/src/ovision/Makefile.defs
@@ -1,29 +1,30 @@
-TARGETS += test_ovision testimg testmap optim test_ovision_tracker
+TARGETS += test_ovision testimg test_group test_map optim test_ovision_tracker
LIBS += ovision.a
testimg_SOURCES = testimg.cc ovision.a video4linux.a
-test_ovision_SOURCES = test_ovision.cc ovision.a image.a video4linux.a motor.a config.a serial.a
+test_ovision_SOURCES = test_ovision.cc ovision.a image.a video4linux.a
test_ovision_ogl_SOURCES = test_ovision_ogl.cc ovision.a image.a video4linux.a motor.a config.a serial.a
test_ovision_tracker_SOURCES = test_ovision_tracker.cc ovision.a image.a \
video4linux.a motor.a date.a \
serial.a utils.a logger.a config.a
-testmap_SOURCES = testmap.cc ovision.a image.a video4linux.a
-testdist_SOURCES = testdist.cc ovision.a
+test_map_SOURCES = test_map.cc ovision.a image.a
+test_dist_SOURCES = test_dist.cc ovision.a
optim_SOURCES = optim.cc ovision.a image.a
-ovision_a_SOURCES = img.cc group.cc oconfig.cc map.cc segmNN.cc space.cc
+optimCam_SOURCES = optimCam.cc ovision.a image.a video4linux.a motor.a config.a serial.a
+test_group_SOURCES = test_group.cc ovision.a image.a
+ovision_a_SOURCES = img.cc oconfig.cc segmNN.cc imgInterface.cc segmTable.cc segmLearn.cc group.cc space.cc map.cc ovision.cc
-testimg: $(testimg_SOURCES:%.cc=%.o)
+testimg: $(testimg_SOURCES:%.cc=%.o)
+test_group: $(test_group_SOURCES:%.cc=%.o)
test_ovision: $(test_ovision_SOURCES:%.cc=%.o)
test_ovision_ogl: $(test_ovision_ogl_SOURCES:%.cc=%.o)
-
test_ovision_tracker: $(test_ovision_tracker_SOURCES:%.cc=%.o)
-
-testmap: $(testmap_SOURCES:%.cc=%.o)
-
-testdist: $(testdist_SOURCES:%.cc=%.o)
+test_map: $(test_map_SOURCES:%.cc=%.o)
+test_dist: $(test_dist_SOURCES:%.cc=%.o)
optim: $(optim_SOURCES:%.cc=%.o)
+optimCam: $(optimCam_SOURCES:%.cc=%.o)
ovision.a: ${ovision_a_SOURCES:%.cc=ovision.a(%.o)}
diff --git a/2004/i/nono/src/ovision/adjust.h b/2004/i/nono/src/ovision/adjust.h
index 204ea34..b07a49c 100644
--- a/2004/i/nono/src/ovision/adjust.h
+++ b/2004/i/nono/src/ovision/adjust.h
@@ -1,9 +1,28 @@
-#ifndef adjust_h
-#define adjust_h
-// adjust.h
-// nono - Programme du robot Efrei Robotique I1-I2 2004
+// nono2 - programme du robot 2005
+//
// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef adjust_h
+#define adjust_h
/// Constantes d'interruptions
#define QUIT 3
diff --git a/2004/i/nono/src/ovision/comm.cc b/2004/i/nono/src/ovision/comm.cc
index 116b7d8..a47bc02 100644
--- a/2004/i/nono/src/ovision/comm.cc
+++ b/2004/i/nono/src/ovision/comm.cc
@@ -12,15 +12,12 @@ using namespace std;
#include "comm.h"
#include "adjust.h"
-#include <IL/il.h>
+#include "ovision/convertImg.h"
/// Constructeur
/// @param *filename nom de l'image a utiliser
Comm::Comm(char *filename)
{
- // Initialisation de IL
- ilInit();
-
// Copie du nom de l'image courante
strcpy(fname, filename);
@@ -65,7 +62,7 @@ Comm::Comm(char *filename)
data[i] = new unsigned char[img.nbPixels*3];
// Initialisation de la segmentation
- segm = new SegmNN(&img);
+ segm = new SegmLearn (&img);
segm->BuildNN(oconfig->nn_NbCouleurs, LOAD_FROM_FILE);
group = new Group(&img, segm);
@@ -83,9 +80,6 @@ Comm::Comm(char *filename)
/// Destructeur
Comm::~Comm()
{
- // Fermeture de DevIL
- ilShutDown();
-
// Liberation de la memoire
delete oconfig;
delete segm;
@@ -110,17 +104,22 @@ Comm::SegmAndGroup()
{
segm->Segm();
- // Creation de l'image segmentee
- img.DoImg(segm->tabSegm , data[0]);
-
// Creation des groupes
if (group) delete group;
group = new Group(&img, segm);
group->JumpPoints(oconfig->groupColor, oconfig->goalColor);
+ group->ShowZones ();
group->TabOut();
+ group->TabOut(GOAL, false);
+// img.WriteSegm ("out.rgb", group->tabOut);
img.DoImg(group->tabOut, data[1]);
+ // Creation de l'image segmentee
+ img.AddGroupToDisplay (segm->tabSegm, group);
+ img.DoImg(segm->tabSegm , data[0]);
+
+
tex[2] = LoadImage(img.width, img.height, data[0], tex[2]);
tex[5] = LoadImage(img.width, img.height, data[1], tex[5]);
}
@@ -330,7 +329,9 @@ Comm::ExecuteUiCmds(char *buffer)
tex[0] = LoadImage(img.width, img.height, imgLeft[0], tex[0]);
// Conversion en YUV et stockage
- img.RGBtoYUV();
+ ConvertImg convert;
+ convert.RGBtoYUV (img);
+
tex[3] = LoadImage(img.width, img.height, img.tabData, tex[3]);
// NN oconfigure en RGB ou YUV ?
@@ -404,8 +405,16 @@ Comm::ExecuteUiCmds(char *buffer)
cout << img.yuv << endl;
// Conversion en YUV et stockage
img.LoadRGB(fname, 0, 360, 296);
- if (yuvSwitch%3 == 1) img.YUVtoRGB();
- else if (yuvSwitch%3 == 2) img.RGBtoYUV();
+ if (yuvSwitch%3 == 1)
+ {
+ ConvertImg convert;
+ convert.YUVtoRGB (img);
+ }
+ else if (yuvSwitch%3 == 2)
+ {
+ ConvertImg convert;
+ convert.RGBtoYUV (img);
+ }
delete[] imgLeft[1];
imgLeft[1] = new unsigned char[img.nbPixels*3];
memcpy(imgLeft[1], img.tabData, sizeof(char)*img.nbPixels*3);
@@ -416,6 +425,28 @@ Comm::ExecuteUiCmds(char *buffer)
case 'z':
group->ShowZones();
break;
+
+ case 'f':
+ {
+ char buf[20];
+ ZONE *pCur = group->zoneListBall;
+ write (fifo, "Groupes balles:\n", 20);
+ while(pCur) {
+ sprintf(buf, "%u %i %i %i %i\n", pCur->idColor, pCur->xmin, pCur->xmax, pCur->ymin, pCur->ymax);
+ write (fifo, buf, 20);
+ pCur = pCur->next;
+ }
+
+ pCur = group->zoneListGoal;
+ write (fifo, "Groupes poteaux:\n", 20);
+ while(pCur) {
+ sprintf(buf, "%u %i %i %i %i\n", pCur->idColor, pCur->xmin, pCur->xmax, pCur->ymin, pCur->ymax);
+ write (fifo, buf, 20);
+ pCur = pCur->next;
+ }
+ write (fifo, "end\n", 20);
+ }
+ break;
}
DrawGLScene();
diff --git a/2004/i/nono/src/ovision/comm.h b/2004/i/nono/src/ovision/comm.h
index d939bf5..79915c3 100644
--- a/2004/i/nono/src/ovision/comm.h
+++ b/2004/i/nono/src/ovision/comm.h
@@ -1,18 +1,34 @@
-#ifndef comm_h
-#define comm_h
-// comm.h - classe Comm
-// nono - Programme du robot Efrei Robotique I1-I2 2004
+// nono2 - programme du robot 2005
+//
// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
+#ifndef comm_h
+#define comm_h
#include "adjust.h"
-#include "imgFile.h"
-#include "segmNN.h"
-#include "oconfig.h"
-#include "group.h"
+#include "ovision/imgInterface.h"
+#include "ovision/segmLearn.h"
+#include "ovision/oconfig.h"
+#include "ovision/group.h"
#define NB_POINTS_UI 6
@@ -31,13 +47,13 @@ class Comm {
unsigned char* data[2];
/// classe image
- ImgFile img;
+ ImgInterface img;
/// classe config
OConfig *oconfig;
/// classe segmentation
- SegmNN *segm;
+ SegmLearn *segm;
/// classe group
Group *group;
diff --git a/2004/i/nono/src/ovision/convertImg.cc b/2004/i/nono/src/ovision/convertImg.cc
new file mode 100644
index 0000000..111e4a8
--- /dev/null
+++ b/2004/i/nono/src/ovision/convertImg.cc
@@ -0,0 +1,73 @@
+#include "convertImg.h"
+
+#include <math.h>
+
+/// Renvoie le minimum entre 2 nombres
+inline unsigned char min (unsigned char a, unsigned char b)
+{
+ if (a<b) return a;
+ else return b;
+}
+
+
+/// Convertit un tableau de donnees RGB en YUV
+void
+ConvertImg::RGBtoYUV (Img &img)
+{
+ unsigned char r,g,b;
+
+ img.yuv = true;
+
+ // Parcours du tableau et conversion des valeurs RGB en YUV
+ for (unsigned long i=0; i<img.nbPixels; i++) {
+ r = img.tabData[i*3];
+ g = img.tabData[i*3+1];
+ b = img.tabData[i*3+2];
+
+ img.tabData[i*3] = (unsigned char)(0.299*r + 0.587*g + 0.114*b); // Y
+ img.tabData[i*3+1] = (unsigned char)(0-0.147*r - 0.289*g + 0.437*b + 0.5); // U
+ img.tabData[i*3+2] = (unsigned char)(00.615*r - 0.515*g - 0.100*b + 0.5); // V
+ }
+}
+
+/// Convertit un tableau de donnees RGB en YUV
+void
+ConvertImg::RGBtoHSI (Img &img)
+{
+ unsigned char r,g,b;
+
+ img.hsi = true;
+
+ // Parcours du tableau et conversion des valeurs RGB en HSI
+ for (unsigned long i=0; i<img.nbPixels; i++) {
+ r = img.tabData[i*3];
+ g = img.tabData[i*3+1];
+ b = img.tabData[i*3+2];
+
+ img.tabData[i*3] = (unsigned char)(acos((r-0.5f*g-0.5f*b)/(sqrt(1.0f*(r-g)*(r-g)+(r-b)*(g-b))))); // H
+ img.tabData[i*3+1] = (unsigned char)(1-min(min(r,g),b)); // S
+ img.tabData[i*3+2] = (unsigned char)(0.33f*(r+g+b)); // I
+ }
+}
+
+
+/// Convertit un tableau de donnees YUV en RGB
+void
+ConvertImg::YUVtoRGB (Img &img)
+{
+ unsigned char y,u,v;
+
+ img.yuv = false;
+
+ // Parcours du tableau et conversion des valeurs YUV en RGB
+ for (unsigned long i=0; i<img.nbPixels; i++) {
+ y = img.tabData[i*3];
+ u = img.tabData[i*3+1];
+ v = img.tabData[i*3+2];
+
+ img.tabData[i*3] = (unsigned char)(y + (1.4075 * (v - 128)));
+ img.tabData[i*3+1] = (unsigned char)(y + ((0.3455 * (u - 128)) - (0.7169 * (v - 128))));
+ img.tabData[i*3+2] = (unsigned char)(y + (1.7790 * (u - 128)));
+ }
+}
+
diff --git a/2004/i/nono/src/ovision/convertImg.h b/2004/i/nono/src/ovision/convertImg.h
new file mode 100644
index 0000000..22dd569
--- /dev/null
+++ b/2004/i/nono/src/ovision/convertImg.h
@@ -0,0 +1,43 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef convertImg_h
+#define convertImg_h
+#include "img.h"
+
+/// Conversion des images en RGB, YUV, HSI
+class ConvertImg
+{
+
+ public:
+ /// Conversion en YUV
+ void RGBtoYUV (Img &img);
+
+ /// Conversion en HSI
+ void RGBtoHSI (Img &img);
+
+ /// Conversion de YUV en RGB
+ void YUVtoRGB (Img &img);
+};
+
+#endif // convertImg_h
+
diff --git a/2004/i/nono/src/ovision/group.cc b/2004/i/nono/src/ovision/group.cc
index 9c9c158..c998c5e 100644
--- a/2004/i/nono/src/ovision/group.cc
+++ b/2004/i/nono/src/ovision/group.cc
@@ -17,15 +17,13 @@ using namespace std;
/// @param *img classe image
/// @param *segm classe segmNN
-Group::Group(Img *img, SegmNN *segm) {
+Group::Group (Img *img, SegmNN *segm)
+{
// Sauvegarde des pointeurs
Group::img = img;
Group::segm = segm;
oconfig = OConfig::GetInstance ();
- width = img->width;
- height = img->height;
-
tabOut = NULL;
zoneListBall = NULL;
zoneListGoal = NULL;
@@ -33,12 +31,16 @@ Group::Group(Img *img, SegmNN *segm) {
/// Destructeur
-Group::~Group() {
+Group::~Group ()
+{
+ FreeGroups ();
}
/// Supprime la liste des groupes
-void Group::FreeGroups () {
+void
+Group::FreeGroups ()
+{
ZONE *pCur = zoneListBall;
ZONE *pPrev;
@@ -64,38 +66,103 @@ void Group::FreeGroups () {
}
+/// Ajoute une balle ou un poteau la liste de groupes
+
+/// @param type type du group rechercher GOAL ou BALL
+/// @param numColor numro de la couleur du group
+/// @param xmin,xmax,ymin,ymax borne du group
+/// @param centerx, centery centre du group
+/// @param dernier lment de la liste o empiler
+void
+Group::AddGroup (int type, int numColor, int xmin, int xmax, int ymin, int ymax, int centerx, int centery, ZONE *pLast)
+{
+ if (!pLast)
+ {
+ if (type == BALL)
+ {
+ zoneListBall = new ZONE;
+ pLast = zoneListBall;
+ }
+ else if (type == GOAL)
+ {
+ zoneListGoal = new ZONE;
+ pLast = zoneListGoal;
+ }
+ }
+ else
+ {
+ pLast->next = new ZONE;
+ pLast = pLast->next;
+ }
+
+ pLast->xmin = xmin;
+ pLast->xmax = xmax;
+ pLast->ymin = ymin;
+ pLast->ymax = ymax;
+
+ pLast->centerx = centerx;
+ pLast->centery = centery;
+ pLast->idColor = numColor;
+
+ // test si la balle est vu partiellement ou completement
+ if (pLast->ymax >= img->height - BORD)
+ {
+ pLast->bottom = true;
+ if ((pLast->xmin <= 0) || (pLast->xmax >= img->width) || (pLast->ymin <= 0))
+ pLast->partial = true;
+ else pLast->partial = false;
+ }
+ else
+ {
+ pLast->bottom = false;
+ pLast->partial = false;
+ }
+
+ pLast->next = NULL;
+}
+
+
/// Construit la table des distances
-void Group::DoDeltaTable ()
+void
+Group::DoDeltaTable ()
{
}
/// Retourne le delta utilis pour la dissociation de 2 balles proches
-int Group::GetDelta (int type, int y)
+/// @param type type du group rechercher GOAL ou BALL
+/// @param y coordonnees de la hauteur de la balle
+int
+Group::GetDelta (int type, int y)
{
- // return 50;
-
if (type == BALL)
return (int)(25 + y*0.1);
+
else return 3*(int)(25 + y*0.1);
}
/// Test si la zone trouv est valide
-bool Group::IsValidZone (int type, int xmin, int xmax, int ymin, int ymax) {
-
-
- // test si la zone trouve un trop petite -> parasite
- if ( (abs(xmin - xmax) < oconfig->minLengthZone) || (abs(ymin - ymax) < oconfig->minLengthZone) )
- return 0;
+/// @param type type du group rechercher GOAL ou BALL
+/// @param xmin,xmax,ymin,ymax borne du group valider
+bool
+Group::IsValidZone (int type, int xmin, int xmax, int ymin, int ymax)
+{
if (type == BALL)
{
+ // test si la zone trouve un trop petite -> parasite
+ if ( (abs(xmin - xmax) < oconfig->minLengthZone) || (abs(ymin - ymax) < oconfig->minLengthZone) )
+ return 0;
}
else if (type == GOAL)
{
+ // test si la zone trouve un trop petite -> parasite
+ if ( (abs(xmin - xmax) * (abs(ymin - ymax) < oconfig->minLengthZone*oconfig->minLengthZone/10)))
+ return 0;
+
// test si la zone trouve touche le haut de l'image
if (ymin > 0) return 0;
}
@@ -107,9 +174,11 @@ bool Group::IsValidZone (int type, int xmin, int xmax, int ymin, int ymax) {
/// Cherche l'objet complet a partir d'un pixel donne
+/// @param type type du group rechercher GOAL ou BALL
/// @param numColor numero de la couleur a chercher
/// @param x,y coordonnees de depart pour la recherche
-void Group::Plague(int type, unsigned char numColor, int x, int y) {
+void Group::Plague (int type, unsigned char numColor, int x, int y)
+{
int xmax = x;
int xmin = x;
int ymax = y;
@@ -117,12 +186,12 @@ void Group::Plague(int type, unsigned char numColor, int x, int y) {
// TODO ajouter une inertie ?
- // Parcours de l'objet trouve de haut en bas
- while ((xmax < img->width-1)&& (segm->FindColorNN(img->tabData + ((++xmax)+y* img->width)*3, 1) == numColor)) {}
- while ((xmin > 0) && (segm->FindColorNN(img->tabData + ((--xmin)+y* img->width)*3, 1) == numColor)) {}
- while ((ymax < img->height-1) && (segm->FindColorNN(img->tabData + (x+(++ymax)* img->width)*3, 1) == numColor)) {}
- while ((ymin > 0) && (segm->FindColorNN(img->tabData + (x+(--ymin)* img->width)*3, 1) == numColor)) {}
-
+ // Parcours de l'objet trouve de haut en bas et de gauche droite
+ while ((xmax < img->width-1)&& (segm->GiveColor(img->tabData + ((++xmax)+y* img->width)*3, true, true) == numColor)) {}
+ while ((xmin > 0) && (segm->GiveColor(img->tabData + ((--xmin)+y* img->width)*3, true, true) == numColor)) {}
+ while ((ymax < img->height-1) && (segm->GiveColor(img->tabData + (x+(++ymax)* img->width)*3, true, true) == numColor)) {}
+ while ((ymin > 0) && (segm->GiveColor(img->tabData + (x+(--ymin)* img->width)*3, true, true) == numColor)) {}
+
// test si la zone trouve est une zone valide
if (!IsValidZone (type, xmin, xmax, ymin, ymax))
return;
@@ -133,6 +202,7 @@ void Group::Plague(int type, unsigned char numColor, int x, int y) {
pCur = zoneListBall;
else if (type == GOAL)
pCur = zoneListGoal;
+
// Calcul du centre de l'image
int centerx, centery;
@@ -142,10 +212,15 @@ void Group::Plague(int type, unsigned char numColor, int x, int y) {
ZONE *pLast=NULL;
int imgY;
+ int delta;
while (pCur) {
imgY = (pCur->centery + centery) / 2;
+ delta = GetDelta(type, imgY);
+
// si on a deja ce groupe on actualise les donnees du groupe
- if ((numColor == pCur->idColor) && (abs(pCur->centerx - centerx) <= GetDelta(type, imgY)) && (abs(pCur->centery - centery) <= GetDelta(type, imgY))) {
+ if ((numColor == pCur->idColor) && (abs(pCur->centerx - centerx) <= delta)
+ && (abs(pCur->centery - centery) <= delta))
+ {
if (xmin < pCur->xmin) pCur->xmin = xmin;
if (xmax > pCur->xmax) pCur->xmax = xmax;
if (ymin < pCur->ymin) pCur->ymin = ymin;
@@ -163,57 +238,15 @@ void Group::Plague(int type, unsigned char numColor, int x, int y) {
// Si il n'est pas presente on l'ajoute
- if (!pCur) {
- if (!pLast)
- {
- if (type == BALL)
- {
- zoneListBall = new ZONE;
- pLast = zoneListBall;
- }
- else if (type == GOAL)
- {
- zoneListGoal = new ZONE;
- pLast = zoneListGoal;
- }
- }
- else
- {
- pLast->next = new ZONE;
- pLast = pLast->next;
- }
-
- pLast->xmin = xmin;
- pLast->xmax = xmax;
- pLast->ymin = ymin;
- pLast->ymax = ymax;
-
- pLast->centerx = centerx;
- pLast->centery = centery;
- pLast->idColor = numColor;
-
- // test si la balle est vu partiellement ou completement
- if (pLast->ymax >= img->height - BORD)
- {
- pLast->bottom = 1;
- if ((pLast->xmin <= 0) || (pLast->xmax >= img->width) || (pLast->ymin <= 0))
- pLast->partial = 1;
- else pLast->partial = 0;
- }
- else
- {
- pLast->bottom = 0;
- pLast->partial = 0;
- }
-
- pLast->next = NULL;
- }
+ if (!pCur)
+ AddGroup (type, numColor, xmin, xmax, ymin, ymax, centerx, centery, pLast);
}
/// Affiche les zones trouvees
-void Group::ShowZones() {
-
+void
+Group::ShowZones ()
+{
ZONE *pCur = zoneListBall;
cout << "Groupes balles:" << endl;
while(pCur) {
@@ -232,51 +265,41 @@ void Group::ShowZones() {
/// Selectionne les points a tester dans l'image
/// @param numColor numero de la couleur a trouver
-void Group::JumpPoints(unsigned char numColorBall, unsigned char numColorGoal) {
-
+void Group::JumpPoints (unsigned char numColorBall, unsigned char numColorGoal)
+{
FreeGroups ();
// Initialisation de la couleur a chercher
numColorBall = segm->index[numColorBall];
- if (numColorGoal != 255)
+ if (numColorGoal != IS_SET)
numColorGoal = segm->index[numColorGoal];
int curColor;
// Parcours d'une partie des pixels de l'image
- for (int x=0; x<img->width; x+=10) {
- for (int y=0; y<img->height; y+=10) {
- // if (tabSegm[y*img->width+x] == numColor)
-
- curColor = segm->FindColorNN(img->tabData + ((y*img->width+x) * 3));
+ for (int x=0; x<img->width; x+=oconfig->jumpPointDist)
+ for (int y=0; y<img->height; y+=oconfig->jumpPointDist)
+ {
+ curColor = segm->GiveColor (img->tabData + ((y*img->width+x) * 3), true, true);
+
if (curColor == numColorBall)
Plague(BALL, numColorBall, x, y);
else if (curColor == numColorGoal)
Plague(GOAL, numColorGoal, x, y);
}
- }
-
- ZONE *pCur;
- for (int k=0; k<2; k++)
- {
- if (k == 0) pCur = zoneListBall;
- else if (k == 1) pCur = zoneListGoal;
-
- while (pCur)
- {
- pCur->ymax = img->height - pCur->ymax;
- pCur = pCur->next;
- }
- }
-
-
}
/// Creation du tableau de RGB pour faire une image
-void Group::TabOut() {
- ZONE *pCur = zoneListBall;
+void
+Group::TabOut (int type, bool init)
+{
+ ZONE *pCur;
+ if (type == BALL)
+ pCur = zoneListBall;
+ else
+ pCur = zoneListGoal;
// On verifie que des groupes ont ete trouve
if (!pCur) {
@@ -284,17 +307,27 @@ void Group::TabOut() {
if (tabOut) delete [] tabOut;
return;
}
-
- // Allocation de la memoire
- if (tabOut) delete [] tabOut;
- tabOut = new unsigned char[img->nbPixels];
-
- // On initialise le tableau pour une image noire
- for (unsigned int i=0; i<img->nbPixels; i++)
- tabOut[i] = 0;
-
+
+ if (init)
+ {
+ // Allocation de la memoire
+ if (tabOut) delete [] tabOut;
+ tabOut = new unsigned char[img->nbPixels];
+
+ // On initialise le tableau pour une image noire
+ for (unsigned int i=0; i<img->nbPixels; i++)
+ tabOut[i] = 0;
+ }
+ else
+ {
+ // Allocation de la memoire
+ if (!tabOut)
+ tabOut = new unsigned char[img->nbPixels];
+ }
+
// Parcours de la liste des zones trouvees
- while (pCur) {
+ while (pCur)
+ {
// Remplissage de la zone avec une couleur
for(int i=pCur->xmin; i<pCur->xmax; i++)
for (int j=pCur->ymin; j<pCur->ymax; j++)
@@ -302,6 +335,8 @@ void Group::TabOut() {
pCur = pCur->next;
}
+
+ cout << endl;
}
diff --git a/2004/i/nono/src/ovision/group.h b/2004/i/nono/src/ovision/group.h
index f7f41cf..878b583 100644
--- a/2004/i/nono/src/ovision/group.h
+++ b/2004/i/nono/src/ovision/group.h
@@ -1,8 +1,27 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
#ifndef group_h
#define group_h
-// group.h - classe Group
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include "img.h"
#include "segmNN.h"
@@ -12,6 +31,8 @@
#define BALL 1
#define GOAL 2
+#define IS_SET 255
+
/// Liste chainee des zones trouvees par la classe group
struct ZONE {
/// bornes de la zone pour x
@@ -49,12 +70,6 @@ class Group {
OConfig *oconfig;
- /// largeur de l'image a analyser
- int width;
-
- /// longueur de l'image a analyser
- int height;
-
public:
/// pointeur vers l'image segmentee
unsigned char *tabSegm;
@@ -78,7 +93,7 @@ class Group {
void JumpPoints(unsigned char numColorBall, unsigned char numColorGoal = 255);
/// Creation du tableau de RGB pour faire une image
- void TabOut();
+ void TabOut (int type = BALL, bool init = true);
/// Affiche les zones trouvees
void ShowZones();
@@ -95,7 +110,11 @@ class Group {
void Plague(int type, unsigned char numColor, int x, int y);
/// Supprime la liste des groupes
- void Group::FreeGroups ();
+ void FreeGroups ();
+
+ /// Ajoute une balle ou un poteau la liste de groupes
+
+ void AddGroup (int type, int numColor, int xmin, int xmax, int ymin, int ymax, int centerx, int centery, ZONE *pLast);
};
diff --git a/2004/i/nono/src/ovision/img.cc b/2004/i/nono/src/ovision/img.cc
index 4fcc7b9..e239922 100644
--- a/2004/i/nono/src/ovision/img.cc
+++ b/2004/i/nono/src/ovision/img.cc
@@ -8,16 +8,13 @@
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
-#include <math.h>
+
using namespace std;
-/// Renvoie le minimum entre 2 nombres
-inline unsigned char min(unsigned char a, unsigned char b) {
- if (a<b) return a;
- else return b;
-}
+
/// Constructeur
-Img::Img (void) {
+Img::Img (void)
+{
// Initialisation des variables
tabData = NULL;
tabSegm = NULL;
@@ -26,71 +23,19 @@ Img::Img (void) {
}
+
/// Destructeur
-Img::~Img (void) {
+Img::~Img (void)
+{
//free tabData
delete [] tabData;
}
-/// Convertit un tableau de donnees RGB en YUV
-void Img::RGBtoYUV() {
- unsigned char r,g,b;
-
- yuv = true;
-
- // Parcours du tableau et conversion des valeurs RBG en YUV
- for (unsigned long i=0; i<nbPixels; i++) {
- r = tabData[i*3];
- g = tabData[i*3+1];
- b = tabData[i*3+2];
-
- tabData[i*3] = (unsigned char)(0.299*r + 0.587*g + 0.114*b); // Y
- tabData[i*3+1] = (unsigned char)(0-0.147*r - 0.289*g + 0.437*b + 0.5); // U
- tabData[i*3+2] = (unsigned char)(00.615*r - 0.515*g - 0.100*b + 0.5); // V
- }
-}
-
-/// Convertit un tableau de donnees RGB en YUV
-void Img::RGBtoHSI() {
- unsigned char r,g,b;
-
- hsi = true;
-
- // Parcours du tableau et conversion des valeurs RBG en HSI
- for (unsigned long i=0; i<nbPixels; i++) {
- r = tabData[i*3];
- g = tabData[i*3+1];
- b = tabData[i*3+2];
-
- tabData[i*3] = (unsigned char)(acos((r-0.5f*g-0.5f*b)/(sqrt(1.0f*(r-g)*(r-g)+(r-b)*(g-b))))); // H
- tabData[i*3+1] = (unsigned char)(1-min(min(r,g),b)); // S
- tabData[i*3+2] = (unsigned char)(0.33f*(r+g+b)); // I
- }
-}
-
-/// Convertit un tableau de donnees YUV en RGB
-void Img::YUVtoRGB()
-{
- unsigned char y,u,v;
-
- yuv = false;
-
- // Parcours du tableau et conversion des valeurs YUV en RGB
- for (unsigned long i=0; i<nbPixels; i++) {
- y = tabData[i*3];
- u = tabData[i*3+1];
- v = tabData[i*3+2];
-
- tabData[i*3] = (unsigned char)(y + (1.4075 * (v - 128)));
- tabData[i*3+1] = (unsigned char)(y + ((0.3455 * (u - 128)) - (0.7169 * (v - 128))));
- tabData[i*3+2] = (unsigned char)(y + (1.7790 * (u - 128)));
- }
-}
-
/// Ecrit les valeurs RGB dans un fichier
-void Img::WriteRGB(char *filename) {
-
+void
+Img::WriteRGB (char *filename)
+{
FILE *file;
file = fopen(filename, "w+");
@@ -99,9 +44,11 @@ void Img::WriteRGB(char *filename) {
fclose(file);
}
-/// Charge les valeurs RGB dans un fichier
-void Img::LoadRGB(char *filename, int mode, int width, int height) {
+/// Charge les valeurs RGB dans un fichier
+void
+Img::LoadRGB (char *filename, int mode, int width, int height)
+{
this->width = width;
this->height = height;
nbPixels = width * height;
diff --git a/2004/i/nono/src/ovision/img.h b/2004/i/nono/src/ovision/img.h
index daeae77..c5c06cb 100644
--- a/2004/i/nono/src/ovision/img.h
+++ b/2004/i/nono/src/ovision/img.h
@@ -1,8 +1,27 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
#ifndef img_h
#define img_h
-// img.h - classe Image
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include "image/image_loader.h"
#include "oconfig.h"
@@ -33,26 +52,7 @@ class Img
Img (void);
/// Destructeur
- virtual ~Img (void);
-
- /// Lis une image en utilisant le peripherique adequate (fichier, cam usb, cam)
-#if 0
- virtual int ReadRaw(char *sourcePath) = 0;
-
- /// Ecriture de l'image sur le disque
- virtual void WriteSegm(char *filename, unsigned char *tabSegm) = 0;
- virtual void WriteSegm(char *filename) = 0;
-#endif
-
- /// Conversion en YUV
- void RGBtoYUV();
-
- /// Conversion en HSI
- void RGBtoHSI();
-
- /// Conversion de YUV en RGB
- void YUVtoRGB();
-
+ ~Img (void);
/// Ecrite des valeurs RGB dans un fichier
void WriteRGB(char *filename);
diff --git a/2004/i/nono/src/ovision/imgInterface.cc b/2004/i/nono/src/ovision/imgInterface.cc
new file mode 100644
index 0000000..c947404
--- /dev/null
+++ b/2004/i/nono/src/ovision/imgInterface.cc
@@ -0,0 +1,114 @@
+
+/// @file imgInterface.cc Chargement des images, conversion en YUV, HSI, detection des contours, transformation d'une image segmentee en RGB, ecriture de l'image sur le disque
+
+#include "imgInterface.h"
+#include <iostream>
+
+/// tableau de couleur utlisation pour la creation de l'image segmentee
+unsigned char tabCol[9][3] =
+ {{0, 0, 0}, {255, 255, 255}, {0, 0, 255},
+ {0,255,0}, {255, 0, 0}, {0, 150, 60},
+ {150,60,0}, {0, 150, 60}, {20, 50, 120}};
+
+
+
+/// Constructor ImgInterface
+ImgInterface::ImgInterface (void) : Img ()
+{
+}
+
+
+/// Destructor ImgInterface
+ImgInterface::~ImgInterface (void)
+{
+}
+
+
+/// Cree un tableau en RGB pour l'affichage a partir d'une image segmentee
+
+/// @param *tabIn pointeur vers un tableau de donnees segmentees
+/// @param *tabOut pointeur vers un tableau de donnees RGB
+void
+ImgInterface::DoImg (unsigned char *tabIn, unsigned char *tabOut)
+{
+ if (tabIn)
+ {
+ // Change les couleurs segmentees en valeurs RGB pour l'affichage
+ for (int i=0; i<(int)(nbPixels);i++)
+ {
+ tabOut[i*3] = tabCol[tabIn[i]][0];
+ tabOut[i*3+1] = tabCol[tabIn[i]][1];
+ tabOut[i*3+2] = tabCol[tabIn[i]][2];
+ }
+ }
+ else
+ {
+ // Si la table donnee est vide on renvoie des couleurs noires
+ for (int i=0; i<(int)(nbPixels);i++)
+ {
+ tabOut[i*3] = 0;
+ tabOut[i*3+1] = 0;
+ tabOut[i*3+2] = 0;
+ }
+ }
+}
+
+
+/// Dessine des contours autour d'une balle
+void
+ImgInterface::DrawBox (unsigned char *tab, ZONE *pCur)
+{
+ unsigned char *pTabMin, *pTabMax;
+ // Parcours de la liste des groupes
+ while (pCur)
+ {
+ // Affiche les coutours horizontaux
+ pTabMin = &tab[pCur->ymin*width+pCur->xmin];
+ pTabMax = &tab[pCur->ymax*width+pCur->xmin];
+ for (int x=pCur->xmin; x<pCur->xmax; x++)
+ {
+ *pTabMin = 1;
+ *pTabMax = 1;
+ ++pTabMin;
+ ++pTabMax;
+ }
+
+ // Affiche les courtours verticaux
+ pTabMin = &tab[pCur->ymin*width+pCur->xmin];
+ pTabMax = &tab[pCur->ymin*width+pCur->xmax];
+ for (int x=pCur->ymin; x<pCur->ymax; x++)
+ {
+ *pTabMin = 1;
+ *pTabMax = 1;
+ pTabMin += width;
+ pTabMax += width;
+ }
+
+ // Affiche une croix au centre
+ pTabMin = &tab[pCur->centery*width+pCur->centerx-5];
+ pTabMax = &tab[(pCur->centery-5)*width+pCur->centerx];
+ for (int x=0; x<10; x++)
+ {
+ *pTabMin = 1;
+ *pTabMax = 1;
+ ++pTabMin;
+ pTabMax += width;
+ }
+ pCur = pCur->next;
+ }
+
+}
+
+/// Ajoute des coutours autour des balles trouves
+void
+ImgInterface::AddGroupToDisplay (unsigned char *tab, Group *group)
+{
+ if (!tab)
+ {
+ std::cerr << "ImgInterface::AddGroupToDisplay tab empty" << std::endl;
+ return;
+ }
+
+ DrawBox (tab, group->zoneListBall);
+ DrawBox (tab, group->zoneListGoal);
+}
diff --git a/2004/i/nono/src/ovision/imgInterface.h b/2004/i/nono/src/ovision/imgInterface.h
new file mode 100644
index 0000000..5160df4
--- /dev/null
+++ b/2004/i/nono/src/ovision/imgInterface.h
@@ -0,0 +1,51 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef imgInterface_h
+#define imgInterface_h
+
+#include "img.h"
+#include "group.h"
+
+/// Chargement des images, conversion en YUV, HSI, detection des contours,
+/// transformation d'une image segmentee en RGB, ecriture de l'image sur le disque
+class ImgInterface : public Img
+{
+
+ /// Dessine des contours autour d'une balle
+ void DrawBox (unsigned char *tab, ZONE *pCur);
+
+ public:
+ ImgInterface();
+
+ /// Destructeur
+ ~ImgInterface (void);
+
+ /// Transformation d'un tableau de valeurs segmentees en RGB
+ void DoImg (unsigned char *tabIn, unsigned char *tabOut);
+
+ /// Ajoute des coutours autour des balles trouves
+ void AddGroupToDisplay (unsigned char *tab, Group *group);
+
+};
+
+#endif // imgInterface_h
diff --git a/2004/i/nono/src/ovision/mainui.h b/2004/i/nono/src/ovision/mainui.h
index 2d550cb..27baef3 100644
--- a/2004/i/nono/src/ovision/mainui.h
+++ b/2004/i/nono/src/ovision/mainui.h
@@ -1,3 +1,25 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
int GetReady();
void SetReady(int i);
diff --git a/2004/i/nono/src/ovision/map.cc b/2004/i/nono/src/ovision/map.cc
index d90b2bd..0e1f255 100644
--- a/2004/i/nono/src/ovision/map.cc
+++ b/2004/i/nono/src/ovision/map.cc
@@ -62,10 +62,12 @@ Map::AddBall(double *pos, ZONE *pZone)
// definit la position de la balle
// ballTmp.zone = (double)((int)(pos[0])%300 + ((int)(pos[1])%300)*7);
+ // Vue de la balle partiel ou en bas de l'ecran
ballTmp.partial = pZone->partial;
if (pZone->bottom)
ballTmp.bottom = oconfig->ball_bottom_time_out ;
else ballTmp.bottom = 0;
+
ballTmp.skepticism = 0;
ballTmp.precision = pZone->centery;
@@ -88,6 +90,7 @@ Map::DelBall(list<tBALL>::iterator &iter)
int
Map::TestSimilarBall(ZONE *pBall, list<tBALL>::iterator &iter)
{
+ // Parcours de toutes les balles dj trouves
for(iter = ball.begin(); iter != ball.end(); iter++)
{
double pos[2] = {pBall->centerx, pBall->centery};
@@ -122,14 +125,14 @@ Map::Angle (double ballPosY, double robotPosY, double distRobotBall)
return acos ((ballPosY - robotPosY)/distRobotBall);
}
-/// Retourne si une balle est locke
+/// Balle locke ?
bool
Map::IsLock()
{
return lock;
}
-/// Lock une balle pour savoir quel balle le robot suit
+/// Lock ou unlock une balle pour savoir quel balle le robot suit
void
Map::SetLock(bool value)
{
@@ -145,6 +148,7 @@ Map::GetCurBallPos (double &x, double &y)
{
x = curBall->position[0];
y = curBall->position[1];
+ cout << x << " " << y << endl;
return 1;
}
return false;
@@ -180,73 +184,30 @@ Map::AddBallsToMap(Group *group)
double pos[2];
double centYMax = 900;
-// bool ballLost = 1;
// On supprime l'ancienne liste de balle
ball.clear ();
+ checkCurBall = 0;
while (pCur)
{
-
+
// On choppe la position par rapport au robot
space->GetLoc (pCur->centerx, pCur->centery, pos[0], pos[1]);
+ AddBall(pos, pCur);
+ checkCurBall = 1;
- // Cherche si la balle est deja dans la liste
- list<tBALL>::iterator iCur;
-
- // On regarde si le group trouv n'est pas en dehors de la table
- if (1) //TODO (pos[0] < TABLE_WIDTH) && (pos[1] < TABLE_HEIGHT))
+ // Place la balle la plus proche dans le pointeur curBall
+ if (pCur->centery < centYMax)
{
- // Si la balle n'est pas dans la liste on l'ajoute
- // if (!TestSimilarBall(pCur, iCur))
- // {
- AddBall(pos, pCur);
- /* }
- // Si elle l'est
- else
- {
- // on regarde si c'est la balle qui a ete lock
- if (IsLock ())
- if (iCur == curBall)
- ballLost = 0;
-
- // on incremente son marqueur de viabilite
- if (iCur->skepticism < oconfig->skepticism_max)
- iCur->skepticism++;
-
- if (pCur->centery <= iCur->precision)
- {
- ///TODO mis a jour de la position
- iCur->position[0] = pCur->centerx;
- iCur->position[1] = pCur->centery;
-
- iCur->bottom = pCur->bottom * oconfig->ball_bottom_time_out;
- }
- }
- */
- checkCurBall = 1;
- if (pCur->centery < centYMax)
- {
- curBall = ball.end ();
- centYMax = pCur->centery;
- }
+ curBall = ball.end ();
+ curBall--;
+ centYMax = pCur->centery;
}
pCur = pCur->next;
}
- // TODO decremente d'autre marqueur
-/* if (IsLock () && ballLost)
- {
- // si la balle sort par le bas pour etre prise
- if (curBall->bottom)
- curBall->bottom--;
- else curBall->skepticism -= oconfig->ball_lost_weight;
- }
-*/
- // presence robot ennemi
-
-
// Detection de poteau ou de goal
pCur = group->zoneListGoal;
if (pCur)
diff --git a/2004/i/nono/src/ovision/map.h b/2004/i/nono/src/ovision/map.h
index d753526..a2d3a15 100644
--- a/2004/i/nono/src/ovision/map.h
+++ b/2004/i/nono/src/ovision/map.h
@@ -1,8 +1,29 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+
#ifndef map_h
#define map_h
-// map.h - classe Map
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include <list>
#include "group.h"
diff --git a/2004/i/nono/src/ovision/mapOld.cc b/2004/i/nono/src/ovision/mapOld.cc
new file mode 100644
index 0000000..9dcb5c0
--- /dev/null
+++ b/2004/i/nono/src/ovision/mapOld.cc
@@ -0,0 +1,299 @@
+// map.cc - Classe Map
+// nono - Programme du robot Efrei Robotique I1-I2 2004
+// Copyright (C) 2004 Olivier Gaillard
+
+/// @file map.cc Fourni la liste des balles presentes sur le terrain et permet le choix rapide et efficace de la prochaine balle a aller chercher
+
+#include "map.h"
+#include <iostream>
+#include <math.h>
+using namespace std;
+
+
+#define TABLE_WIDTH 2100
+#define TABLE_HEIGHT 2400
+
+
+/// Constructeurs.
+Map::Map (Space *space)
+// : motor (Motor::getInstance ()), tracker (motor.getTracker ())
+{
+
+ oconfig = OConfig::GetInstance ();
+
+ lock = 0;
+ treeFound = 0;
+ checkCurBall = 0;
+
+ Map::oconfig = oconfig;
+ Map::space = space;
+
+ posGoal[0] = 105;
+ posGoal[1] = 0;
+}
+
+/// Destructeur.
+Map::~Map (void)
+{
+}
+
+
+void
+Map::GetPosFromLoc (int locImgX, int locImgY, double &posX, double &posY)
+{
+ double locX, locY;
+ double posRobotX, posRobotY, angleRobot;
+
+ space->GetLoc(locImgX, locImgY, locX, locY);
+ // tracker.getPos(posRobotX, posRobotY, angleRobot);
+ space->GetPos(locX, locY, posRobotX, posRobotY, angleRobot, posX, posY);
+}
+
+/// Ajoute une balle a la map
+void
+Map::AddBall(double *pos, ZONE *pZone)
+{
+ tBALL ballTmp;
+
+ ballTmp.position[0] = pos[0];
+ ballTmp.position[1] = pos[1];
+
+ // definit la position de la balle
+ // ballTmp.zone = (double)((int)(pos[0])%300 + ((int)(pos[1])%300)*7);
+
+ ballTmp.partial = pZone->partial;
+ if (pZone->bottom)
+ ballTmp.bottom = oconfig->ball_bottom_time_out ;
+ else ballTmp.bottom = 0;
+ ballTmp.skepticism = 0;
+ ballTmp.precision = pZone->centery;
+
+ // calcul du score partie
+ ballTmp.preScore = 0;
+// oconfig->distance_ball_goal_weight * Dist (pos, posGoal);
+
+ ball.push_back(ballTmp);
+}
+
+/// Supprime une balle de la map
+void
+Map::DelBall(list<tBALL>::iterator &iter)
+{
+ ball.erase(iter);
+ checkCurBall = 0;
+}
+
+/// Test si une balle trouvee correspond a une balle de la map
+int
+Map::TestSimilarBall(ZONE *pBall, list<tBALL>::iterator &iter)
+{
+ for(iter = ball.begin(); iter != ball.end(); iter++)
+ {
+ double pos[2] = {pBall->centerx, pBall->centery};
+ if (Dist(iter->position, pos) < ((iter->partial || pBall->partial)?oconfig->map_error_part:oconfig->map_error))
+ return 1;
+ }
+
+ return 0;
+}
+
+/// Calcul de distance
+double
+Map::Dist(double pos1X, double pos1Y, double pos2X, double pos2Y)
+{
+ double x = pos2X - pos1X;
+ double y = pos2Y - pos1Y;
+ return sqrt((double)(x * x + y * y));
+}
+
+double
+Map::Dist(double *pos1, double *pos2)
+{
+ double x = pos2[0]-pos1[0];
+ double y = pos2[1]-pos1[1];
+ return sqrt((double)(x * x + y * y));
+}
+
+
+double
+Map::Angle (double ballPosY, double robotPosY, double distRobotBall)
+{
+ return acos ((ballPosY - robotPosY)/distRobotBall);
+}
+
+/// Retourne si une balle est locke
+bool
+Map::IsLock()
+{
+ return lock;
+}
+
+/// Lock une balle pour savoir quel balle le robot suit
+void
+Map::SetLock(bool value)
+{
+ lock = value;
+}
+
+
+/// Donne la position de la balle la plus proche ou locke
+bool
+Map::getCurBallPos (double &x, double &y)
+{
+ if (checkCurBall == true)
+ {
+ x = curBall->position[0];
+ y = curBall->position[1];
+ return 1;
+ }
+ return false;
+}
+
+
+/// Retourne si un palmier est sur l'image
+bool
+Map::IsTree ()
+{
+ return treeFound;
+}
+
+
+/// Donne la position du palmier l'image
+bool
+Map::GetTreePos (double &x, double &y)
+{
+ if (treeFound == true)
+ {
+ x = posTree[0];
+ y = posTree[1];
+ return 1;
+ }
+ return 0;
+}
+
+/// Met a jour la map avec une nouvelle liste de balle
+void
+Map::AddBallsToMap(Group *group)
+{
+ ZONE *pCur = group->zoneListBall;
+ double pos[2];
+
+ bool ballLost = 1;
+
+
+ while (pCur)
+ {
+
+ // TODO passer par la classe space
+ //GetPosFromLoc
+ pos[0] = pCur->centerx;
+ pos[1] = pCur->centery;
+
+ // Cherche si la balle est deja dans la liste
+ list<tBALL>::iterator iCur;
+
+ // On regarde si le group trouv n'est pas en dehors de la table
+ if (1) //TODO (pos[0] < TABLE_WIDTH) && (pos[1] < TABLE_HEIGHT))
+ {
+ // Si la balle n'est pas dans la liste on l'ajoute
+ if (!TestSimilarBall(pCur, iCur))
+ {
+ AddBall(pos, pCur);
+ }
+ // Si elle l'est
+ else
+ {
+ // on regarde si c'est la balle qui a ete lock
+ if (IsLock ())
+ if (iCur == curBall)
+ ballLost = 0;
+
+ // on incremente son marqueur de viabilite
+ if (iCur->skepticism < oconfig->skepticism_max)
+ iCur->skepticism++;
+
+ if (pCur->centery <= iCur->precision)
+ {
+ ///TODO mis a jour de la position
+ iCur->position[0] = pCur->centerx;
+ iCur->position[1] = pCur->centery;
+
+ iCur->bottom = pCur->bottom * oconfig->ball_bottom_time_out;
+ }
+ }
+ }
+ pCur = pCur->next;
+ }
+
+ // TODO decremente d'autre marqueur
+ if (IsLock () && ballLost)
+ {
+ // si la balle sort par le bas pour etre prise
+ if (curBall->bottom)
+ curBall->bottom--;
+ else curBall->skepticism -= oconfig->ball_lost_weight;
+ }
+
+ // presence robot ennemi
+
+
+ // Detection de poteau ou de goal
+ pCur = group->zoneListGoal;
+ if (pCur)
+ {
+ treeFound = 1;
+ space->GetLoc (pCur->centerx, pCur->ymin, posTree[0], posTree[1]);
+ }
+ else
+ treeFound = 0;
+}
+
+/// Met a jour les scores
+void
+Map::UpdateMap()
+{
+ double robotPosX, robotPosY, robotAngle;
+ double distRobotBall;
+
+ // On met a jour tous les scores
+ list<tBALL>::iterator iter;
+ for(iter = ball.begin(); iter != ball.end(); iter++)
+ {
+ distRobotBall = Dist(robotPosX, robotPosY, iter->position[0], iter->position[1]);
+
+ iter->score = iter->preScore
+ + oconfig->distance_ball_robot_weight * distRobotBall
+// + oconfig->angle_ball_weight * abs (robotAngle - Angle (iter->position[2], robotPosY, distRobotBall));
+ // + oconfig->ball_density_weight *
+ // + oconfig->ennemy_presence_weight *
+ + oconfig->skepticism_weight * iter->skepticism;
+ // + oconfig->ball_precision_weight *
+ // + ... iter->partial;
+
+ if (checkCurBall)
+ if ((!IsLock()) && (iter->score > curBall->score))
+ {
+ curBall = iter;
+ checkCurBall = 1;
+ }
+ }
+}
+
+
+/// Affiche les balles de la map
+void
+Map::ShowBalls()
+{
+ list<tBALL>::iterator iter;
+ int i=0;
+ cout << "balles: (" << ball.size () << ")" << endl;;
+
+ /// Parcours de la liste
+ for(iter = ball.begin(); iter != ball.end(); iter++)
+ {
+ /// Affichage des infos de la balle
+ cout << i << ": " << iter->position[0] << "\t" << iter->position[1] << "\t" << iter->skepticism << "\t" << iter->score << "\t" << iter->bottom << endl;
+ i++;
+ }
+ cout << endl;
+}
diff --git a/2004/i/nono/src/ovision/oconfig.cc b/2004/i/nono/src/ovision/oconfig.cc
index f424159..98f8ad9 100644
--- a/2004/i/nono/src/ovision/oconfig.cc
+++ b/2004/i/nono/src/ovision/oconfig.cc
@@ -13,20 +13,44 @@ using namespace std;
OConfig *OConfig::instance = 0;
+
+/// Constructor
+/// @param *filename nom du fichier de config
+OConfig::OConfig(char *filename)
+{
+ instance = this;
+
+ Load (filename);
+
+ colorMode = 0;
+ color = NULL;
+ node = NULL;
+ index = NULL;
+ LoadNNFile("rc/poids");
+ LoadDistFile("rc/dist");
+}
+
+/// Destructor
+OConfig::~OConfig()
+{
+}
+
/// Parse une ligne du fichier de config
/// @param *var nom de la variable a fixer
/// @param *arg valeur de la variable
-void OConfig::Parse(char *var, char *arg) {
-
+void
+OConfig::Parse(char *var, char *arg)
+{
char argu[20];
-
+
if (!arg) cerr << "OConfig::Parse : Error during config file parsing" << endl;
strcpy(argu, arg);
-
+
// Verifie si l'argument est un nombre ou un nom de fichier
if (((argu[0]>'9') || (argu[0] < '0'))
- && strcmp(var, "Source") && strcmp(var, "imgPath")) {
+ && strcmp(var, "Source") && strcmp(var, "imgPath"))
+ {
FILE *file;
file=fopen(arg, "r");
@@ -35,55 +59,36 @@ void OConfig::Parse(char *var, char *arg) {
fgets(argu, 20,file);
fclose(file);
- }
+ }
// Affecte la valeur de argu a la variable var
- if (!strcmp(var, "Hauteur_Image")) height = atoi(argu);
- else if(!strcmp(var,"Largeur_Image")) width = atoi(argu);
- else if(!strcmp(var,"NN_Step_Learning")) nn_sl = atof(argu);
- else if(!strcmp(var,"NN_Neighborhood_Learning")) nn_nl = atof(argu);
- else if(!strcmp(var,"NN_Number_Iteration_Learning")) nn_nil = atol(argu);
- else if(!strcmp(var,"NN_Nombre_Couleurs")) nn_NbCouleurs = atoi(argu);
- else if(!strcmp(var,"NN_Threshold_Output")) nn_threshold_output = atoi(argu);
- else if(!strcmp(var,"NN_Influence_Luminosite")) nn_influ_lum = atof(argu);
- else if(!strcmp(var,"imgPath")) strcpy(imgPath, argu);
- else if(!strcmp(var,"NN_Lazy_Threshold")) nn_lazy_threshold = atoi(argu);
- else if(!strcmp(var,"Map_Error")) map_error = atoi(argu);
- else if(!strcmp(var,"Map_Error_Part")) map_error_part = atoi(argu);
- else if(!strcmp(var,"Angle_Ball")) angle_ball_weight= atoi(argu);
- else if(!strcmp(var,"Distance_Ball_Robot")) distance_ball_robot_weight = atoi(argu);
- else if(!strcmp(var,"Distance_Ball_Goal")) distance_ball_goal_weight = atoi(argu);
- else if(!strcmp(var,"Ball_Density")) ball_density_weight = atoi(argu);
- else if(!strcmp(var,"Ennemy_Presence")) ennemy_presence_weight = atoi(argu);
- else if(!strcmp(var,"Ball_Precision")) ball_precision_weight = atoi(argu);
- else if(!strcmp(var,"Skepticism")) skepticism_weight = atoi(argu);
- else if(!strcmp(var,"Skepticism_Max")) skepticism_max = atoi(argu);
- else if(!strcmp(var,"Minimum_Length_Zone")) minLengthZone = atoi(argu);
- else if(!strcmp(var,"Ball_Lost")) ball_lost_weight = atoi(argu);
- else if(!strcmp(var,"Ball_Bottom_Time_Out")) ball_bottom_time_out = atoi(argu);
- else if(!strcmp(var,"One_NN_Learning_Rate")) one_nn_learning_rate = atof(argu);
- else if(!strcmp(var,"One_NN_Learning_Iteration")) one_nn_learning_iteration = atoi(argu);
+ if (!strcmp (var,"NN_step_learning")) nn_sl = atof(argu);
+ else if (!strcmp (var,"NN_neighborhood_learning")) nn_nl = atof(argu);
+ else if (!strcmp (var,"NN_number_of_iteration_learning")) nn_nil = atol(argu);
+ else if (!strcmp (var,"NN_number_of_color_to_segment")) nn_NbCouleurs = atoi(argu);
+ else if (!strcmp (var,"NN_threshold_output")) nn_threshold_output = atoi(argu);
+ else if (!strcmp (var,"NN_luminosity_influence")) nn_influ_lum = atof(argu);
+ else if (!strcmp (var,"UI_img_path ")) strcpy(imgPath, argu);
+ else if (!strcmp (var,"NN_lazy_threshold")) nn_lazy_threshold = atoi(argu);
+ else if (!strcmp (var,"Map_error")) map_error = atoi(argu);
+ else if (!strcmp (var,"Map_error_part")) map_error_part = atoi(argu);
+ else if (!strcmp (var,"Map_angle_ball_weight")) angle_ball_weight= atoi(argu);
+ else if (!strcmp (var,"Map_distance_ball_robot_weight")) distance_ball_robot_weight = atoi(argu);
+ else if (!strcmp (var,"Map_distance_ball_goal_weight")) distance_ball_goal_weight = atoi(argu);
+ else if (!strcmp (var,"Map_ball_density_weight")) ball_density_weight = atoi(argu);
+ else if (!strcmp (var,"Map_ennemy_presence_weight")) ennemy_presence_weight = atoi(argu);
+ else if (!strcmp (var,"Map_ball_precision_weight")) ball_precision_weight = atoi(argu);
+ else if (!strcmp (var,"Map_skepticism_weight")) skepticism_weight = atoi(argu);
+ else if (!strcmp (var,"Map_skepticism_max")) skepticism_max = atoi(argu);
+ else if (!strcmp (var,"Map_ball_lost_weight")) ball_lost_weight = atoi(argu);
+ else if (!strcmp (var,"Map_ball_bottom_time_out")) ball_bottom_time_out = atoi(argu);
+ else if (!strcmp (var,"Group_minimum_length_zone")) minLengthZone = atoi(argu);
+ else if (!strcmp (var,"Group_jump_point_distance")) jumpPointDist = atoi(argu);
}
-/// Constructor
-/// @param *filename nom du fichier de config
-OConfig::OConfig(char *filename) {
-
- instance = this;
-
- Load (filename);
-
-
- colorMode = 0;
- color = NULL;
- node = NULL;
- index = NULL;
- LoadNNFile("rc/poids");
- LoadDistFile("rc/dist");
-}
-
-void OConfig::Load (char *filename)
+void
+OConfig::Load (char *filename)
{
const int NBARG = 3;
char *cut[NBARG] = {NULL};
@@ -95,31 +100,35 @@ void OConfig::Load (char *filename)
file = fopen(filename, "r");
if (!file) cerr << "OConfig::OConfig : Error during config file opening" << endl;
else cout << "Lecture du ficher de configuration" << endl;
-
-
+
+
// Parcours des lignes et analyse
- while(fgets(ligne, 50, file)) {
+ while(fgets(ligne, 50, file))
+ {
if (ligne[0] == '#') continue;
// Division du string
cut[0] = strtok(ligne, " \n");
if (!cut[0]) continue;
i=0;
- while ((cut[i] != NULL) && (i<(NBARG-1))) {
+ while ((cut[i] != NULL) && (i<(NBARG-1)))
+ {
i++;
cut[i] = strtok( NULL, " \t\n");
- }
+ }
Parse(cut[0], cut[2]);
- }
+ }
}
/// Chargement des poids d'un reseau de neurones
/// @param *filePath nom du fichier de poids a charger
-void OConfig::LoadNNFile(char *filePath) {
+void
+OConfig::LoadNNFile (char *filePath)
+{
const int NBARG = 4;
char *cut[NBARG] = {NULL};
FILE *file;
@@ -207,14 +216,16 @@ void OConfig::LoadNNFile(char *filePath) {
nbNodeMax = numNode;
fclose(file);
-
}
+
/// Creation d'un fichier de poids pour le reseau de neurones
/// @param *filename nom du fichier a creer
/// @param mode mode de l'espace de couleur
/// @param nbOutput nombre de couleurs a detecter du reseau de neurones
-void OConfig::CreateNNFile(const char *filename, int mode, int nbOutput) {
+void
+OConfig::CreateNNFile (const char *filename, int mode, int nbOutput)
+{
if (!node) {
cerr << "OConfig::CreateNNFile : NN non initialis\n";
@@ -253,61 +264,10 @@ void OConfig::CreateNNFile(const char *filename, int mode, int nbOutput) {
}
-
-/// Chargement d'un fichier de seuils
-void OConfig::LoadThFile() {
- const int NBARG = 6;
- char *cut[NBARG] = {NULL};
- FILE *file;
- char ligne[50];
- int i;
- unsigned char *pColor;
-
- // Ouverture du fichier de conf
- file = fopen("threshold", "r");
- if (!file) {
- cerr << "OConfig::LoadThFile : Error during config file opening\n" << endl;
- return;
- }
-
- // Nombre de couleurs contenu dans le fichier
- int numColor=0;
- while(fgets(ligne, 50,file))
- numColor++;
-
- if (color) delete [] color;
- color = new unsigned char[numColor*3*2];
-
- // Parcours des lignes et analyse
- numColor=0;
- rewind(file);
- while(fgets(ligne, 50, file)) {
- if (ligne[0] == '#') continue;
-
- // Division du string
- cut[0] = strtok(ligne, " \t\n");
- if (!cut[0]) continue;
- i=0;
- pColor = &color[numColor*6];
- pColor[0] = (unsigned char)atoi(cut[0]);
- while ((cut[i] != NULL) && (i<(NBARG-1))) {
- i++;
- cut[i] = strtok( NULL, " \t\n");
- pColor[i] = (unsigned char)atoi(cut[i]);
- }
- numColor++;
- }
-
- nbCouleurMax = numColor;
-
- fclose(file);
-}
-
-
-
-/// Chargement de la tabPointle des distances
-void OConfig::LoadDistFile(char *filename) {
-
+/// Chargement de la liste des points pour les distances
+void
+OConfig::LoadDistFile (char *filename)
+{
FILE *file;
char buf[50];
@@ -329,43 +289,37 @@ void OConfig::LoadDistFile(char *filename) {
tabPoint.push_back(point[1]);
tabPoint.push_back(point[2]);
tabPoint.push_back(point[3]);
- //cout << point[0] << " " << point[1] << endl;
}
}
- cout << endl;
fclose(file);
+ // Nombre de points venant d'tre charg
nbDistPoint = tabPoint.size () / 4;
}
-/// Creation d'un fichier pour la tabPointle des distances
-void OConfig::CreateDistFile(char *filename, int numPoint) {
-
- if (tabPoint.size () /4) {
- cerr << "OConfig::CreateDistFile : tabPoint vide\n";
+
+/// Creation d'un fichier pour la liste des points pour les distances
+void
+OConfig::CreateDistFile (char *filename, int numPoint)
+{
+ if (tabPoint.size () == 0)
+ {
+ cerr << "OConfig::CreateDistFile : tabPoint vide" << endl;
return;
- }
+ }
// Ecriture dans un fichier
FILE *file;
file = fopen(filename, "w+");
-
+ // Remplissage du fichier
fprintf(file, "#imgX\timgY\tdistX\tdistY\n");
- // for (int i=0; i<numPoint; i++)
-// fprintf(file, "%i\t%i\t%i\t%i\n", tabPoint[i*4+0], tabPoint[i*4+1], tabPoint[i*4+2], tabPoint[i*4+3]);
-
- fclose(file);
-}
-
-
+ // for (int i=0; i<numPoint; i++)
+ // fprintf(file, "%i\t%i\t%i\t%i\n", tabPoint[i*4+0], tabPoint[i*4+1], tabPoint[i*4+2], tabPoint[i*4+3]);
-/// Destructor
-OConfig::~OConfig()
-{
+ fclose(file);
}
-
diff --git a/2004/i/nono/src/ovision/oconfig.h b/2004/i/nono/src/ovision/oconfig.h
index abdf805..1fe43cd 100644
--- a/2004/i/nono/src/ovision/oconfig.h
+++ b/2004/i/nono/src/ovision/oconfig.h
@@ -1,8 +1,28 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
#ifndef config_h
#define config_h
-// oconfig.h - classe OConfig
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include <vector>
@@ -26,20 +46,12 @@ class OConfig {
public:
/////////////////////////////// IMG ///////////////////////////////////////////////////
- /// hauteur de l'image
- unsigned int height;
-
- /// largeur de l'image
- unsigned int width;
- /// source d'aquisition
- int source;
-
- /// mode de couleur (RGB, YUV, HSI)
- int colorMode;
+ int colorMode;
/////////////////////////////// RESEAU DE NEURONES ///////////////////////////////////////////////////
+
/// nombre d'iteration pour l'apprentissage(number iteration learning)
long nn_nil;
@@ -50,7 +62,7 @@ class OConfig {
float nn_nl;
/// seuil pour la verification des noeuds de sorties inutiles du reseau
- unsigned int nn_lazy_threshold;
+ int nn_lazy_threshold;
/// nombre de couleurs a detecter
int nn_NbCouleurs;
@@ -72,6 +84,7 @@ class OConfig {
////////////////////////////////// MAP ///////////////////////////////////////////////////////////////
+
/// erreur accepte pour la construction de la map
int map_error;
int map_error_part;
@@ -108,6 +121,7 @@ class OConfig {
/////////////////////////////// GROUP ////////////////////////////////////////////////////////////////
+
/// numero de la couleur des balles a chercher
int groupColor;
@@ -116,19 +130,25 @@ class OConfig {
/// taille minimum des zones trouves
int minLengthZone;
-
+
+ /// distance des sauts pour la recherche des balles
+ int jumpPointDist;
+
/// tableau de correspondances des couleurs RGB
unsigned char *color;
/////////////////////////////// SPACE ////////////////////////////////////////////////////////////////
/// Points utilises pour le calcul de distance
- std::vector<int> tabPoint;
- int nbDistPoint;
- float one_nn_learning_rate;
+ // Liste des points chargs
+ std::vector<int> tabPoint;
- int one_nn_learning_iteration;
+ // Nombre de points chargs
+ int nbDistPoint;
+
+ //float one_nn_learning_rate;
+ //int one_nn_learning_iteration;
/////////////////////////////// UI ////////////////////////////////////////////////////////////////
/// tableau d'index des couleurs a melanger (merge)
@@ -138,19 +158,13 @@ class OConfig {
char imgPath[30];
/// Constructeur
- OConfig (char *filename);
+ OConfig (char *filename = "rc/vision.conf");
/// Destructeur
~OConfig (void);
- /// Chargement d'un fichier de seuils
- void LoadThFile();
-
- /// Chargement de poids pour le reseau de neurone
- void LoadNNFile(char *filePath);
-
/// Chargement de poids pour le reseau de neurone
- void LoadNNFile() {LoadNNFile("rc/poids");}
+ void LoadNNFile(char *filePath = "rc/poids");
/// Creation d'un fichier de poids pour le reseau de neurones
void CreateNNFile(const char *file, int mode, int nbOutput);
diff --git a/2004/i/nono/src/ovision/optim.cc b/2004/i/nono/src/ovision/optim.cc
index cef97b5..7c547d0 100644
--- a/2004/i/nono/src/ovision/optim.cc
+++ b/2004/i/nono/src/ovision/optim.cc
@@ -5,6 +5,8 @@
#include "group.h"
#include "space.h"
+#include "segmTable.h"
+#include "segmLearn.h"
#include <time.h>
#include <sys/time.h>
@@ -37,6 +39,7 @@ main()
{
/////////////////////////////////////////////////////////////////////////////////////////
/// Initialisation des classes
+ tstart ();
OConfig oconfig("rc/vision.conf");
Img img;
@@ -47,14 +50,17 @@ main()
space.AddSetupPoint (356, 23, 300, 300);
space.AddSetupPoint (283, 171, 600, 600);
space.AddSetupPoint (253, 234, 1000, 900);
- space.Setup ();
+ space.Setup (0.00603759, 0.593767, 291.474);
- SegmNN segmNN(&img);
+ SegmTable segmNN(&img);
segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
+ segmNN.DoColorTable ();
Group group(&img, &segmNN);
Map map(&space);
+ tend ();
+ std::cout << "Initialisation:\t" << tval () << std::endl;
/////////////////////////////////////////////////////////////////////////////////////////
@@ -71,6 +77,8 @@ main()
tend ();
std::cout << "Find Group:\t" << tval () << std::endl;
+ group.ShowZones ();
+
tstart ();
if (group.zoneListBall)
{
diff --git a/2004/i/nono/src/ovision/optimCam.cc b/2004/i/nono/src/ovision/optimCam.cc
new file mode 100644
index 0000000..8bf2b9c
--- /dev/null
+++ b/2004/i/nono/src/ovision/optimCam.cc
@@ -0,0 +1,105 @@
+
+#include "video4linux/video4linux.h"
+#include "map.h"
+#include "oconfig.h"
+#include "group.h"
+#include "space.h"
+
+#include "segmTable.h"
+#include "segmLearn.h"
+
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/timeb.h>
+static struct timeval _tstart, _tend;
+static struct timezone tz;
+
+void tstart(void)
+{
+ gettimeofday(&_tstart, &tz);
+}
+void tend(void)
+{
+ gettimeofday(&_tend,&tz);
+}
+
+double tval()
+{
+ double t1, t2;
+ t1 = (double)_tstart.tv_sec + (double)_tstart.tv_usec/(1000*1000);
+ t2 = (double)_tend.tv_sec + (double)_tend.tv_usec/(1000*1000);
+ return t2-t1;
+}
+
+
+
+int
+main()
+{
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Initialisation des classes
+ tstart ();
+ OConfig oconfig("rc/vision.conf");
+
+ Img img;
+ Video4Linux::ColorSpace cs;
+ cs = Video4Linux::rgb;
+ Video4Linux v4l("/dev/video", cs, 60000);
+ v4l.calibrate ();
+
+ // Prendre une image pour que la taille de l'image soit configur
+ img.load (v4l);
+
+ Space space(img.width, img.height);
+ space.AddSetupPoint (356, 23, 300, 300);
+ space.AddSetupPoint (283, 171, 600, 600);
+ space.AddSetupPoint (253, 234, 1000, 900);
+ space.Setup (0.00603759, 0.593767, 291.474);
+
+ SegmTable segmNN(&img);
+ segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
+ segmNN.DoColorTable ();
+
+ Group group(&img, &segmNN);
+
+ Map map(&space);
+ tend ();
+ std::cout << "Initialisation:\t" << tval () << std::endl;
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Prends une image a partir de la camera et l'analyse
+
+ tstart ();
+ img.load(v4l);
+ tend ();
+ std::cout << "Acquisition img:\t" << tval () << std::endl;
+
+ tstart ();
+ //segmNN.Segm();
+ tend ();
+ std::cout << "Segmentation:\t" << tval () << std::endl;
+
+ tstart ();
+ group.JumpPoints(oconfig.groupColor);
+ tend ();
+ std::cout << "Find Group:\t" << tval () << std::endl;
+
+ group.ShowZones ();
+
+ tstart ();
+ if (group.zoneListBall)
+ {
+ int x,y;
+ x = group.zoneListBall->centerx;
+ y = img.height - group.zoneListBall->centery;
+ space.GetLoc(x, y, x, y);
+ tend ();
+ std::cout << "Map:\t\t" << tval () << std::endl;
+
+ }
+ /////////////////////////////////////////////////////////////////////////////////////////
+}
+
diff --git a/2004/i/nono/src/ovision/ovision.cc b/2004/i/nono/src/ovision/ovision.cc
new file mode 100644
index 0000000..db97857
--- /dev/null
+++ b/2004/i/nono/src/ovision/ovision.cc
@@ -0,0 +1,104 @@
+#include "ovision.h"
+#include <iostream>
+
+/// Constructeur
+OVision::OVision ()
+{
+ // Initialisation des classes
+ oconfig = new OConfig;
+ img = new Img;
+
+ // Initialisation camra
+ Video4Linux::ColorSpace cs;
+ cs = Video4Linux::rgb;
+ v4l = new Video4Linux ("/dev/video", cs, 60000);
+ v4l->calibrate ();
+
+ // Prendre une image pour que la taille de l'image soit configur
+ img->load (*v4l);
+
+ segm = new SegmTable (img);
+ group = new Group (img, segm);
+ map = new Map (space);
+
+ // Construction du rseau de neurones
+ segm->BuildNN (oconfig->nn_NbCouleurs, LOAD_FROM_FILE);
+
+ // Ouverture ou cration de la table de couleur
+ if (!segm->LoadColorTableFile ())
+ segm->DoColorTable ();
+
+ step = 0;
+}
+
+/// Destructeur
+OVision::~OVision ()
+{
+ delete oconfig;
+ delete img;
+ delete segm;
+ delete group;
+ delete map;
+}
+
+/// Prends une image avec la camra
+void
+OVision::TakeShoot ()
+{
+ // Prends une image
+ img->load(*v4l);
+}
+
+
+/// Analyse une image
+void
+OVision::Update ()
+{
+ // Compteur du nombre d'image trait
+ step++;
+
+ // Cherche les balles
+ group->JumpPoints(oconfig->groupColor);
+
+ // Parcours la liste de balles trouves
+ if (group->zoneListBall)
+ {
+ int x,y;
+ x = group->zoneListBall->centerx;
+ y = img->height - group->zoneListBall->centery;
+ space->GetLoc(x, y, x, y);
+ }
+
+ // Mets jour la map
+ map->AddBallsToMap (group);
+
+ double balX, balY;
+ if (map->GetCurBallPos (balX, balY))
+ std::cout << "Balle Courante: " << balX << " " << balY << std::endl;
+
+
+ if (map->IsTree ())
+ {
+ double treeX, treeY;
+ map->GetTreePos (treeX, treeY);
+ std::cout << "Tree: " << treeX << " " << treeY << std::endl;
+ }
+}
+
+/// Affiche d'info sur l'update
+void
+OVision::ShowInfo ()
+{
+ std::cout << "-----------------------------------------------------------------" << std::endl;
+ std::cout << "image n" << step << std::endl;
+
+ // Info sur les zones trouves
+ group->ShowZones();
+ std::cout << "-------------\n" << std::endl;
+
+
+ std::cout << "-------------\n" << std::endl;
+ std::cout << "Map:\n" << std::endl;
+ // Info sur les balles trouves
+ map->ShowBalls ();
+}
diff --git a/2004/i/nono/src/ovision/ovision.h b/2004/i/nono/src/ovision/ovision.h
new file mode 100644
index 0000000..75c2821
--- /dev/null
+++ b/2004/i/nono/src/ovision/ovision.h
@@ -0,0 +1,66 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef ovision_h
+#define ovision_h
+
+#include "oconfig.h"
+#include "segmTable.h"
+#include "group.h"
+#include "space.h"
+#include "map.h"
+
+#include "video4linux/video4linux.h"
+
+
+class OVision
+{
+ OConfig *oconfig;
+ Img *img;
+ SegmTable *segm;
+ Group *group;
+ Space *space;
+ Map *map;
+
+ Video4Linux *v4l;
+
+ // Nombre d'image dj trait
+ int step;
+
+ public:
+ /// Constructeur
+ OVision ();
+
+ /// Destructeur
+ ~OVision (void);
+
+ /// Prends une image avec la camra
+ void TakeShoot ();
+
+ /// Analyse une image
+ void Update ();
+
+ /// Affiche d'info sur l'update
+ void ShowInfo ();
+};
+
+#endif // ovision_h
diff --git a/2004/i/nono/src/ovision/segmLearn.cc b/2004/i/nono/src/ovision/segmLearn.cc
new file mode 100644
index 0000000..4a2217e
--- /dev/null
+++ b/2004/i/nono/src/ovision/segmLearn.cc
@@ -0,0 +1,195 @@
+// segmLearn.cc - Classe Segmentation
+// nono - Programme du robot Efrei Robotique I1-I2 2004
+// Copyright (C) 2004 Olivier Gaillard
+
+/// @file segmLearn.cc Apprentissage du rseau de neurones
+
+#include "segmLearn.h"
+#include <iostream>
+
+/// Constructor SegmLearn
+
+/// @param img classe img permettant d'acceder au donnees de l'image a traiter
+/// @param oconfig classe oconfig permettant d'acceder aux variables de oconfiguration
+SegmLearn::SegmLearn (Img *img) : SegmNN (img)
+{
+ freq = NULL;
+}
+
+/// Destructor SegmLearn
+SegmLearn::~SegmLearn ()
+{
+ delete [] freq;
+}
+
+
+/// Genere des poids pour un noeud
+/// @param numNode numro du noeud changer
+void
+SegmLearn::WeightsGen (int numNode)
+{
+ // Parcours des 3 composantes
+ for(int i=0; i<3; i++)
+ {
+ // Attribution alatoire des poids
+ node[numNode*3+i] = (unsigned char) (255.0*rand()/(RAND_MAX+1.0));
+ if (node[numNode*3+i] < 30) node[numNode*3+i] = 30;
+ else if (node[numNode*3+i] > 220) node[numNode*3+i] = 220;
+ }
+}
+
+
+/// Construit un reseau de neurones
+
+/// @param nbOutput nombre de noeuds de la couche de sortie du NN
+/// @param loadFromFile (GENERATE ou LOADFROMFILE) indique si les poids sont charges d'un fichier ou generes aleatoirement
+void
+SegmLearn::BuildNN(int nbOutput, int loadFromFile)
+{
+ SegmNN::nbOutput = nbOutput;
+
+ // Permet de charger les poids du NN depuis un fichier ou en les initialisant aleatoirement
+ if (loadFromFile)
+ {
+ // Verifie si le nombre de poids donne dans le fichier est suffisant
+ if (oconfig->nbNodeMax < nbOutput)
+ {
+ std::cerr << "SegmNN::BuildNN : Nombre de nodes insuffisants dans le fichier poids" << std::endl;
+ }
+ else
+ {
+ // Charge les poids du NN et l'index des couleurs
+ delete [] node;
+ node = new unsigned char[nbOutput*3];
+ for (int i = 0; i<nbOutput*3; i++)
+ node[i] = oconfig->node[i];
+ delete [] index;
+ index = new int[nbOutput];
+ for (int i = 0; i<nbOutput; i++)
+ index[i] = oconfig->index[i];
+ freq = new int[nbOutput];
+ }
+ return;
+ }
+
+ //initialition de random
+ srand((unsigned)time(0));
+
+ // Initialisation des noeuds du NN
+ delete [] node;
+ node = new unsigned char[nbOutput*3];
+ delete [] index;
+ index = new int[nbOutput];
+ delete [] freq;
+ freq = new int[nbOutput];
+
+ for(int i=0; i<nbOutput; i++)
+ {
+ // Remise zro de l'index et de freq
+ index[i] = i;
+ freq[i] = 0;
+
+ // Regeneration de nouveaux poids
+ WeightsGen (i);
+ }
+}
+
+
+/// Entraine un reseau de neurones
+void
+SegmLearn::TrainNN ()
+{
+ unsigned long pixelNum;
+ unsigned char *tabData;
+ int numOutputMax;
+
+ tabData = img->tabData;
+
+ for(long i=0; i<oconfig->nn_nil; i++)
+ {
+ // On choisit un pixel au hasard
+ pixelNum = (unsigned long)(img->nbPixels*(rand()/(RAND_MAX+1.0)));
+
+ numOutputMax = WinnerOutput (tabData + pixelNum*3);
+
+ // Mis a jour des poids
+ for(int k=0; k<3; k++)
+ {
+ node[numOutputMax*3+k] =
+ (unsigned char)((node[numOutputMax*3+k] + oconfig->nn_sl*tabData[pixelNum*3+k])/(1+oconfig->nn_sl));
+
+ // Recompense pour la sortie qui travaille
+ freq[numOutputMax]++;
+ }
+
+ // Verification des noeuds inutiles partir de 300 iterations
+ if ((i%300) == 299)
+ {
+ for (int k=0; k < nbOutput; k++)
+ {
+ if (freq[k] < oconfig->nn_lazy_threshold)
+ // Regeneration de nouveaux poids
+ WeightsGen (k);
+
+ // On remet le compteur a zero
+ freq[k] = 0;
+ }
+ }
+ }
+}
+
+
+/// Renvoie le code de la couleur segmentee
+/// @param *x pointeur vers un tableau contenant une valeur RGB
+/// @param testOutputMax choix de l'utilisation d'un sueil maxi pour la sortie pour viter qu'une couleur trop diffrente soit attribuer une autre couleur
+unsigned char
+SegmLearn::GiveColor (unsigned char *x, bool testOutputMax, bool indexProcess)
+{
+ if (indexProcess)
+ return index [FindColorNN (x, testOutputMax)];
+ else
+ return FindColorNN (x, testOutputMax);
+}
+
+
+/// Entraine plusieurs reseaux de neurones avec des parametres differents et crees les images associees
+void
+SegmLearn::TestNN ()
+{
+
+/* double sl[] = {0.01, 0.1, 1};
+ unsigned long nil[] = {100, 1000, 10000, 100000, 1000000};
+ int nc[] = {3,4,5,6,7,8};
+
+ char filename[30];
+
+ // Parcours de toutes les valeurs de nombres de couleurs
+ for (int i_nc = 0; i_nc<6; i_nc++) {
+ BuildNN(nc[i_nc], 0);
+
+ // Parcours de toutes les valeurs de nombres d'itrations
+ for (int i_nil = 0; i_nil<5; i_nil++) {
+
+ oconfig->nn_nil = nil[i_nil];
+
+ // Parcours de toutes les valeurs de Step Learning
+ for (int i_sl = 0; i_sl<4; i_sl++) {
+ oconfig->nn_sl = sl[i_sl];
+
+ // Apprentissage du NN
+ TrainNN();
+
+ // Segmentation
+ Segm();
+
+ // Ecriture des rsultats dans une image
+ sprintf(filename, "NN/%i-%f-%luNN.jpg", nc[i_nc], sl[i_sl], nil[i_nil]);
+ img->WriteSegm(filename, tabSegm);
+ sprintf(filename, "NN/%i-%f-%luNN", nc[i_nc], sl[i_sl], nil[i_nil]);
+ oconfig->node = node;
+ oconfig->CreateNNFile(filename, oconfig->colorMode, nbOutput);
+ }
+ }
+ }
+ */
+}
diff --git a/2004/i/nono/src/ovision/segmLearn.h b/2004/i/nono/src/ovision/segmLearn.h
new file mode 100644
index 0000000..69b3493
--- /dev/null
+++ b/2004/i/nono/src/ovision/segmLearn.h
@@ -0,0 +1,56 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef segmLearn_h
+#define segmLearn_h
+
+#include "segmNN.h"
+
+class SegmLearn : public SegmNN
+{
+ // memorise la popularit des noeuds pour enlever les sorties inefficaces
+ int *freq;
+
+ /// Genere des poids pour un noeud
+ void WeightsGen (int numNode);
+
+ public:
+ /// Constructeur
+ SegmLearn (Img *img);
+
+ /// Destructeur
+ virtual ~SegmLearn (void);
+
+ /// Apprentissage du NN
+ void TrainNN ();
+
+ /// Entraine plusieurs reseaux de neurones avec des parametres differents
+ void TestNN ();
+
+ /// Cree le NN
+ void BuildNN (int nbOutput, int loadFromFile);
+
+ /// Renvoie le code la couleur segmentee
+ virtual unsigned char GiveColor (unsigned char *x, bool testUndefined = false, bool indexProcess = false);
+};
+
+#endif // segmLearn_h
diff --git a/2004/i/nono/src/ovision/segmNN.cc b/2004/i/nono/src/ovision/segmNN.cc
index 9e4db20..5667e75 100644
--- a/2004/i/nono/src/ovision/segmNN.cc
+++ b/2004/i/nono/src/ovision/segmNN.cc
@@ -2,45 +2,42 @@
// nono - Programme du robot Efrei Robotique I1-I2 2004
// Copyright (C) 2004 Olivier Gaillard
-/// @file segmNN.cc Segmente l'image et cree un tableau contenant des valeurs segmentees, creation du reseau de neurones et apprentissage
-
+/// @file segmNN.cc Segmente l'image et cree un tableau contenant des valeurs segmentees, creation du reseau de neurones
#include "segmNN.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
-#include <math.h>
-#include <time.h>
using namespace std;
+
/// Constructor SegmNN
/// @param img classe img permettant d'acceder au donnees de l'image a traiter
/// @param oconfig classe oconfig permettant d'acceder aux variables de oconfiguration
-SegmNN::SegmNN(Img *img) {
- // Sauvegarde les pointeurs
- SegmNN::img = img;
+SegmNN::SegmNN (Img *img)
+{
+ this->img = img;
oconfig = OConfig::GetInstance ();
nbOutput = oconfig->nn_NbCouleurs;
node = NULL;
index = NULL;
- freq = NULL;
tabSegm = NULL;
}
+
/// Destructor SegmNN
-SegmNN::~SegmNN()
+SegmNN::~SegmNN ()
{
delete [] node;
delete [] index;
- delete [] freq;
delete [] tabSegm;
}
/// Affiche les poids du reseau de neurones (neural network : NN)
void
-SegmNN::ShowNodes()
+SegmNN::ShowNodes ()
{
// Affichage des poids du NN
cout << "Poids:";
@@ -50,14 +47,50 @@ SegmNN::ShowNodes()
}
+/// Renvoie la sortie ayant le plus haut niveau
+/// @param x couleur segmente
+int
+SegmNN::WinnerOutput (unsigned char *x)
+{
+ int output[nbOutput];
+ int numOutputMax = 0;
+ int tmp;
+
+ // Calcul des valeurs de sorties pour ce pixel
+ for(int j=0; j<nbOutput; j++)
+ {
+ output[j] = 0;
+
+ for(int k=0; k<3; k++)
+ {
+ // XXX Ne pas oublier de mettre abs si on ne calcule pas les carrs
+ tmp = node[j*3+k]-x[k];
+
+ // Si on est en yuv ou hsi et que c'est la composante de luminosite on ajoute un poids sur son influence
+ if ((img->yuv && k==0) || (img->hsi && k==2))
+ output[j] += (int) oconfig->nn_influ_lum * tmp * tmp;
+ // Sinon calcul normal
+ else
+ output[j] += tmp * tmp;
+ }
+
+ // On cherche la sortie ayant le plus haut niveau
+ if (output[j] < output[numOutputMax])
+ numOutputMax = j;
+ }
+
+ return numOutputMax;
+}
+
+
/// Construit un reseau de neurones
/// @param nbOutput nombre de noeuds de la couche de sortie du NN
/// @param loadFromFile (GENERATE ou LOADFROMFILE) indique si les poids sont charges d'un fichier ou generes aleatoirement
void
-SegmNN::BuildNN(int nbOutput, int loadFromFile)
+SegmNN::BuildNN (int nbOutput, int loadFromFile)
{
- SegmNN::nbOutput = nbOutput;
+ this->nbOutput = nbOutput;
// Permet de charger les poids du NN depuis un fichier ou en les initialisant aleatoirement
if (loadFromFile)
@@ -69,178 +102,74 @@ SegmNN::BuildNN(int nbOutput, int loadFromFile)
}
else
{
- // Charge les poids du NN et l'index des couleurs
+ // Charge les poids du NN
delete [] node;
node = new unsigned char[nbOutput*3];
for (int i = 0; i<nbOutput*3; i++)
node[i] = oconfig->node[i];
+
+ // Initialisation de l'index de couleur
delete [] index;
index = new int[nbOutput];
for (int i = 0; i<nbOutput; i++)
index[i] = oconfig->index[i];
- freq = new unsigned int[nbOutput];
- }
- return;
- }
-
- //initialition de random
- srand((unsigned)time(0));
-
- // Initialisation des noeuds du NN
- delete [] node;
- node = new unsigned char[nbOutput*3];
- delete [] index;
- index = new int[nbOutput];
- delete [] freq;
- freq = new unsigned int[nbOutput];
-
-
- for(int i=0; i<nbOutput; i++)
- {
- index[i] = i;
- freq[i] = 0;
-
- for(int j=0; j<3; j++)
- {
- // Attribution alatoire des poids
- node[i*3+j] = (unsigned char) (255.0*rand()/(RAND_MAX+1.0));
- if (node[i*3+j] < 30) node[i*3+j] = 30;
- else if (node[i*3+j] > 220) node[i*3+j] = 220;
}
}
-}
-
-
-/// Entraine un reseau de neurones
-void
-SegmNN::TrainNN()
-{
- unsigned long pixelNum;
- unsigned char *tabData;
- int output[nbOutput];
- int numOutputMax=0;
-
- tabData = img->tabData;
- oconfig->colorMode = img->hsi*2 + img->yuv;
- oconfig->groupColor = 0;
-
- for(long i=0; i<oconfig->nn_nil; i++)
+ // Si on ne souhaite pas loader les poids a partir d'un fichier
+ else
{
- // On choisit un pixel au hasard
- pixelNum = (unsigned long)(img->nbPixels*(rand()/(RAND_MAX+1.0)));
-
- // Calcul des valeurs de sorties pour ce pixel
- for(int j=0; j<nbOutput; j++)
- {
- output[j] = 0;
-
- for(int k=0; k<3; k++)
- {
- if ((img->yuv && k==0) || (img->hsi && k==2))
- output[j] += abs((int)(oconfig->nn_influ_lum*node[j*3+k]-tabData[pixelNum*3+k])*abs(node[j*3+k]-tabData[pixelNum*3+k]));
- else
- output[j] += abs(node[j*3+k]-tabData[pixelNum*3+k])*abs(node[j*3+k]-tabData[pixelNum*3+k]);
- }
-
- // On cherche la sortie ayant le plus haut niveau
- if (output[j] < output[numOutputMax])
- numOutputMax = j;
- }
-
- // Mis a jour des poids
- for(int k=0; k<3; k++)
- {
- node[numOutputMax*3+k] =
- (unsigned char)((node[numOutputMax*3+k] + oconfig->nn_sl*tabData[pixelNum*3+k])/(1+oconfig->nn_sl));
-
- // Recompense pour la sortie qui travaille
- freq[numOutputMax]++;
-
- // Verification des noeuds inutiles
- if ((i%300) == 299)
- {
- for (int k=0; k < nbOutput; k++)
- {
- if (freq[numOutputMax] < oconfig->nn_lazy_threshold)
- {
- // Regeneration de nouveaux poids
- for(int m=0; m<3; m++)
- {
- // Attribution alatoire des poids
- node[numOutputMax*3+m] = (unsigned char) (255.0*rand()/(RAND_MAX+1.0));
- if (node[numOutputMax*3+m] < 30) node[numOutputMax*3+m] = 30;
- else if (node[numOutputMax*3+m] > 220) node[numOutputMax*3+m] = 220;
- }
- }
-
- // On remet le compteur a zero
- freq[k] = 0;
- }
-
- }
-
- }
-
- }
+ cerr << "SegmNN::BuildNN : Utilis la classe SegmLearn pour l'apprentissage du NN\n";
+ }
}
/// Renvoie le code de la couleur segmentee
/// @param *x pointeur vers un tableau contenant une valeur RGB
+/// @param testOutputMax choix de l'utilisation d'un sueil maxi pour la sortie pour viter qu'une couleur trop diffrente soit attribuer une autre couleur
unsigned char
-SegmNN::FindColorNN(unsigned char *x, bool testUndefined)
+SegmNN::FindColorNN (unsigned char *x, bool testOutputMax)
{
int numOutputMax=0;
int output[nbOutput];
- int j,tmp;
+ int tmp;
- // Calcul des valeurs de sorties pour ce pixel
- // si on est en yuv
- if (img->yuv)
+ // Calcul des valeurs de sorties du NN pour ce pixel
+ for(int j=0; j<nbOutput; j++)
{
- for(j=0; j<nbOutput; j++)
+ output[j] = 0;
+
+ // Parcours des 3 composantes
+ for(int k=0; k<3; k++)
{
// XXX Ne pas oublier de mettre abs si on ne calcule pas les carrs
- tmp = node[j*3]-x[0];
- output[j] = abs((int)(oconfig->nn_influ_lum*tmp*tmp));
- tmp = node[j*3+1]-x[1];
- output[j] += tmp*tmp;
- tmp = node[j*3+2]-x[2];
- output[j] += tmp*tmp;
-
- // On selectionne la sortie ayant la plus grande valeur comme couleur
- if (output[numOutputMax] > output[j])
- numOutputMax = j;
+ tmp = node[j*3+k]-x[k];
+
+ // Si on est en yuv ou hsi et que c'est la composante de luminosite, on ajoute un poids pour affecter son influence
+ if ((img->yuv && k==0) || (img->hsi && k==2))
+ output[j] += (int) oconfig->nn_influ_lum * tmp * tmp;
+ // Sinon calcul normal
+ else
+ output[j] += tmp * tmp;
}
- }
- // si on est en rgb
- else {
- for(j=0; j<nbOutput; j++)
- {
- output[j] = 0;
-
- for(int k=0; k<3; k++)
- output[j] += abs(node[j*3+k]-x[k])*abs(node[j*3+k]-x[k]);
- // On selectionne la sortie ayant la plus grande valeur comme couleur
- if (output[numOutputMax] > output[j])
- numOutputMax = j;
- }
- }
+ // On selectionne la sortie ayant la plus grande valeur comme couleur
+ if (output[numOutputMax] > output[j])
+ numOutputMax = j;
+ }
- if ((testUndefined) && (output[numOutputMax] > oconfig->nn_threshold_output))
+ // Test si la valeur de sortie est assez proche d'une couleur du rezo
+ if ((testOutputMax) && (output[numOutputMax] > oconfig->nn_threshold_output))
numOutputMax = UNDEFINED;
-
-// if (testUndefined) cout << output[numOutputMax] << endl;
+
return numOutputMax;
}
/// Segmente l'image avec le reseau de neurones
-void SegmNN::Segm()
+void
+SegmNN::Segm ()
{
-
unsigned char* tabData = img->tabData;
if (tabSegm) delete [] tabSegm;
@@ -250,14 +179,15 @@ void SegmNN::Segm()
// On recupere l'index et non le numero de sortie du NN
for (unsigned long i=0; i<img->nbPixels; i++)
{
- tabSegm[i] = index[FindColorNN(tabData+i*3)];
+ tabSegm[i] = index[FindColorNN (tabData+i*3)];
}
}
-/// Segmente l'image pour une seule couleur uniquement et permet donc d'isoler un ficher
+
+/// Segmente l'image pour une seule couleur uniquement et permet donc d'isoler une couleur
/// @param numColor numero de la couleur a isoler
void
-SegmNN::Segm(int numColor)
+SegmNN::Segm (int numColor)
{
unsigned char* tabData = img->tabData;
@@ -267,60 +197,9 @@ SegmNN::Segm(int numColor)
// Parcours de l'image pour la segmentation
for (unsigned long i=0; i<img->nbPixels; i++)
{
- if (FindColorNN(tabData+i*3) == numColor) tabSegm[i] = 1;
+ if (FindColorNN (tabData+i*3) == numColor) tabSegm[i] = 1;
else tabSegm[i] = 0;
}
}
-
-/*void
-SegmNN::InitCache ()
-{
-
-
-}
-*/
-
-
-/// Entraine plusieurs reseaux de neurones avec des parametres differents et crees les images associees
-void
-SegmNN::TestNN()
-{
-
-/* double sl[] = {0.01, 0.1, 1};
- unsigned long nil[] = {100, 1000, 10000, 100000, 1000000};
- int nc[] = {3,4,5,6,7,8};
-
- char filename[30];
-
- // Parcours de toutes les valeurs de nombres de couleurs
- for (int i_nc = 0; i_nc<6; i_nc++) {
- BuildNN(nc[i_nc], 0);
-
- // Parcours de toutes les valeurs de nombres d'itrations
- for (int i_nil = 0; i_nil<5; i_nil++) {
-
- oconfig->nn_nil = nil[i_nil];
-
- // Parcours de toutes les valeurs de Step Learning
- for (int i_sl = 0; i_sl<4; i_sl++) {
- oconfig->nn_sl = sl[i_sl];
-
- // Apprentissage du NN
- TrainNN();
-
- // Segmentation
- Segm();
-
- // Ecriture des rsultats dans une image
- sprintf(filename, "NN/%i-%f-%luNN.jpg", nc[i_nc], sl[i_sl], nil[i_nil]);
- img->WriteSegm(filename, tabSegm);
- sprintf(filename, "NN/%i-%f-%luNN", nc[i_nc], sl[i_sl], nil[i_nil]);
- oconfig->node = node;
- oconfig->CreateNNFile(filename, oconfig->colorMode, nbOutput);
- }
- }
- }
- */
-}
diff --git a/2004/i/nono/src/ovision/segmNN.h b/2004/i/nono/src/ovision/segmNN.h
index 3a2bfa2..102eaf2 100644
--- a/2004/i/nono/src/ovision/segmNN.h
+++ b/2004/i/nono/src/ovision/segmNN.h
@@ -1,8 +1,27 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
#ifndef segmNN_h
#define segmNN_h
-// segmNN.h - classe Segmentation
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include "img.h"
#include "oconfig.h"
@@ -20,57 +39,54 @@
/// Segmente l'image et cree un tableau contenant des valeurs segmentees, creation du reseau de neurones et apprentissage
class SegmNN
{
- // Classe image
- Img *img;
-
- // Classe config
- OConfig *oconfig;
-
public:
- // tableau avec couleurs segmentees
- unsigned char *tabSegm;
-
- // tableau de poids du NN
- unsigned char *node;
+ /// Constructeur
+ SegmNN (Img *img);
+ /// Destructeur
+ virtual ~SegmNN (void);
+
+ // tableau avec couleurs segmentes
+ unsigned char *tabSegm;
+
// index des couleurs pour melanger (merge) les couleurs
int *index;
- // memorise la popularite des noeuds pour enlever les sorties inefficaces
- unsigned int *freq;
-
- // nb de couleurs a differencier
- int nbOutput;
-
- /// Constructeur
- SegmNN (Img *img);
+ // tableau de poids du NN
+ unsigned char *node;
- /// Destructeur
- ~SegmNN (void);
/// Affiche la valeur des poids du NN
void ShowNodes();
/// Cree le NN
- void BuildNN(int nbOutput, int loadFromFile);
-
- /// Apprentissage du NN
- void TrainNN();
+ void BuildNN (int nbOutput, int loadFromFile = true);
/// Segmentation de l'image
- void Segm();
+ void Segm ();
/// Segmentation de l'image permettant d'isoler une couleur
- void Segm(int numColor);
+ void Segm (int numColor);
- /// Entraine plusieurs reseaux de neurones avec des parametres differents
- void TestNN();
-
/// Renvoie le code la couleur segmentee
- unsigned char FindColorNN(unsigned char *x, bool testUndefined = 0);
-
+ unsigned char FindColorNN(unsigned char *x, bool testOutputMax = false);
+
+ /// Donne la couleur partir du tableau
+ virtual unsigned char GiveColor (unsigned char *x, bool testOutputMax = false, bool indexProcess = false) = 0;
+
protected:
-
+ // Classe image
+ Img *img;
+
+ // Classe config
+ OConfig *oconfig;
+
+ // nb de couleurs a differencier
+ int nbOutput;
+
+ /// Renvoie la sortie ayant le plus haut niveau
+ int WinnerOutput (unsigned char *x);
+
};
#endif // segmNN_h
diff --git a/2004/i/nono/src/ovision/segmTable.cc b/2004/i/nono/src/ovision/segmTable.cc
new file mode 100644
index 0000000..e4b4b9e
--- /dev/null
+++ b/2004/i/nono/src/ovision/segmTable.cc
@@ -0,0 +1,109 @@
+// segmTable.cc - Classe Segmentation
+// nono - Programme du robot Efrei Robotique I1-I2 2004
+// Copyright (C) 2004 Olivier Gaillard
+
+/// @file segmTable.cc Segmente l'image et cree un tableau contenant des valeurs segmentees, creation du reseau de neurones
+#include "segmTable.h"
+#include <fstream>
+#include <iostream>
+
+static const int COLOR_TAB_SIZE = 256;
+static const int COLOR_TAB_SIZE_2 = COLOR_TAB_SIZE*COLOR_TAB_SIZE;
+static const int COLOR_TAB_SIZE_TOTAL = COLOR_TAB_SIZE_2*COLOR_TAB_SIZE;
+
+/// Constructor SegmTable
+
+/// @param img classe img permettant d'acceder au donnees de l'image a traiter
+/// @param oconfig classe oconfig permettant d'acceder aux variables de oconfiguration
+SegmTable::SegmTable (Img *img, bool loadFromFile) : SegmNN (img)
+{
+ if (loadFromFile)
+ LoadColorTableFile ();
+ else
+ colorTable = NULL;
+}
+
+
+/// Destructor SegmTable
+SegmTable::~SegmTable ()
+{
+ delete [] colorTable;
+}
+
+
+/// Cree un tableau des couleurs segmentes pour ne plus faire de calcul
+/// et augmenter la rapidit
+/// @param testOutputMax choix de l'utilisation d'un sueil maxi pour la sortie pour viter qu'une couleur trop diffrente soit attribuer une autre couleur
+void
+SegmTable::DoColorTable (bool testOutputMax)
+{
+ unsigned char x[3];
+
+ delete [] colorTable;
+ colorTable = new unsigned char[COLOR_TAB_SIZE_TOTAL];
+
+ // Parcours de toutes les valeurs possibles
+ for (int i=0; i<COLOR_TAB_SIZE; i++)
+ for (int j=0; j<COLOR_TAB_SIZE; j++)
+ for (int k=0; k<COLOR_TAB_SIZE; k++)
+ {
+ // Remplissage de la table
+ x[0]=i; x[1]=j; x[2]=k;
+ colorTable[ i*COLOR_TAB_SIZE_2 + j*COLOR_TAB_SIZE +k ] = FindColorNN (x , testOutputMax);
+ }
+}
+
+
+/// Donne la couleur partir du tableau
+/// @param *x pointeur vers un tableau contenant une valeur RGB
+unsigned char
+SegmTable::GiveColor (unsigned char *x, bool testOutputMax, bool indexProcess)
+{
+// if (indexProcess)
+ return index [colorTable[ x[0]*COLOR_TAB_SIZE_2 + x[1]*COLOR_TAB_SIZE + x[2] ]];
+// else
+// return colorTable[ x[0]*COLOR_TAB_SIZE_2 + x[1]*COLOR_TAB_SIZE + x[2] ];
+}
+
+
+/// Cree un fichier de ColorTable
+void
+SegmTable::CreateColorTableFile (char *filename)
+{
+ std::ofstream file;
+
+ file.open (filename);
+
+ if (file)
+ file.write ((char*)colorTable, COLOR_TAB_SIZE_TOTAL);
+ else
+ std::cerr << "SegmTable::CreateColorTableFile Erreur d'ouverture de fichier" << std::endl;
+
+ file.close ();
+}
+
+
+
+
+/// Charge un fichier de ColorTable
+bool
+SegmTable::LoadColorTableFile (char *filename)
+{
+ delete [] colorTable;
+ colorTable = new unsigned char[COLOR_TAB_SIZE_TOTAL];
+
+ std::ifstream file;
+ file.open (filename);
+
+ if (file)
+ file.read ((char*)colorTable, COLOR_TAB_SIZE_TOTAL);
+ else
+ {
+ std::cerr << "SegmTable::LoadColorTableFile Erreur d'ouverture de fichier" << std::endl;
+ return 0;
+ }
+
+ file.close ();
+ return 1;
+}
+
diff --git a/2004/i/nono/src/ovision/segmTable.h b/2004/i/nono/src/ovision/segmTable.h
new file mode 100644
index 0000000..af03938
--- /dev/null
+++ b/2004/i/nono/src/ovision/segmTable.h
@@ -0,0 +1,55 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#ifndef segmTable_h
+#define segmTable_h
+
+#include "segmNN.h"
+
+
+/// Segmente l'image et cree un tableau contenant des valeurs segmentees, creation du reseau de neurones et apprentissage
+class SegmTable : public SegmNN
+{
+ // Tableau de correspondance couleurs pixels -> couleur donne par le rezo
+ unsigned char *colorTable;
+
+ public:
+ /// Constructeur
+ SegmTable (Img *img, bool loadFromFile = false);
+
+ /// Destructeur
+ virtual ~SegmTable (void);
+
+ /// Donne la couleur partir du tableau
+ virtual unsigned char GiveColor (unsigned char *x, bool testOutputMax = false, bool indexProcess = false);
+
+ /// Cree un tableau des couleurs segmentes pour ne plus faire de calcul
+ void DoColorTable (bool testOutputMax = false);
+
+ /// Cree un fichier de ColorTable
+ void CreateColorTableFile (char *filename = "rc/colortable");
+
+ /// Charge un fichier de ColorTable
+ bool LoadColorTableFile (char *filename = "rc/colortable");
+};
+
+#endif // segmTable_h
diff --git a/2004/i/nono/src/ovision/space.cc b/2004/i/nono/src/ovision/space.cc
index 705dd0c..e523b50 100644
--- a/2004/i/nono/src/ovision/space.cc
+++ b/2004/i/nono/src/ovision/space.cc
@@ -33,7 +33,8 @@ Space::~Space ()
/// Cree un fichier gnuplot avec la courbe des points donns par la courbe x et y
-void Space::CreateGnuPlotFile (int y)
+void
+Space::CreateGnuPlotFile (int y)
{
FILE *file;
double locY, locX;
diff --git a/2004/i/nono/src/ovision/space.h b/2004/i/nono/src/ovision/space.h
index a30472d..6d44c41 100644
--- a/2004/i/nono/src/ovision/space.h
+++ b/2004/i/nono/src/ovision/space.h
@@ -1,20 +1,40 @@
+// nono2 - programme du robot 2005
+//
+// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
#ifndef space_h
#define space_h
-// space.h - Space class
-// nono - Programme du robot Efrei Robotique I1-I2 2004
-// Copyright (C) 2004 Olivier Gaillard
#include <vector>
#include <iostream>
+#include <stdio.h>
#include "group.h"
#include "oconfig.h"
-#define NB_NODES_X 5
-#define NB_NODES_Y 3
+//#define NB_NODES_X 5
+//#define NB_NODES_Y 3
-#define START_WEIGHT_MIN 0.1
-#define START_WEIGHT_MAX 0.9
+//#define START_WEIGHT_MIN 0.1
+//#define START_WEIGHT_MAX 0.9
struct SETUP_POINT
diff --git a/2004/i/nono/src/ovision/startui b/2004/i/nono/src/ovision/startui
new file mode 100755
index 0000000..d43791f
--- /dev/null
+++ b/2004/i/nono/src/ovision/startui
@@ -0,0 +1,4 @@
+rm -f adjust.log
+./adjust 1>adjust.log 2>adjust.log &
+sleep 1
+./ui
diff --git a/2004/i/nono/src/ovision/test.rgb b/2004/i/nono/src/ovision/test.rgb
new file mode 100644
index 0000000..c80dfae
--- /dev/null
+++ b/2004/i/nono/src/ovision/test.rgb
@@ -0,0 +1 @@
+|iqXoHmDi@f?g>i=e<b=`>^AeElF{Ga||iwXqHkDk@l?j>i=g<f=d>cAhEnFGa|k}YsGjEnDrBm@i?i?j=i<i?lApEIfmzZtGmGnHoElBiBhBh>l;p<s>wDKkfxXtKqGnDlCkBjAhAf>n;w={@FMt_wWtPrHp@oAnBmAk@i>n<s?yBIP}ꎣYvRtKsFsAsAqApAn@l>m;o@xEJPx뇕StMsGsDrBrAqApAnAk>o;sBzILP}~OrJrDsBrAqBqCq?n<k;q;xA|GLR}vLuGsBqAq@pCqFq>o7m9p<s@zEMUrzzrNyHtBp@p?pBqDr@p<o=n>nByFS`߇seven}QxItBq@p?oAoCoBoAnApArDzHZlꑴ}tkbY}zUlQ\^Qz~d{OxKuFrCp@nBmDmCmBnBrCvGzK_t돻v|mjeZv^KYKaTLrOKeKKiJFbJB]J怨h|[zNxLsKoFnAnCnEoDmDlDpEuIzN~e}udSdlN\tITbDNc@IT>K]=M^=Ld=Jd@EaCAYC~賾ԧ䝯䔫{bzWyMxIrElBm@nAoBqCmEjEoFuKyQ~eyݕⲾrj}^PwTEYI;]F<UC>^AC`?Hb=Kb;Oe=Lk?JdAEeB@YBy֣暽ܘ۔␤v]{TxLvFs@p?m?k?n@qCmFjFqGwM|Tevܑx\jqR\kHNVDHY@C[?D]>Fd>Fe>Gs;Hg9Jj=HcBF]BCXB@NB뵾۬秸ڣܤह⤺楻⠼ܛߒ犞qY|RxKuFtAt@n>iAmCqAn@kBrEzMTi}ޕꭿ{sfa~RPsKNjDMaAL]>K^<La;N_=J`?Gc<F]9E^:D\<CY;BQ9AP9勵좾⠼꠺ࡸॸ੷ޫ߭㦼៻ᑫᄛlUO|JxFuCr@n>jBnGs@r:r?sDuLUm΅ajT]}HQtDMk@Hj>G_<E_:H]9KZ<JZ@I]<HZ9HX8DX7@[4AS1CQ1ڤ᤼ݤޤ⥶ݦاޡ䜺⏩jSMG{EvDq@n<kApFuAw;z?uCpLVtؓjqSaKZrDSk?Kd;D_;BW;@\:D]8I[;JS=KY:KV7KX8DT:=T7AO3FM3{¾䨼姻ܧۤۢܢۢڝݙ卤ہiQJ~D{DvEq@p;n@pFrAs=t?uBvMX|csTgE\~BVl@Pb;J^7DY9CY;B^9E\8Gb9GY;Ga8F\5E_9BW>?U:AO6BH6z赾գܤդأӣ٤ߦܟәފ{ePK}G{FvDrBr@rBqDpAo?nAuC|NZ~ޭsVfM^rDWnAR_>N_:I\7E\9EX:EX8ET7FX8EQ:DY6B[3@Z6AW:BU7@S3?M3z޳ᱻۨڟ⠺ڢݣפקݪ墻ߚ㇢ubO{MzKyGvDtErFpDqBrBpBoCvD|P]̀p]oJ\wFVnCQk?Ma<Ja:I`8He9F[:EX8EW6EY8EU:FX5CW1@W4@T7AX4@P1>N1}յͼƷ۷Яխժשڨئפ٣עۣդ٧ڪ壾㛿慤n[GwGwGwEwCwDrEnCqBt@r>qBwG}WgڅcgT\wFRnBOf?Lc=I\;F^9H^7K`9HX:EV9DW8DW7FT6HU4DR2@T4@L5AQ2?J/>J/ҐǒƔԣͳԭ˨Σ̢̢Ɲʙ̜ΠҠԠפթե٢䣿ޤާhT@yAwCuCuCtCrDpCpCp?q;qB|J]qsVbLYrBPg>Oc;N`:K]:H_8Ga7Ge9EZ;CR;CU;CX7CU3DY3DV4D[4AU4?W1@M.AF.v{ęӧ۶ҼɱƿϴŨå¡ßśėÙɛȜŞ̞ϟԠԢ٧լ헺}bRC{CwDtCsBrArAr@o@m?o>rDJfgxM]yGVn@Of<Pb8Q_7M\7J]7Fd6C]7B\9BV:BU;BR8AO6@T7DR8HW8BW8=T5@P2DM2ÿލu]dlvʩɦǝԝɜʜʝןם᛾驺᷷ᮻݩכ撳w]QzFtFrFpCpAq?q>r>p>o@pBrFJos\oEYgBSg?Mb:Nb5Na5LZ5IV5FW6CQ6AT7@U9@T<AN:AT9BV:BW<CY<CV<CT9DR6ER6ûxqv~RtWus]wcu|iso}uơТۡΧ֨Ω֧ۦ쫾冕\}QuGnGmHmEnBpAq?r?q?qAqCrFJlmwWftAUk?Pf>Lc9Ld5L^6JU8IU8FT8CR6@P3>T8?X=@Y<B^;E]=A[?>[=CV;IV7HT3GS3ľ~mGJNzPz|RtTwVzz_hpy~æ峾ҦӚřȘĞɤԦѵ[~QxHrIrJsGsDsBrAq@o@mBoDrGKiݥ޴hsScz>Sm=Pd=Mc9Mc5Ma8IX;EU;DT;CS5AS0@U7B[>Ea>Ga>Ie@GaBDc>F];H]6FX1DT1sJOURyPvNqvMmsTnu[o~_qvctjrw}ѡٯתǦТɞĠѥν蔞YP{GvFxEzDxDvAs>q=m=j@nDrI|OjDžړآݢޢߢݡ٩ݱ߳޷޵ެ٫ްݰhoT`r?Rj>PZ<Ob9Ne5N^8HW;BX;BV<CW8BV3B[8F`=Kb>L`?Nd?La@Kb=Ia:H^7E]4BX4榙yMU]UNI~yEggIipNluNprNtoWxv`|zbelt}Ŝ֣槻ګ꯷߳쫽٤â͠ˤ̰XO}GzCx@wBuEt@q;n:n:n?nDnKSkƒ͓ңۣߤ޴ޱݮެ߲޸ޱ߳ިiUoAUq>Rf<Pf9Ma6K^8H\;Ea<E\>E`:E^7Eb:Gb=Jg?KgALm?Kd>Je;H_9G]8EZ7BX7|LPUQMHeDbiEepGioIopKuyRrhYosZnoZmw]npaohyorv|šΤߪިۥTME~By?tBsEs@o;k:n9r=nBkMXq̊Ҙʦڴ౿ٯ٭֬ڭٯݭ߯Բܮޫ߯ݵ۷߫ߵڳܪߣӘy`}HYwCUi>Qf<L];H^<H[=H]=H\>H`;Ha8Ia:I_=If=Jj=Ke>J^>I^;HZ8G[7EX6CX6KLNMMHCahAej@ijDx}HMS}QP}vOyuNukSvsXwqZ{\fp{Ψ֨ܩ爔lQJ~D{Aw?sBqFoAm<l:m9n<n@nO^xے瞾㪿ߥԨ׫ЫիԨեեԦާ֨֩ݫ⪿۴ݪ߰ݷܶ޶ڃkxlP]}HVo@Ol@Kd@Hd?J`?L_?J^?Ic<Hb:Gd;Jc=Mh;Lb:K_<J^?I];G[8E^6CZ5BY5مzSiwLlmEp}DC@xs>ah@efBjnC~EMUVWSP{TrnYjrVklSmyZpvati~}pty~ћҜ؋{dM~J{FyEvCsBoBl@l>m=l<k?nCrSd~ុܣڥʧѦʦΨΪѩרӦգΤʤѦͨԨިܫޮܰزݮثܭܯۯް֗ߊoQbGWl=Mi>Kd>Ib=Lb<P_>M[@J^=Ha;Fb<Ka>Q`<NX:L_<K_?J[;FW8C[7BY7BW7ź[qK|p<m`:rh9wa9ke:_b?adEdmCu{BMX[_XRVZRJzN|ySvrVwwZwz\z_}chs~zn\J|IxItHqHoCn>n?o@o@l@jCnFsXjԅԡȟ˝ğɢФ˧Ϩ̪ϨЦΤϢƢˣҦЩѧզլٳٱް۬ީڧبܪܮlwփsR`~FXn;Qi<Qd=R`;O`9La=I]BF[?F^<Fd>Ia@M_=L^:Kg<I_?G[<E[9C_9AZ9@U9j^QjJe^C`i=^a8]_;^[>_a=hg<q|DLOSQOTXTQV[y\os]fwYhiUjpZpm_v{cxyg{lqrskdU~G{HuJpHmGkBn>q?q@r?m?iCnGtXjŜŞǡȣƥǦĨʨΨөӪͨͦΦѦϧШԩث٫ګҬة֧ۨުۭޱ߲۫מĞtP_EZf:Ud;Xe=[d<R_<I`<F_=C[<EX:G`=H`@Ia=J];K`<H\>E\=D^;D[8A[5>V5zpgZMA6_6wh7hX7ga7gb<slA~kDGJMRWWX^db`V}LmQ{qVrkStrPvtSwyWyx[_\ZOzDuHsLqInFlBn>p?pAp?n>lCqHwYjƝŠšãäæʨӫஹ۲ϭ̩ȦʣʦΪӧѤХӦЩӬܪۨ㩿ܪ׭ݰߩߣˇlxׅvOYhDSc:N^;Pe=Sm>Pe?Nd<K`9H\9I^9Jd=IdAIi>Kc<Lb=I\>F]>D[>BV8?T2<R2~tk_SPMqHgeD^pBc[@ihAfiAcoCfeFjtGszI}KMT\ZYURTVoyRppNqwOnkQk{SzUUV}MvEpFqGrEoDmAn>o?nAnAoApDuGzZmšǤԣۦ면ҧͦʤȢʣˤ͢ˠ̢ˤϨѬ֪٩ڨبةڪ޹ݯިⶠߔwPSxEMb;Ha:Ie9Kf:Od;Tb;Qb:Nb:Mc:Me<Kc>Jf=Lf=Na=KW>HZ<D^;@W6=P1;O1}qjcZRI@s>z<vg=nf?f`=ih;mo?u|C~|KTSRUYWVzQMLKKLOR|LxFtDsBrBqBq@p>p?oAmBmDnEsFy[pĠʜОСĢǣˢɢ̠ɟΞʝʠɣШҭիԪԨ֧צץ߳А|wQXzGSi=Nb9Od6Pe7Pc8Qa:O`<Ng;Lf;Jj;Ie;Hf<Jd>Le>JZ?HY;CY8?Z4>S0=M0}wqf\XUOumJbwE`Z@^e>_b=akAflFkyGuvHIKMOOOPQPOPR{LzFyCu@r@sAu@s@r?o?m@lAlDrGy[nzОžğƟśŚŜɢҥϥϦѥؤҤϤ޷ߺꇮ]^MYf=Tf:Ug7Vg9Rb:N]<NY?Nb=Kd<He:Ga9Gc<H`?J`>IT=HV:CU8?U5?U2@P2xsnaUMEj>{8e8uj8ke;og>td=ww=zvBx}HuLQTXURRR|L{G{Bx>v?tAsAsBs?r=r>o?lDoIs[|mvě˜ėǙ›Ĝȝǟʢɡ͡ˢңޮޫjkS^t=Qj;Qg9Qn;Nb=KY?K]BKg?Ib=Fg:Hb7Je<GbAEi>EY;FV9CU8AS6>Q5<P5}rkc[ļSNuJkGj_DjiAdc>_h?baAfnCmmFuILNPNM~K}I}E|A{AvBqAr@t=v:x=r@mDmGm|Qoz[rc|jpvԙřŝɢȟȜ̠ͤߵܮ߬ܫݭyy]dv@Oi=N]:Md;Ja=H^<H]<I`;GX:E^8I]6M_:FZ?@\;BT8DY7DY7DU4>O18O1xne\SKE?f<~m:oh;nf<md>|xAHOKH}IyKuGuDuCtCt@t>u;u8u=qBmDi~FfcHglJioPovVvz[ya}p“œãȝʘџΦѰ޺ݹݴުf~DTh?Pd;Lg<Ic=Gc:Ha7Je7H_8G\6G]5H`9F]>D\9DY5Da5CZ6BY1=T-9P-{riaYSMyIoaFerBpo?|HQNK|JtJmEnAoAsBw@w?w>u>s>p>m@fB_vD`dGagJc`MfjOhhRju]mthq~k|ntz}ĜǚʞʡϨ̯ծӭڮگׯqLZvBSd9Ld;Ia=Fa:I_7La7Jc7I^6F]5D]7F^9I\8FU7DY6BV4AV2=X/:R/}tjaXPG>HSQOyLvIsCs>s@tBvAv@wBuDs?q;p=x?Ax|DpkDjeEegDcdCbaIdePgaQloSqu[r{crkvszx~ę˜ƚƝȝ̝ՠդ٣ި{T[zFSi8Kd:Ia=Gc:H^7I^7H_7G^6F[5F[4G]4Id7FZ:CZ6AU3@S2>P2<O2xqdXVUO}JwHxFyDxBxAvAu@v?x?u@s>s<t@EEF|DrdBdb@_a>[aA\aD]]F\\H[aLY]PWcV\\\am`lycw~glvÕƛС̟ÜȝÞΠɢީݰzU\FSc8Ka8J\8I]7H[7G[7F_7E\8FX8HY6HX5I]6FX8CY6AT4?P3>I2>K2rdWN}EwDxCyDwFtCtAs?t>u=t<s=q>nDzKJIyDlp@__<\^9Za9Z\9ZY;YY>XY=X\=WTAXSFYLG^[Id_NilSn{du~u|{ϝ˨ۤסՠߠyVZ{GPg9Fb6F]3G[5FX7EU7FY8G]:DZ<B[9D[6Fb6E\6Da6AW6?S4>L2>J2nWN~FwDxBzDuFqBq?r?r?s>s=t<n;i=ip?i{>n~>su<fm:Z_8ZW6Z\8XW;WX9VY8V]7Wh7XY:TQ=QO?QKBRRDSOFT\P\VZeh`nofxlsx|՟lNXyBM\6BZ3C\0EW2DU3CT5FU6IY7C[9=Z6@X3DY3DU3EW4BU6?P4?M3?I3xWO~HxDxAyCuFqAq=q?vAz?y>y;q8jw5de3^Z3`a3cY4`\5\\4[[4Zb8Y[=YZ7YX2ZZ2Y^2YX3XW5XW8XS;XP:XR:YR<\Q?`JDe\JkcRpr[vt`fp{ڙx_GXl=M\4CO1CR.DR/DS0DR2EU5GX5C[6@\3AX0CW0DU0ES3@R6;L5;I5;E5yWNEzDyBxBuBr@q>q?yA?=~=t>k;_V9S_6SU3TW2YP2_W1\W0Z[3[Z6\Y4]V2^[2\_2Z]3]]4`Z6_[7_V7^Y7^W8]T:\S?]WE^]I``McfRl`Wu~^fՀw_HXq<NY0ES.DN-CO-DS-FU1EV4EY3D`3DZ2CU1BV1DX1FV3?T68N48G38G3{WMC{CxDvAt>s>s?t@yA><@DB@x:4`1uc/fY-^Y,WZ-X[/Y]1YX3Y^2Y]2Y`3Z[4\[4[Z4[T4\X4]]4^Z5^Z:^Y@_\?a`?cgCgdHljLQg}ywv_IYi:O\,EX,CQ-BL,BR+C[/BW4AW2CV0EY1DT3CW2CT2CY4?T6;R49K28J2tSK~C}DxFtCt@u>v=w?zA}?=ELV`VLHCm>a\8Ue5U^2VZ2UO2UX1VW1XZ0XW0Y[4X`8WY7ZX5]]6_^7a[8`X8`^:a]=ca?caAdmEzH\pxxurbS[APY0FU/CS.AP,@P+@S0?V5>R2BI0FO3EN5DO2BJ.AQ0?Q3>O0;J-8H-졼mOI~CyFwIuEvBw>v;u>yB}@?JUjre^XNE=6^3vb1f\0`\0YZ.XZ-XZ5X[=YY:Y[7Yb8\\:_]5`_1bg6ba;ca;ee;hq=@Qchmnpw~|wsof]qH\g4FR1DS/BT-BQ+BK0@R6?R3AN1CO4CL8CO1AM+?P->L0>P,;K)9F)ޢlPJ~DvEvFvDxAz>v;s=x@~BDTey|vmcZQMIxAie:[k7Y]4W\6YZ7\[6YZ4V]5ZV7^[6a`6d`7d_9db9hc9lm;~>N_chijpvx{~vnlkc[Jgj9G^4EV/DV,DX*DQ-BV1@R2@O3@M2AF1CM.@L,=K/=B2>I.<M*;I*lQKEyDxDwByA{>x<u=|>DJ_uwlfaSE@<`7o2nb2ea2\d3\^4\\7^];`]9`^8`h8ec8id:<úK[_dddinlkosvy{~{pffg`YL~?Zf7O[0CW,EU)FN+CQ-@R1AO5AR/@N*?S,=N.<O1:H49G/:I+;H+jRKE}C{AyBzB|@y>w>>CHc|qga\̽QGFqfDbu@^d<[b;\\:][:]W:]^:b]:gg:w:JZ^aa`dhggiknrvzsoke_TJn@XY7CY1FW,IU-EP.AR/BL0CP-?O+<N+;K+;K-7J/4H-8H+<F+|gSLE}B{?yADB@??BFg|l]ZWNEl?|9\;yc=j^<jb<je:y8IZ\_^]_bcdccgkpvtrtw{xpkf]UIs>R`6M_/I_/FX0DZ-CU+CU,BP-AL+?K)>J):J*6J*9G+=B+{eNHA}@{?z>>==>>DJizsja[VS}PwLriHmrBp<HUX\\\`dcccddehkgdefmt{voic]TKbAUh6Ia3HW1GY/EZ.DV.ES.FP,DK*BK*=M*9J*;K*>F*ݭ{bJD>>?;89;<>FOl}xskc\UKAHPUZ[\agdbceb__`[VUU_jpvwy|tmie_YKr>_f8Sa2G\1FZ1EW0EU0FX.CP,AL+>J*;G)>G)BE)۪wc^ŷXTPNKLNPɰR\g}|rhc^]ŧ]ZVSOSWXY]a`_`a_\Y׫W[_hrqqv|ynlje`THu@^l8Gc4F]1FY/FU.FT,CQ*AN*?J+>H+BH+FD+}xsjb`_`adgs|{{sk_SKCEHLQW^_`accc^YVTankinsy~|pookg\RHk?Wh8Pc1J]/GX-DR*CP(AR*?K-=I->F.@@.ߨ}uuuvw{wlgb]ڶXVSRRRRUY\`__acfjlnqux{yw|}ulhe]ULxDgw<Zk5Nd1H\-CU+BN*BL)?J)<J);J):D)٣ui_V޵NFEEJOV]aflslflsuxwvtrw}zjfc]XPItAdw9To3Mc-FY,DS,BI(?K%=L%:F%7C%뵽۱}ti_\YYXVTSS[cbaglosssssvzypg`ZTMGmAZw9Rj2Jb.F\+BQ*@M)>J'9B%5B%٬xsnhbVKFAJSX]afjnoprtvxykd]WRMIq@by7Sk1L_+FT,AO-<N)8H%5A%ܫukc[]̲_][YWZ]cjnsss|ymb[UPKDr=\~7Sa2J[.BN+;P(8P%5H%ܪuplbYPHJMXdkspnzwg_XSNHCx>l9`j1Q_)BU'>O%:I%ý뱹ةߦ{qggfiǧkigdamz}qe^XQJFBv9`p1Jf-DP*?L*ݨysg[WTansjbZROLBt9c4Yi/OL/ꭻ٫ު|wryށwj^XRH>}9n4_w4禿ᠽԞߝ{jaYND?{:o:笰Ϧդ͡zpaRI@@ឰϟӠԡs`SFF婮ӥӢȞ՟ܠteUU듬ݞƜƛ՟weeŃ쒣؞ۙuut喥䗯瘹ޜ頷ᦥߞϗ˔bo|}}Ӏ݄솫؋͐᚜ɤܥɕnuPn}Yuc{ccdehlszؠϦƙ›T[BTjKWlUZlU\^U_uVaqWcZdu^fapezp{~Ԃ߇S[4Sd=UgGWdGZeG^jH_xI`LcPg~Pi}QkqSv}VwZ^chtST3S^:SjAThBXkC]nD]vD^GcJhLbM\}N_ePbmOcnOd{TYj{SZ2SY7Tb<Ug>Xf@[m@^}@`BbEdGdJeJc|JalEakAauF~K`vSb:S~AU`GWuGXYHZqD^sAcBaD`DgEoEgE`n@_]<_mAv}EXlCKSQPIBvBC~Aw@ps@hx@`i<^f8]g<r}@Qb}[gsrqkddc^YrVi|R`P^lO\LnqIUa{s{sleef\RY`z{wsosxv~yt{~xrnieajtzynie`\XTbp||wsle_YURRRdv~vjfc_\TMKILPf}}~vtqg]ZWUSNIIJR[}~vngdaXPMKJJHFHKXfzsmhdabd[ROMIECACDUf}u{yqia]ZZ[ag]TQOHѪA?ŕ=ʈ=ޛ>Rgђqnkn۷qw}zvrkea\VPOOYdYOJEǙ?t9<h?FMbxhc^_aaaaan{ymhc^ZYXOFEDRaUJӶCƚ<q61~9İBO\sٛ|]\ZZZYWSNZfjog_YSTVQLHEϵEEP\NӪACFHӼJWer϶UTS~UWUTQxNpzEt<wGƵRUYUQJCۥJRI@B۾EEFNWԦGҡ8ВDPZdvŶnhrmEw{EvFu~H}JLxMj{GeoBai=a^8ag>wDMVRNH۬BFJGDDѼDDDHԥLLѤLZiw}Hdc>ca5c]7di9dl;ks>rBisG`d>Z^6T^5TV4UP5m7ơESOKFڬABڳCEHFDլCثCڟBױAԶP`qm?Zb9UX3P[4RV5TY5YX6^c8Z[;VU5OI/HO.HL-IK/mM2>ȻJJIEٶABٱCCCDDCڛBLVi|״t7Zc4V^1Sb1SV1SM/SQ.TR/PP0MO,KJ(IK'IH&JD)fT-p7BEHEحBBاCA?BEޣCܼAVkϓvh3Zc2X]1V]1TV0RQ.NN,JM+GE*EI(HH%KJ'KH)KK*`Q,uw7BDFE׷EE׬FB>BۙFQ]m}ooǣ{t0]d1[_2Yb1VY0SV-OP+KO(KI%KH$LM#MP(OP-RR,j\+r7CCDFHכHݯIC=BH`yweTT޴n8ad4_[0][/XX.TY,PO*LN*NH)QP(PT'OV+TW/YY0t]1;ǻEFHHHECƦE·GWgxwnbWfMWoDHD@ff7`_.ZZ-V],Rg+RX*SU,SU.S\,TY+U^.Y^2]_5r8@۵HJLJHۘBު=GRl′rdXLE>~h:~}7~\54`4䭵`ktGac/W^-T\,Q\+UV*ZW,X[-VZ-XW-[_.^d0aj3{7>FDBӥFŁJPUdsyrmgdv`fXYsPMMJ[JHLThN`N{MOQQ€vX{j1Y`.T[,OW+SW+XX,UY-SZ.Y\0_c/je.tl26=ǜE։>ߨ8BM]n}sj\OHBo?=|c?Af><BIT_adinnѹopK[h=TZ/MZ,QX)VZ)SX)QY+Z\.d]-vd-k3{:BsJPWhz~{yvsok|brYiVbeS\NVXIQKOXMMP`rTsY_cgmsw|~{vv~eyLki3W`-T\'R`&RU&RW)[V,dR,}c-b5>ȔGPcv敼⎹wrng`YSLFnA<i=~{>p`:yv6Y9<m@DNYbkt~~~|qVs<ah4XX,OY+QH+TW/\U3em8u=L[hvވ~yz|wsvytnmlorpnkizgwft`jvZ`Y\mXYTU\PQKQ[GRuEP_CNqDQ\ETK\bPdTY]agmv{}aFv;}1e0q0h5;DNcxۀtha[SL|LȤLtDҭ=i@ӪCd?Ҧ;n;Τ<g:̞9c<͠?h?̟@g@ȝAf?>k>>n>>@CA@><>@GOYco|qh_\Y\`lx҈ڄ~wqtrjfibkh_kbeoepqqq}rtrpqrpnmmjhhhda``[VVUROU[]_`bglsy~|}㖶ncXR˥LqFڮAnCʡFMU_iv؃ۖwonmgajsx~}zxz|||݆́~wpnmjliglkknoqtvy~{zzyyyzwtwzxv{~}|zx|ܖ|sjfbaa\WN¥EyB߯@rAݩBpFKXeuڅ}zyxwvy{wtvyyz|}{yxvuvx|ޕҐ܏}zzvwsqqtkpihfgaiggkmmgqpbsilwpww{~}ۄ|wxyxwutx}xtvx{~~~{xvtsrqqrtvyyyݍwoh_WTRRѦR}NKqKLqJ֦IwKǥMRXYZ_eltzۀڗ}zyyxwtrrrrqtwxz|~|zwuvwvutttuvx|ߓϋĆzwxwtnsnksntllvkyli}n~wzżԌێݎ~zvrqqtwqlmnnopqx݀|xz}zwsopqnlmorvᔿrdV˕RNNOzN߮M{NɩPV\eoyڄ٣ݬ{vrqqommmkikmnpsv}}{vrpnljjjjjmptxӋ̓|}v~~lu˲̊ݐޗ}}~xsnjmpmjfcegilnqv|܆ysnnnidfhfegjihpyޣlʣ]NLḰXewފܫ~}|||wskdgjhfffgimqqrw}{vqnjjjihfedccdcbjr}תܦ}u΅Ԍߗ݁ywvx{{{wsi_adcbeijlqvuty~~wqmjhgggjmgbaa`_]\dllmxЋu`bd̢sǂ{uvvx{ywrnieefedhkkks{wtwz}xrligdaaabdb`][[ZZZ]abcmv˞|ŸǩҬzzzvrtwy{wsnijljhggjnlju€zuvw{܀׀߁xusmged_[[[[[\^ZVVVWXWWXZaitЂlVZ_ԟuwyvsuw~~wqljiiigefhijpvsqtwz}{yz|{zupmjfbaa]XXYYYXWUTSSSTUWVV[_goП…ƈђqqqtxvuvx|uokghjfcbbfjkmmmrwxzuqrspnkieb_]^_ZVVWWWTQQRQPPQTWUSTVZ^lz橼seܼWUTžlτxmnoqttuvw}|vrnieddb``acefhkoprsuroliiid`][YWWVSQPPQSQPPPMJKMNPONOQTX`iՠљ{zr}{jlmrvԃlkjkmoqsuuvvvspnmhda^]]^``abcjromnpnmf_ad]WVUSRPNMLKJLOOPONIEGIIJJJKLOSUXoێydYO…LI~KMWbsۂtfggikklostvtsokjjd^][\\\\^`abfjjjlnkieb^[XUTTRPMKLNLIIJIIHHFDEEFGGGHIKLOQaqԝǗvrn{iw{dsi|nvȈ݇ieacegihhlqsvsqlggg`YYYZ\ZX[_`bbbehjlifee[RRSSTQOKHLQMIGEDCBB}CD}CBCEDDEG|FF}HKT]v݃uhaZVSMHJɦLӀJ˥HĀKOZeyߍނrabbbcegggilmonnhbbb]XWWXYXVXZ[]]^^_`a``^\VQPPPPMJFBGLGBCDBA~A}B|A@~@AA}BBACDDCEGOWfuqnyhu{brdssfuk|puzƏєߠ_\Z_dbacffgggghjle^]]ZXVUVWVUUVWXY[XVVVXZVSRQOMMMIFA={BGA};z?C|A}@zAyBu?y=u>z@v@{@??@}ByAABCJRV[ίZŸYZ\gsxXXW[^_abcegfefgggc_][YWVUTRQQSUUTUUTTTUUVTRPOMLIGDA~@|@zCzGxBv>y@AzA}ByBzCs@y=v>z?v@xA{?>{@yB|A}ABBIO[gpaRSUWY]a``dhecegdbaa]YXWVVRNMMQUSQPPQRSUSRQQOM~LKFA{?|=w@wCwEvGpDsBtAt@nBuDtDsDfAq>p>v>r@tCu@w=u?yBBB}BBGM`t؍cZPRTUVY]\[^b_\^a`_\ZWUTSQPNLLKNPONOPONMMNNLKMOLIEA{?|<w>t?v@xArAu@q>p<o>v?t>{=n>v?r>|=s?t@t<t8t9x:|<???DJby܆nWSOQSSSVYWVY]YVX[[\WSRRPOLJJKJJKLLLNQMJ~HFHKHEvK}RuL~GxDxBu?v<u<u<x<t<l=n?l<m9k:p;m8o6b;kAf?o=j=p>u9s4q3v3x7}<z<<BHcl]NNNLKNQQQRSVYVSTVWXTOOOMKIGHJIIIIHHIKHEDCEHEB|DFzEDu@s=r<u;s=s>s:u6n8n;k;n;n9u8n6r5k9n=l<s;o;n;q9r8s7z5{9=>?EKhSLEIMHDJPLILPSVSPQRSUPLLLJHFDzFIIIvG{FyE|DvEzF{C|AyA|A{C}E{B{?z=|;s>tAk=m9n:q;o>nAm9l1j4j7e:i=k9k5d5k5j7l9j9q9s8p8q:q=q:w8y;?@BHOn~NJGHJGEIMKJLNOQPONMMNKIJKGDCBzCE}EFwGwIxFyCuBwBvB{Bz@y>wCzHuAy;x98w:s<m=k>n=u<m>j@i;e7j9k;f;f<i:h9g8l7l8p9n8t7q8n:q;q<u;y;<>ADNW~߈hIIJHGFFHKKKLMLLMNKIHG~G~GyH{JwE{@w@wApAxAuBvDrHtLyGvBq@q>nAvD}@u<qCqKgAo7o6t6w6p7k=kDkAm>h?l@j>k=o>k?b=g;m<i=g;l9m9n9k7n6o9p<t<w<z=~>==BGS`v^FHIJJHEF}FHJJJHGGGG~G}FzF{E|DwDwEyB}@w?v?t@}At@s>pAqDr@u=p<p;o>u@t?p>n?tAl=o9k8k7m7l7k:m>j<m:i;w=m:p8l9p;g9i8h7i6j8s9n8s7l7l8n8r9u<?????DIVbeTDFIKNwIE}CzBuExIuHwGtD{B{A|@B{EyEwEyCvAsAwAy@w@r?r>p@rBm=o9k:m<k:o9l9l9j;o=p>l@f;k7h9j<f:h8f7i7j7h8e7e6[8f:d7h4b5g7c6i6b3h0g5i:f8l6f8k:k8p6q<|CA@@AFLXeԀVLBDGGH|E|BxA|@vAuBrCrDsBw@v@x@vAxBuAq@p?q?r>z=w=v>p=q=p=o>n:s7m7m7n8p8k8k7j8o:m:l:i8k6h6m7h6l6f6h6f5d5d4g3b4e5f5k6f5g4i4w3m3p3h5e7h6t5n7r9p7s5v;@AABCHNdzyneݿ]RGDACE|C}B@w?m?u?t=s<o>qAq?t>q?s@k?r?u=q<h<m=j;r9u:q<g<n<s;l;j8l5i4m3p5l8h7i6f6j7j6j5j5g6c4g2b3e5a5d5b4c3`2d1a1d1d4g8e5f2f1l1m3k6d5g4d4n5o6r8s6t5t9{>y@CDEKQpӁusqia_^Y}SLFC@BC}@{=x=w=t=w>t>u?r?t?t?w?s?{?p>s=r:y8o9t:n9p8p8s9k8o8l8g8j8l9k7n5m6j7i8k8g8k7j5k2g2g3f3j3e4h5d4f4c3f3d3i3c1g0f2j4f4f4f3h3i3k3k2r2o2s3q5w6v7w8z:;>@CGR]vpkhe^XƷQǼJɝLǹNƗMĹM‹IEGIC>×FĭOƞLݭJGEB@ABy=x9q:u;v<u=q?rBp@t>v?rAj?p>j<n;i7n4i5l7j7m7k7k7d5j4j4k5l9l=l:k7n7j7g9g;`9e8j4f0`0d0e2f5c5d5b4a4_3b3a4d5]2d/f0h1c3f6e6h6e3i1m0r0s1q2n3v5z8<:9;>CIYjݐ臼ҁ|tmhccbeifd^X\`ceghqz}jLGBBC~>~:z;y<v<y<s=r>p<v:t;r=n;q9o:s;m8m6k8k9k8r8m7k6l6n6l5o4k6o8i9f:h8k5h7j8c7b6e5f3c3f4f4i4c5c6a5`4`2c1d3k5d3h2h2m1i4k8j6q5i6j7m6v6t5s4u7z9:;=?@BHOhqd\¾UåT¶TOȿJϚN̼SʒPNOQUZcmnpkflsx~TLDDD}?;<{=p<u;q:o:h8r6u8p:l7p5q8o;l:m9l:k<f:l9l7m6u7m8k6i4e4i4e9f>a9e4f5h6a5d5c6c7a7d8g5d3_5b7b5a4a2c0d2g5c5g6f4j2k6l:j7k5g9j>j=t<t9u7y:y>{<;@FFFMUwڅ{upjedca_```_cglpru|fUDDE?:;=x;z9s:t;r8v5q7n9o8s7o8m:n8r6o8r:k9o8k8h8j9i:j7h4h4n4i6l8f5d2f3o5g3k2d4e6b5d5c4d4b5e5b5a5a4d4f4m4h3l3i3m4k5q7k7k8l9o;q;|;v9w6z9y<=?CFHJWdzm`\XRLKȽKГIɶHIKVbfjkmqux|y_EEF@:x;{=v:w7o9u<w8t5k6n8p9q:m9n9m6m4l6l9g8j7i9g;c;i<m8i5g5i5e4j3i1f0b2g4b2d0_3a6a4b2^4b6a5c4a5`7_7b8e5i3f2h1f3i6e5l4g7n;p9t8p9v;r8z68:?DEGKOasvrnkifd~efa\^a``ejwyNGA=9~9|:z9~7w:{=w9y6q7p9q9v:r;r<n9l6n6l7h7k7i8j9i8l6j8i9h7l4j3n1i2i4e3i2f1g0b2`4a4g3d4h4d4g3c5b6_6b6f4o3k2k2k4l5m7s8q8v8v88x9z:y99:;>BEIPX}{naXPNLŋN˹QЛSɽUVX`iouplqwxyWJ=:8~88{8{8u;z?q;s7s8p:n:u;t=s@m<q8u6m5c6h7f7k8l4j1f7g>f9k4j2j0e4h8a4g1g1g1d2a3_4e5d4e3`3c3d4c6\5b4c3l3m3m4o4q5s9s=t9w5t7z9u9{9{;=<<>AFKVaڇـxojdb`aaehikkkorx~hGB===;~:};<y:r9s9p:r:};w=y?r;w6t8q:k8o7m6s5n3m0h4i9k6s3l4k5k5q5i4m3k3m4g4d5e5k6g5i4d3b3h4k6g6i6k5r5n8p:q7v5u8s<u97z8~:|:;;<=>BFMTm}ria``[VPKLMċJƲGȌJķNW`is|RJCCC?<z:9{:x<z;v:p:v;s=r?k:r5v:p@j;l7m5n3m1k0f2k4m3o3j7l;k7m3g4i6k6k7h7i7g7i7d6h6f4i3q4m6l7l8p8o8k<nAr;t5y8t;o:y:v:z;{<=<<>@FLU^܋䂮zqmjf|cscvdyhvmto~rollkheimsyfIGDA==<};|:::x:x;v<r<q:s8r;r?n;m7k5k3k2m1m2s3o4t5n6p8l6q4k4g4g6l8l9q9k8q7k5m4j4m5m5o6m6l6n6q6n8n;q8q6u9v<v;~;{:z:~=@?>AEKRh~燾znf_WPLIJLȎLŶL PU^gpzPKFB?~?@|<99:y;{<|;v:t;s<p=q>o:m7g5l4j3l2n2p2k5m8k6m5g5m5m3g2a6l:m;l<e9l7m5n3h5l7i6o6o5n4l4p4p4s5v6t7u:y>w=|<:9>CBAEJQX{\bh}etbkdmeokzrrsw{{|||}{cLE?>>;9:~;|<<{<y<w:x9r9u:s8s7o5t3p3n4n4u5o6m7n7s6m5q5m4j4k6v8n9n:l8p6q4y2p4s5o5u5q6q6r5v5s6x8v8w9y;>|<:;=>@@?ELXd6@JPWal|RH?>=;9};{=w={=v>z?y:t6l6s7w7u7n5s3t4p6j7p9o8p7o7o8j6o5m6n7m7n7j8q9p7q5p3r2m3q4n4q5p7r9v7u6p8u;v;x;x={?y;}8|<A?>>>FN_qTev{fLGC@>=|<|<=~;:y8u6s6u7v6|6u4x3r3u4o5t7r8u9q7s5o4u3q5v7o6l6o6}7s7t7r6r5r5x6s6s7p7r7t7x7u8x:y9~9y:y<}:9<?=<AFQ]en~rYQIFD@<<=9|6u6w6w6u7q6u5s4r4i3n2k3o5q8r;p7o3k2o1o4q8m6k5l5o5m7r9r9t9r8u8t8r9l7q5q7v9t9w9y8z8v8{99:{;=;:DO]lkkwxrcTE?:<>=|<y9{7x7y7t6u5v5t5o4q4o5n6q7s7p5q3n2p1o4r7m6i5l5i5o6v8u8}8w8v9r9u9r8v7u8~9y8w8y7}7x8|9}9:}:;=?FNYehkry}aF?9<@ABz={9w8w7s6v5|6t7n6p6s7q8s6r4o4r4o3p2l4o7n6o5t5n6p6t7p7w8w9s:l:r:q9s9p9z9{8w8x7w6r7v9u:z;t:ׂ9ь>֝DڷINV^elmow{k_SKB@???|;|8w8w9x8t8r7p7s8q9r8u7t6z5s4v3p7s;q8r6q7l8r9z;v9y7v8t9r:v:t9x8w:;w8r6t5u4r5v7t8~:};=@ȪCҴHNU\cimr}nYE@<@E?9w;y={;x9w8t8x9s;r;v;r8t6n5r5n:r?q;s7t9s;s=x?w:x6t7s9s:u;t9t8s;v>n9o4s3o2k3l5i7u9|=ABCɩH׽NT[agnuo_VLHC?:|:{:x8z6v6q6u7t8v77w6x5s6v7u9y<v:v7t8z8t8|9w51t3s4q4z4s4t4r4w5k2j0h.j-g.i/j2q5z9=?AGиNSYaiuzk]OBۄ?؃<w9v7p5v4w4w4w4s5u4v4r4r4q6s9w9w:x9r8n7p6j4p3l0o-k.j0f.l-m.k0l.g-^,^,Y*`)_)f*g-q1u59<@ũGֺNSXbl}~m[P޵EΖ@ˊ<};x9s5w1s2~4o2l0n2r4k3o2o4n6n4t2o3m4g4n3j2n1h1f1c/g.a,f+a,`-_,\+|Y-zX/x[-|^+b,i-n1y59>A¤DɰIOW`kunZQѪHB=}<w<m5l.c1h4]/d+f/e4\2e0o2e4a/e+f.a1V1_1b0`/a2Z5T0U,T+V*Q*V*U)U)yS.{Z3}`0a.b/o0q4}9>DFѭILP\it~lZ׻QàHB=;|9s3k-e.b0_/b/_/a0Z-\+\-X/W-Z+Y-\/U.X.W+R)S+P.{N,yH*xO)oP'gS)kS+pS*sQ*vW/|^3c2f1q349?CGƴNV\bipyyiZײQĤHB=:~7y1o,e,c-^0]3V/W,O)U'O(R*L*O+K,R-J,Q+P'{O$wK%tO'qM(rN)tR'rV%qZ(vY,|V,}\,Z0f4j4r5z79?E΢GJWdluvx{l]TưJC=;:x5s1k0g/c0|`0t\/tV.uW,q]*nW,jW-fS.oM/xQ.rX-mT,kT+iT(iQ&jR(hV*gS+kT,pY-u\-{`/b0f0o1o5v9y:~<AFLRV[eouz}|wxz{}~~|}oaWMɨE===w:v7p4o2j0i.|d/|`1{Z/y].vY/u^1t]2wX3zW0w].u\,u]+v^*wZ)xX+z].|V/`0d3i6j5n5q5{6~:>@ãCдKTY_els{}}}}zwutrqomqvpjotuwtrw|rintwzxvvw~~uj`YSMȻGDB<7}7|6w4v3p3o3g1f0h1l3f2`2|d2}k2~g1f1f/d-i/r2m2t2x4}7}9{<?CFƳJNSY_dimrxzywurssrppoljnrnjmoqsqouzrkotuwvvtrv{xmf_\ZUQL̰GŠ?89;98z6x5p3r2v3s5r3o2p4p6r6q7q4u1w4}7x5468=ǧCҪJQSV\cfjotvx}ynpsolormhlpnmmnjgjnlkkkmonmsysmptttuwrmnpnlpuwytmfc_]ZVSLиFEECBAA?>=y<~;{:~:u;>~A?=?BD FѹIMRX\_`afkosvy}wqlmnljlnkhjkkjkljhkmmmkjlnpruytoswwwwxqkptqnoqpp{{tniedda_YTRPNLսLӼMҬKϰJ͢F˧CʔBǥBŚAƧ@ȡEͳKӮJӾIKNSY]bhnnnmmptx}~ytolijkjjihikjihgghikjjkmnoliknsxxyurv{zzyyqjqytponjgkoqsx}}xtsspnhcdfc`][]`^]]]\\_bbaehkmpruy|~}wsppqnklmkjlmkjihhihgikklllkjpuxz{{{z}}zwsturponjegiiins}xsw|xtniovwxxxxxyzzz~temvsquyrlnpnmnpmkjijkgcgllljijlt}}}}~||}wrqpooidddb`diknsx{}umejponqutsrqnkkkjjklheddgiijjjmquz~}wqpnooibdgdbcefhikz{ihgfegjjknrvzvrmihghilpg_bffgghikpvwx}wqomnphaejgecabc`^djpvzpgikhffgiknpuzyxrlnpkgghebehfeefhklnsx}~{wsopqkefhgfdba`][^bdfwokgfejokgedhlmotz|~votyoecacegjfccdhlifoy|؄~xrrrmigffgeda^[XY[YW]cx}edddegigffglrqpv|||vppplgedegggfedcfihgmszԂ܅wwvrooplhecega\]^[YZ[XVY\fpf`[^bcedcdfhjqxtqw~|zvrmhijhghigefgdbdfghknyԅ܆ل߀wooomlmnjgdadg]TY^\ZZ[XVVVTScsxda^_a``bdfhhhntssw||}wrmhhhhhgfeddecabcegjmrw{~xsolmnljhfc`_^ZVWWWWWXURQQRRY`rbbcbba`^\afhjhfkpruwz}Ԁyrmigfhjgdcccca``adgiljiqyyyz{|}}}zwpimqlgfeb`ZUWYUQSUUUQNMMORPNV_{q]]^^]]\^_adfhijlnqtw{xuuunggfgggfdbcedbaadhiijjpwvux{zzwurojefheccd_[YXYZUOPQPPNLMNNONLOReya\XYZYYYY^cbbdgkonmpsx}sjqxoffgfegieadhfecaejhgilpusrv{yxrmjhda``__ad]VY\\\UNMMLLKJLONMLKHFP[us]XSTVVVXZ]_^^_`ceghkoprnkklhdfgdadgdbbbbc_[`efghjmqrtvxvurokgc`^[]`_^ZVVWVURNLKLMLJJJJJIHFELRapc^ZTNPRSTX\\\[ZZZ[\`dgkihjlfabcehc^bfdc_\^a[V[adgghjmqvvvtrqqkfc`[W\a]YXWTRPOOOLILOMKzHEFGFEEEGJMQj~ZWUROPPQSSTVYYYZ[[Z^adggggghhfefgc`cgc`\X\_[W[``adgjlosturpmkgc_[YWZ]XTUVROMKKJJJLNKIFDEFC@@@BCFHYjraQQQQQPOPROMQVWY[][Y\_acegecipkgggdbehb]YUY^[Y\_]\agilnpruqnieb`[VWXXYTORVQLJHGFHKLM|JG}EDD~Ex@|;s;z<~<=z>@HPkaXOOONNNNOQONPRTUX[YWXZ]`dhijloljhfdcegc_ZVXZXVX[YX^ecbfjmqnkgc]XWVVWWWSPPQMIIIGEFGIJ~GD{D{D{C{Cz@>v?r@w><{>ADHZmqPNMMMLKLMOQPOOOPRUYWUUUY^cimqonnnieddefca\XWWUTUWVU\c^Y_eimjhdaXPSWVVVVSQNLIFHKGD|D~DyF~H}E{BvCxErCxAvAxBsCqDl@t<q?vBuA@JTuxeROLLLKJHGKONMNPOOSWUSTVZ]`defhiklgabca_][ZXURRQSTUVY[YX[_^^^_\ZUPSVTSUVROLJIIHHFE~CB|CDzC{AvA@t?>v>w?sAtCrAu@q?t?u>=FPdy^YUPLKKJIEAGMLLNQOMQUSQTXZ]^_]\`ehkd^`c^YWVWYSNNOPRUXVTUWXZTORVUTRQSUSQTWRMJHJMyI{EzE}FzCwAtAwAnArAk>o<c;r;l;r<o?rBvCqEj@p<n;w:zCLT\}}}߄WSPLHHHFEC@CGGHLQNKNPNLQWTQUYXWZ^__]Z[\YVUUSRRRNJJKNPOOOOQSOKOTQNNOONNNOQNKGCFHyEuCyBBw>p;s<v=o=t>m=o=o;z9q;v=p=r=r>r?n;t6p;w?xEzJNQl}}oQNKGDDE|CB{A@~@ACEKQMJKLIGNVNFMTSRUXVTUWVUTSTUPKPVMEEEGIIJ}IHJLIGMSMHKNKHJLKKvJ{JxD}?sAzDpBsAv?v>s9s5s7p9f:l;h<n>q;p8h;l>k;m8n9m:k5n1i;uExG{IHG\q}}~hRKEDCC}B|DE}DCBBDFHKJJIHHHMRNJMQPPSWTRTWVUSRQOLHLPKGFFHIJKIHJLJHKNLJIIGFG~H~GG{G|HyECzDEuAt>s=q=r:x8r:s;k=n?l=r;o9m8i:j<i:l9k9k:l8r7o<|AvBxC|B}BRcn`SI@~AC|A|@uDyItHzGwE}C{EGuF{FyHJ|GDGJLONNNNNNRVSPTXVU~SRNJ{HF}HKJIHH~IJwKM|JHJM~KI|II}JLHD}DED{DqCxCxDuFqGwHyGwFrAr<r<o<n<o<m=k>f@jCj=j8l8h9e9f:c:g:j:j;h<j=g=q=o=u=x=z>|IUvcWLGBA@~><xB{HuE}Bw@~?~BEwCxB{CD~D}DFGGGJMMMKJLNMLOQONMLJI}FCyCwD|FH{F|D|DD}EEDC|EyGwE{CzE|GzGFzB{?vA{CzCCuCvDsBr@q@tAq@s@o=p:n:n;k9i8h9g:g9k9f8`8b6c5e6h7d6e4f6k7f8i:i9s9o8x8t8y8AKeYOEEEzA{=x:x8n@rHjBr=m<x<}?yCnAu?w>|>}AyEyE|E}B@~FM{LLIFyFF|GI~J~KyI~G}G}G{G{HuDvAm?r=uBvGqCw@w?z?{>{>}>z?r@rAm?r>vBwFnCsAl=p:f=oApAtBoCoEl?n:n:k:g:l:h9i8i9k:g7e4d5e6d3f0f4a8[5`2d3b5a2b/a1b4_5d7b6g6b4k3j3s3|:ASf{{UMDB@?{>x;w9t=vAp>w:t:z:x<w>r>x?u?z?wAtCvByB{A@~DH}GG}EyDvFyHyG{FzE|DzD|DyCzBwBzAsAs@q>r<q>s?r?z?s@vAu?s>u?y@s?q>l>j>rA}DqAq>o>s>lAnCoAr>l>k?i;n7j8e9h9n8f7f7i7p8f4c1`4f7c4c2c5f9_4`/`2]5_2d0a2`3b3g2c3f3e3h3h2o2u7}<ߒIV}~~RKD?;~=z?v=x;u;r;j9p8s8s9t9q:l<o?k@rArAsBt@v?u?x@vBxDsCwBuBtBpFsKvGuCu@v=w?wBs@t>p<p;l=p@r>p<l:n8m;o?hAnCp@n>l@qBr>p;k=k?k@pBi>l;n?nClDkFo@k;d:e9_7h5i7g9g8e7^6c6h6d6`2^.S3_8a6_4]7`;\4`-b1^5Z3^2`2`3c0_.]/a1f2b3a2g1l4w7օ?GpهOJDB@~>{<x;~;t:t9n9l:p;n<p:p9l;n=n=s=o=r>t>w>s>s>s@uBrAtAr@r@n@nAn@n?q=p;t<x=r>v@o>m<l<p=p=s=m;n8m9p:j=mAm?j>j>m>o<r;j;m;j;q<j;i;k=n@j@d@i>j=f;f9f7k5k7h9d9b:`7`5a5Z6^3e1^1e2_4]6]6d6^3d/a2b5]4]2[0^/].X-Z/^1_2\4_3b1h2n3};De҅MIEEE~?y9s:s;j9m7p:n=s>p@o<m8f9j;m:k9h9p:v;s=n=o=o>n@m@m@m?m>j;k8h:l<r:q9r8o8j=mCi@n>i<m:n<k?g<i9j7k6d:i?n>i>e<i:n;h<`9d7_7e7f9e;h<j=k;e:g<e?f<f:i8j6q7g9a;d=c8a4b5]6Y5_5^0^,\2[9W5[2U2[2\4^6[4Z3S/Z+[,Y-V/]1^3^6_4c2e1k0p8ݎA[uԆJE@@A~=z:t9t8n7h6k6l7r:p=m9r5j7h8h8g9k9r9r:t;l:l:k;h=g=i<j;l;j8o6i8j;m:q9l8i7i:m>m?z?m;n7i:f=f:f7i8o9f:h<i:j9e8e8f9`;a7g3`6a9`8`8c9g;c9`8a9^:`9e9c6d3e4b6b6j7a7b7_5`3\3b3^/\,Y/]3Z3\3Y1W0Y3`6]4_2Y1]1[0^/[/b/_2b4a2g0d3j5s:ԁ?Vl~~؇HA;~<=}<x<p8r5s5o5i3p2y6p;e7k3h4j6g7h9l8l8m9l:d8h7j9f;a:i9i8j8e6g5e7g:g9h9d7f6d8e:d=mAe;h5b8f;f8f6f9f<_:h9g6f4c5f7f8b:`5a0Y5_;Z8^5]7a:\8^6b5^5X7^9Z5^1^2_3\2^1W5]:[5[1W1]1\.Z,P,X-X0[4\1V/S3Z7Z4X2U4X7Y4Y2Y0].[0]3\1`/]5i;q<т>ޚQd}}LC:;<}:z9s8q8s9x:r7r3q7r;k8n6m8q9h8h8j7h6j7l7g6i5i7h:g:q:k7p4g4c5g6h7e6f6d7i7c8a8g;r=i9k6f6j6f7h9d9f9e8r6g6d5e5k5f5d5`3d0b4k8`7a7_5g4_5]6_4a1]4a7]4^1^0a0^1]1Z3a6\3[0Y0a1^.`,W.\0Y0]1Y/U.U0[2V0U.V0X3W3V3Z0^.[0]3_1c0e4o8t=~AڪRc{{QE::;y8y6s8s;r=t@u:o5g8l;h:l:o;k=c:i7l6h5g5g5d4g3j6e9c:h;d6h1e3f5n4g4b4b4`6c9_8b7f8h:c8e7d4c2`7a<_9b7_5f4`5a7b5f4e2a1Y1`1`3c6\7]9X4^/\3_7_2^.Z2^6]3^1[/^.a0[2S2[2\0[/S0Z1\.X,S/X3T0Y.X.W.T-U-Q+V*V-W0V2Y4\1\/Y1\3a2d1e3o6u=˅E՛TcyyaO=;8|9:y9w9r;s=q9n6l7q8l8m8h9i:i7o5h5f5g4h3e3f4e5a5e9i<f7h1d3d5g6g7c5b4a4f5a6e7d6e5a6b7`7`7^6`5c7g:b7h4a4_3b3h3c3b3_1c0`1e3]5_8[5`3_3e4a2a1^2`4^3d3]1[/[1[3Z2_1^1b2X2Z2X0T.V/]1Y/\.Z/]0W/U/U-],X-Y/X1]4\1]/[0Z2b1h1l3t5|<ȈDѪSbyrYA;6z:{>};t8l9p;n9l7l6o6l6f6[6f7i5h4a5d6i3f1d3d6f4c2h8d>c8c2`4d6e8d;a7`4^3`2]4a7c3a0\3^7[9^<Y5_/g6b=\9`5_2^0`1a2_4`6^2^/Y0[1W4Z7X7[8[4_1a2a4_3]2W4^6Z3Y1X2Z4^2\0\2\5T4V3W1V0V/Y/W/Y/W0[2X1V1T/W.T.V/V1Z4Z1\/]0`1e1j2p3x4<̋DלSbsiD?:~<x?w;q7q9s;p8i5l6s8l6g3g7m:i8j6e6c6e5f5c6b7b4_2b6^;a6b2c3i4e5c7a4a2_3`5_4_4a2b0^2`5`6g7^4_1b5a9^6`3^3]3a3`3a4c4`3_1Z1W0W2Z4Y5Z6[3\1`2f4`4_3]2c2[0W/X0Z1Z/Z-[/_2W0V/V/W0V/Z.X.Z.Y/b0Z0W0V.W-V-V-X0\3[1]/_1f2g2n2r4|6‚;ЊA޶Xon~xHC?y?y@w;t6u8s;v7l3h7m;k6j1m7k>f;g9f8e7e8d9a9c9a5`2c5a8b5b2a2e2f2b3`1_0_4_8b5b2c1_0\1_3^2a2[3_4c5`6]4]2^4`6j5b5c4_3`3]4\2X0W0[1Y2Z4]2]1]3_5^5\5]1\.Z-X-X-X.X,Y+Y-Y/U-W,W.W0T/V.U.W.S.Y/X/X0V.X,W,Y,Y/Z3Z1]0^2d4e3l2p5{9;Ҙ>]}m~ZL>=~=y:v8q7n6p4j3k6n9k6l3k6l9h8g8d8g7e6d5c5i4c2_1a3c6`4d2`1f1d1b2_2_2]3]5_3f0_0^0\0c0_4d9]4a/_/`0[0Z1\3]4^3[1Z1V1Y1[1Z/X.Z0c2Y1X/Z0^2\2[2Y1W1Z/Y.[.Y/X.Y-X-Z-X,W+V-Z.X.]-W.U.W-]+X,X-Z/]1X/]-\,_+\.\0]1`2a3g3j3p4v69?՝Fqm~lT=;:{:v;l6p2s2n3m5k7i6i6g5i4i6e8_8c8g5d2^1c0d0b0b2a4Y3_2W1`1c1b1[2\4[3\3W1]/X/Y0U.]-Y6\@V5Z+Z*\*U-Y1[2Z3Y0Y.V/V0V/Z.[-W-V0[4Q/U+X/Y3Z1Z/W.Y.^.\.b0[2W/W-X.X0Y,W(U,W1U.X+Y-W/V,Z)X*X,\/Y3S1Y/]-^+^,].^1a5b4k3o4u6z79ĔCܴNocD?::~9u6u3q3q4m6k9j9k:i7i4h4g5c5a5d3h1b1b1c/f.e/f1`2k3a1b0c1h3b5[7]5b4_2b0\/[/Z/d/^4^8Z3Z/].d.]/[0Z1Z3X2[1X2X3Y1_/Y/V.Z0d1Y0U.W0V1Z1\0X1Y3X1W/Z0[0X/W.W1W4X0X-X.Z/W.V.W.Z.Z.]-Z-T-Y-Z-Y.X/\-`,`.^/`2e5h4u3t5|6;@ǛQbqqL{C;9}8u6r4k4m5k8k;k=j?i9h4h3f3f2c2`1b1a1a2c/f,g-`.V1a4c2_0_2c5j7`:\7_5b3_1]0\/W0\2^1^1\2[3\2^2a1[0Z1X3T4X5X5V6W3X1V0V0Y/\/\0X2[1[0^1[2W5W8R4V1V0Y/Y/Y0V4W8Y5Y2Z0Z.Z/X1U/[.^0]2_0\/]+](a+\/].a.e/a1`3e6g5q4q5{7?ƒHӣ_wn|dQ>;ۂ9w7v6o5o4l5l6k8i:h6i3f3c4d2h0c1a2a1^1c/h-c.`0\1`3]1Y.\2]6a6`5^4^4^1].^/d0]1Z2\2`1[1\0Z/Y-[-Y,Y0X4V2Z/W1R3U1W/W.U.V/Y/X0V1Z1_0\1\2Z1]1X0Z/Y/Z0[/a.\0X2W0Y-Z.[/\/[0Y0`0^0]0_/c.^,_*],Z._.b.d/c1d3i5n4x4y7:DСOwl|_Bw>x:q9t9o6p4j3l2l3j5e3h2g3f5b1e.c1c4f2d0f/d.^0^2\2]3Y0\-\2^8^4_1^2]3^/^,\.a1`2\3X2[2W0Z.W+[)[)[)\/Z6T0X*Y-V0V.X-Z-X-T.U0U0W1X1Z1Y1Y2[.[+X,Z-[/Y2Y/\,b,X-R+Y)Z,^0b/[/V0[2]0]/_.`.[-_,[-^.c.d.g/g1g2l4q4y4|8ʼn=ʚJWps\Ky;v;}<w<x<o9n5l6q6i4m2j2l2h/i,d0e4g5g6e2d._/_1]1_1^/c-a1c5^3b1]2[4]3a2`0d/_2^5[4_4[3^1\0a.\-\-]/a2X0Z.X-W-X/[1Z1]1V1U0W1\1Z1[0Y0W1Y/].Z/\0[/V.X.W.Z/W1Z-_*\.a2]1[0[1Y3\2[1b0d0b/g.`.a.e0f2g3l4k3p3w69?ʑEhuvY<s>w@xBvEn?n9e8j8b5j2j0k/j,f*_/e5j8e<e5b.[/\1\0^0]/a.e0^3W2\2\3\5]6_8`2_-Z2\7Z7[7[6[5\4\4Z2[1^0[/R0U2V.Y+[0X6V6Y6T3U1X1Z2[1Z0Z0Y0X1Z2Z2]3_/Z+[.Z1Y3Z5`0^,Y0\4X2]1b2`4`3b3j3f3b2a1^0b/j2g6f6k7l5s3|9†?ȏFطMpiOԇFu=v=|>u:x7n4r1k2q3k1l0j.h,f0h3e4a6c1e-_/\1_0b/`0`1_2[3Y0\-]0]4^5`6^2`.^.`/[0Y1Z3Z5Z4Z4Y1Y.[-\-V-U-W-\,X.W/X0]0X/V-Y-[.Y/Y0Z1]3[1[/[/`0[.Y,[,_-Z/Y1].b,]/^3]0b.c/e1b1c2e3h4a3^2^0a/f1g3j3n4r5w7<ȎBϨ\vk~zbNف;p9t7r6q6j0l*h/j4f3i2j0h/k0i2d1b0a.c-`/_2c0`.b1`4]3[3Y.[)_.^3`3_4Z2]0a+]'\)[+Z0Z5[4Z4Z/X+Z+Y,V*W)W+X.R+V)V*X+Y*Y*^*X*U-W0Z3\6]1\,Y,Y-W-X-[+[*Y,[.Y-\,]/^2]/_,d-c.a0a2b3b5\4a3^1d/i0l1n1s1x6};@ӟFsmq^KAs7q5q4n2o1l2l4j3i2g3f4i3o3f1c/d/g.a0`3_2Z1_1d1^1]2[.[+\-]0]1b3\1Y0Z/Z.[,^+[.Z2[2\2Z/U-W-Y-Z,\+Z.\1Z/Z.W,W*Y+Y-Y,U,X-V.Y0^3\1_/[/V/Y/X/[-[,\,b-^.Z/`/a0^/[.`-c-`/_1a1c1e3n4h2k/o2t4s4x5;BXߴmۘpn\I7q4q2n5m8p6m4n3h2f5f9d6g4b1b/e/c0_2_4_4]5]1_.[/\1[/[.Y-\-W0[3^1\0W2Y5[0\+Z-Z0\0\0_/Z/V/Y/^.Z.\1_5`4Z3W.X*\-Z0Z/Z/c.Z-W.Z0V1[2^2[2_1]1^0\/]-_,c/b2h0c.`/`0_.a-^.b0b/d.g2k6i3o0t4t8t8~9AϟJ߰oؕuyndUG~=t3m4i4k5k5k4e4f5k6h3j/b0a1b0b/^0_1^2a2^0`.]/]1^._+^,d-\/X1\/d-\/Y1[0^.[.Z/\.Z-\.^/Z.Z-Y-T-[1e4\1X-X,X+[.[1X.W,[,Y-].`/[0Z1Z/\._.a.\.Z.\.]/a/e0e/d.c/h0c0e0e/k.f/g0j1q3n3t3t6w9z<?ĢUkڏzrolaWF}5i3h1h4k7p6k6e5i4g/d+\/`4c1_.Z.\/Z/\0[/]/\0]1a,b(]*_-`.[/W-\+],[.Z0]2\0\.a,\*Y-\0\.[,[,X-Z0[4S.W([*[,^/Z3V.X)Y+\.f.a.]/Z1W-\*b+^,Y-[.]0]2_0a.d.b/a/d0b2d4f0f-d/i2i1m1m4r7t9z;z@ϜFiލuvnmme]PݎCÀ<h6j5i4l5r6l5o4i2f1`2b3_0].\/_1^0]/]/`/]/]/`-k+a-]/\.].Z,Z+[._2]2b2^0[/].].]/^1Z.[+Y,Z-Z/\1X-X)Z*]*\,Y.X,Z*X,X.^-a,[-\.Z-[,]-\/Z/_0]/]/]/\/_/b/b/h0c1e3f2f1i2r4o3o2q6t:}=AN\݁q{upmjlnidZQFЊ;o6l1f4m7j6h5i6e7]4a2\0].[0_3c0`.^.^/].[.Z.b/a0]2X/[-],[,V1[6Z4^3_1\0^1_2a2\3U/X+T,X.X.Y.Z,X+[*Z)])Y)Z*W,V-Y/Y-Z+U+Z,\-Z.[0Y3W2^2]/[,].]0Z/]/]/`0^1e2j4g6i6p6t5r4t8=ĉBңG]st}wqqpmjihfd\UOIΓ?s6n5r5i4a4e4h5d2k0c/b.`0a2b0b-]-].\-[-\.c0^/_.[.\.^._.]0]3\2^1]0Y0\/`._/^1Y.],X-\.X-Y,X*V)X*U,Y+W*W*T+V-]0Z.Z,Z,^-[-W-Y.W/[/e/[/V.[.a.^/^/^/`0d0i1h3c5k4r4r4r5:ď>խJVi|x~smhlqmjfbcd_ZXWIҘ<m7p3i3e3d3f3b0e.e.b.e0a2b/^-Y-]-]-Z-X/[1X-Z*\,_/`0_1d0^0[0^0_0[0Y-\*`-[0V.X-V.X/T,W*Y)X([+X/[-W,X+V*T-W1X/Z-[.\/\.Z-],Y,Z,Z-U/W1[/^-_.`0_0b0k0i1j2i4p3p2q4{7;ҵ@Reurwnkiknligec`^\YVMDɘ=y6o5f4e4k4e1b/b-_,_0\5_3^1].b,\.X0X0Z0W-W+\,f-\.[/\0^1\0c/_0^2Z.Z+Z,Y-X.Z.Y.Z.V*X'V*X-Y-V.U-R,X+[+W,U-X-^-Z.\/\.Z-X,W+X,W-W-Y.\,]+_-g/`/a.d/i/e3g6k7p7y:>÷GP[fsl|wpiijkljiiic]^_ZUPL۸Bʓ9t7m5a5g5h2d0b-`*_1^8`6^5\0],Y/Y3V1X0S.U,X+Y+R,X.T0Y2U0Z.]1Z4W0Y-X,W+W-Y0[/X.T)T$Q+X2^0W.T-S,\,Y,X+V*V,X.U.[/a.Y-T,Y+Y,X-X,Z,_*^)Y,]/[.`-]-b.]3d9g;v=A̵ESadhr|lyspligghhhfda^^^\ZVSIѪ@;{7q6i5h4g3d.a*`/^4^4`5]1].\0]3Y/_,Y,T,V,U,W-b.[/`0X-V+Z,Y-X.\/V.T,V,Z-X,W,U)U&W*[/Y.V-R.M.U,V)U)U)W,X/W.Z.Z.V/W,b)\,Z/Y-Z,\+c*[,\._-g,c,f-b1e6r:>«DJT]bgowl|vpppjdddfhc__`^]^_\ZP׶G@97q6h6j7i0e*d-b0\2_5\2^0^2\4U.[(`*Z,X,Z-V-\.W.Y.U+X)\(['T,W2Q0V.T,W*V*W+T)W([*X-W-X-T/U1W,V'S(W)Z,X0V.Y-X/V2R,['[,Z1Z/Z-V,\+W,_.`,c+d,f-`0t39@HPUZ`glr}i~|wromhdddegd`__^^\YXWO޾HʥA:6s2n3p5j1f-e0k4b2e0`/a.b/c1Y-[*[-^/].e.]-a,[-[.W+Z)\)c)Z+^.Y-b,\,Z,X-^.X+Z)W*V,X-`.Z/]0Y+['V)[+Y,Y.Y._/Z/Z.X+^([*Y,[._1\,b(^+i.`-b,h-n/x49;=DLQV\bipg||xtokgdddegdb`^_`ZTTUOٻIƪB<5z/o1k3i2b1_4b8\2_,a,c-h-_.T-Y-U0Z3Y1[/Z-[+[,Z.U,Y*X*Z+U*Y*U*[*`,Z.T0X2T.V*S+T,U.[0Z/W/U+W(Q+V.W-W,V/Y2W.X+X*Z*Z)Y(Z.]5\-[&V*]/X.f-m/v29@=:A÷HMSX^foj}}xrlifffca`_^]]]XSSSMHñB<8~4q2d1z`0}\0\0d1^/]-_.e/b.^.[-`-[/^1Y1U1X.X+Y,^-X,]+Y+[+Z,^-Z,X*Z,Z/V.[-V+W)S)S)V+]-W-S-V+[*U*W+V-X/V.Y.V-Y,W*Y)X)Z*[,`.[,Z)[+].c/q0r1x1~6<;:@FILRYaj~n~|unkhhhb\\\\\[ZVRQQLоH²B=;9s4i0{Z/xZ/tR,}Z*],Y.[/]1`/\.[-]-Y.Z/X1X4Y0X,V,X-S-X-T,Y,\.\1].Y+W-W0O,X(U(U)P(V'T(V*U+W,V,X,T*U(U-U2R.U+R,U-T+S)S*W,Z*Y(Y*[-b-d-~i0o4r2v1z489:?DDEͻLT]fzo}ytolid___[WXZWUSQM̼HD@=:6t2}i1s`0iZ,mW(qW*rR-tU-{V-X+Y)Z(b'Y*Z,X.\1Y.Z,X+\*Y*^*W*Z*Z,Z/X-Y*W,\.X+a)Y'X%X'd)X*S+U,_,Y+\+T+U,U/V2R.W)T)W(Q'O'Q(V*T*|R*tW,rZ/p`0sh1wh0zq0}r2w5}7999=ABDINYevp~wpjfbbbZRVZYYURMϾIïFC?<84x3m2u`,q_&nV)nV-mR+qS*uR'wS$zS#|T"~Q&T*T,V.X-U,U)W'Y'X(Q(U(Y+V.S,V*S+U,T+X+V&U!W&X+U,S-O-U-U+S*K-R0V1P3I-P(R&P$~K$zM%wM&uR(sP*pR,mW.mZ1n^3q`5t_1vj-xo3v99~:{98;>@CFIVdrqytolige]UWZYXTPMӿJŴGDA?;86|3s.vo*lb+i_,gX*gR)gP'cQ%_O&bM&fN'nQ)wO)}P*P(P'P'S'U(Y*Q)S)S*R+U)Z(T*T,R+U)R'R$R&Q(P(U)P)Q*P,R.L,R+}P,|N-{K*xN(uL'oN&jL%cM$\N&_V(bQ)dO*fW+i\-m\/o^1rb0sj0tn3{u7y7z7|8:=AAADGTan|s{xusqlh`XYZXWSOMKηH¯ECB?=951v.yd,rc+l]*hW)dM(aO'^L)aL+dJ)eJ(gG'kL&oJ$uK#{I%yN'wR*yO-|J+|M*}P)Q(U'R'R*O-N*L(K(L(P&L%{H%yL%vK&sL'pJ,nG2lB,kI&iK&hJ'fG(fH)fC)bI)_L&^L#]J&^Q)`R(cT(f[(iZ)l[+o].sa0uh3vl4|s5w5y5|8<@DA?BFR^kyr}|xtokd^]\[ZVSRRLȸGDA?=:754w1{n.qi,ia*aX)_U(]S(`P'cM']G'WJ'YO'\K$_J!bJ$_J(\I'aG&gF&fH'fJ&jN%nJ$oI#pI%rH(uG'sE&qE&rC'tG$mJ!fF#cJ&`H&\K&YG'W@)UC'UF%VE&TH(RI'TI'WH%UG$TK$YN%^N'^U)^R(bU'fX)hY+k[-p]0ud1wl2ym2~v2u3y5}8:=@ABFȸKT^hsq}xsniea^]]ZXXYQоIEA?><:::51x.{o,r]+k]*e]'eV$eU%`P&ZL'[M)[L$\L]M$\I*ZD%\E ^A"`C$bE#aF#`>!`B aA!bF#cA#aC$`B%`B'aA"^E[>"YD'WC&UE%SG"SD SE"SE$S>'SG*TL'TL%UM"WIZL#[M'\K([Q*ZO(_S'cW*g[-k^0oa3sg2uj1xi0{o/q2w6y79;=AEƹJPW_fm|q~|xtnhhhda`_[WRMɾHC@>>>==:730u/xe/ma+jY(hX'cX&^O'\N([L'ZN&YL'YI)YH%WK!VG#ZA$^E#XH!RD"RB"SF"RN"RD%PF'OC%OD$OG"ON!PF#OE%OD%N@%ND#OE!QG#QM$QG&SJ(VI&TO%SL%ZG%aL&]P'ZP(XX)WS)\T*aY*f`+l`.nd1qf0tn/xk0yn1{v3z68;>ADHMRY_elwq}{slosleef^VSQKFµB>@BA@>=954z4}l0yg,u[)oZ&jM&eQ'`I*]N-[M*ZL(ZI%XL#VR$XH%[F"YJ WK"TG%RG#RG"S>&QB+PB&OF!OI#OI%PG$QH$RJ%QF&PE$QF#SF$RJ%QI&RJ'TB&UJ%WM(\K,aM*^P(ZO([U(\S*_V-c[+ha*m_,o`/ra.uh.xg1~r456:>AEHLPUZ`ekrzq~}}xruwqjihc^ZUPKF»BBCBA>;::9975t2|j/vc-ma*eZ*aT*]V+\S,\N)YJ&WN&WJ%XL$ZO#\M$WH%RI$SI"TG$RE'QG%PK#OI#PH#QJ$SM%UJ$SI#RH#TI$VJ%SL'QP'QS'RK'VK'[O(^L*bR)^W([U(^\(bX*cW+e^+ij,nb.q^0tf0vm1ys4u89:>CFIJLPTY^bgq{r}}~|~|yz|vpmjhg`ZUPKGǸEDCB>:<?????<}9w3s.wk+rf(ng,i]1dU-cS*bR(_Q&\Q&]P'^R&[L&XN$WO#WP#WM#WM$VL&UJ$VJ"XM$VM'UK#VK XJ"XK%XM'XN*XX(XR'YM([Q)^X(`T(b[(aX)`T)c[)f])g\*i\,me.qb/wh1}l3{58<=>CH»JMMMPTX]`cp}n~~}||}zxtqnkfb]XTPMKFAAA@@?><;:975|2s/q1vh3mc/m\+m[*fY)`U(`P'aQ&`J%_R%\W&ZR$\S"^Q$]O&\N']J'_O'ZQ'VP%ZP#^N$\L&[L']G(_O'_P&`T&aV'bU(bQ*bW+dZ,fZ,h`,j_-l`.ng.qo.ut1}u4569=ADHLMNOORUZ__`kvj|}~~|ytomke`\YURIA˸EȺIŶEA?=:889:<976z5}t0{o,zg,te,n]*k\(hY&fY%dY'bW)_O%`T"aX$`V'`W*_S-^W*_U'aU&aR&aT&_S'\S&aO&eO&dV&b]%dW%fU(fU,gY.j\0mZ0oc0ra1wl3}s0.3877;?EKMPPPQRTV[a_^gpl}~~~zwtrqpmjgd`\VONLIFȽE¶DB@@@?><;:962z2x1|n0uj/oh.li-jc-g]-eZ)dY&dY'd\)eX*aU,]W+eX*mT)iQ)eV*aZ+^V*eT)lV)h_*e\*gV*jZ-kX0ma1pc3tk4wr5{v7}888;?ADDDHLNQQRTWUTY^__hrn~}yuuuuvuurokgb^WPNLKKJǼIHGDA@?>>;987|7|7w6t5m3{m1vd.uf+uZ+s`+qZ+l_+fZ,l\.rT-oY,m\-j_/gZ-l^,rY-rc.r^/ud0xe2|m5r5w6~8;<>@CDFĿLRMIKMORSUX\WRW\^ajtm}}yyywtuvtrnjgd^XUSQONNLJJþIGEEEEEA==<;:9853y2q1~t3wu4pr3sk1wk1vf1un4rn7pq3tr/yr1|v4u6w9}99;=?@CEFGGHLQPOOPRTUVXZXUY^aemum{~~~xsuwvuqnlkfa]ZVSSSPNPRNKLMŻOQJDCB@???=<:8;>9567;?936:>B?=AEEFIMLKKKMPRUTTUWWWXYYY\`ejpwp|}}|ywvvuurpnlkifb]YVTTTUVTRQQRSNIIIGEFG¼FEDCFIEABCDFB>BFGHF½D˿GKJINRSSQPRTVWVUX[ZZ\_^]_bjrw|t}~}|{{{xuuvtrpnprnke_ZVXZZZYYWVVVROOPMKMPONNOQTPMNONNLJNRPNLKNQOMRXZ\XUWYYZXW[_^]aecabdo{~o}}{{|{zz{ywvvsqqqmjge`\]^^^\ZYXY[XVVVVVVVSPQSSTRQRSSSSTSSRQSUROPRTWZ^]\\][XY[\^`bcdfhijt~k~~~{y{}{z}|xyzwurplijlgbbbbc_\[Z]`^]\\_b_]XSUWUTTUVWWX[^YUTTY_VMRWVV[abcbb\W[_^^chfdjppqyl{}||~|xz}{xz|ywvusqqrkefhfdb__`][\^_`bd`]ZXZ\YWTRUXXX[^[XYY\_YTWZ]`_^__ab_\^`abcdfhlprt|ny~}~|vxzyy||yz{zyxxpiknjfdcdf^V[`bdegb]]]_a^[UOTYYY\_]\]___]\\]cjc\\\_cbbaadgdaflnqtxm{}~~wy{zy{}zwvuuvtspmnoliiihga\_cdedd`]]]_b_]YUWZYXZ\]_^]]^]\^`cfb_`aejfccdfhhhlqux{~l}~y{}{zz{xvroqtqnprqpnmnplhebdgffca_^]]`ca`][[\ZXXY^c_[\^]\_cccbbdfkqjdeghilosw{iy~|{yvwyyzxvutssuwvtqmnonmkhfdddba``__`a_]]]^_\Z\^`aa`^]\\`dcbcdddioidgjklpux{~fv~zvsuwz~}}xtvy{}{ysnnnpsqohbbbbbbbbba`][]`ac_\`db`cfa\\]afdbdgechmhdhmnpu{}mz|{zyyz||vxzxwxxurpoopqsmgdbcecaa``a^\]^`b_\^``abccca`acegghfeimkimqsux{}t~|{zxy{vruxwvsponrwqlgcehda`_`b`^]\^a_]\\_ba`ejfca`fljihgjmmnrvx{{{}m{~{|}~|yxxwvwxvuutrqsupkjijkhfeccddda^_a^[_cdeddfhfdcbfjkkjjmpruwy}gw~{~ztw{xux{yxvuwyvtssokmonnmljhgfhjeaaa]Zbkjihhgfffedfiknmmpsx}}}du{|~zwxyxwvvwwvuvwusrqnkmponmmlkkkkkigda^[ahiifdeggghimqqrrsuw{at{{{{{yxxyurtwvuuusrqpnlnqponnnnoqnllmgb_\afhje`diiiknsywvwyz|ds}zxwvvvwwwvvusqqrrsqpppppppnmlligecgkmplhjkmorvz}|~gs}wxzxvxzyxxyvsuxvusrrrrrqponlkklkknqsvsqonquzfu}{}}{|}}~{xz}{zxvuttutsuxsorusqsuy}{xy{}fw|}~}{ywwxwv|zsx~{xxyfu|ftesdsdreq}erethukuis~gq|cq`q}co|}~z|}}|fnvx{yx}~ytx|{z~~z~~u{|w||Zcmoqonpruwtpnlptsrtvtsstuvtrrrvysnsxsnrw{{wxxusw{{{{{zz{}|{|||}||}~~}~z}{{|~|}~}zzzyyz||}~|}xyz}~{zxz}}}{z{}}~|z~z}ummOYdegedcchmhcdehlkkjjklifhjhfedhligkojfilnplhhhijkmnppppqqrolmopqpoopruuvwxxxurstuwwxyzyxxyz{zz{}{zwuuuvxz|{zwtwzzz}}z||z{}|{~|yz|}|zyy}|wxyyzyxz}yvx{}{wxzyyxxwvz~zwwxy{{|yvx{xuuvx{}vmrwxzuqponntzvspmnpqrsuqmpspmllpttusqsuqnjfmuromkotsspmptrqqrrsvzwuttplszutsmhec]XX \ No newline at end of file
diff --git a/2004/i/nono/src/ovision/test_dist.cc b/2004/i/nono/src/ovision/test_dist.cc
new file mode 100644
index 0000000..38ee0a4
--- /dev/null
+++ b/2004/i/nono/src/ovision/test_dist.cc
@@ -0,0 +1,39 @@
+#include "space.h"
+#include <iostream>
+
+int main(int argc, char **argv)
+{
+ const int imgWidth = 360;
+ const int imgHeight = 296;
+
+ if (argc < 2)
+ {
+ std::cout << "testmap renvoie les coordonnees dans le rfrentiel du robot par rapport aux coordones de l'image donne.\n";
+ std::cout << "Usage: testmap <coordonnees x de l'image> <coordonnees y de l'image>\n";
+ return 1;
+ }
+
+ OConfig oconfig ("rc/vision.conf");
+
+ Space space (imgWidth, imgHeight);
+
+ // Entree des 3 points ncessaires la cration des quations
+ space.LoadFromFile();
+// space.AddSetupPoint (356, 23, 300, 300);
+// space.AddSetupPoint (283, 171, 600, 600);
+// space.AddSetupPoint (253, 234, 1000, 900);
+
+ // Calcul des quations pour la conversion des coordonnes
+ space.Setup (0.00603759, 0.593767, 291.474);
+
+ int locImgX = atoi (argv[1]);
+ int locImgY = atoi (argv[2]);
+ std::cout << "Coordonnes de l'image: " << locImgX << " " << locImgY << std::endl;
+
+ int locX, locY;
+ space.GetLoc (locImgX, locImgY, locX, locY);
+
+ std::cout << "Coordonnes de la balle " << locX << " " << locY << std::endl;
+
+ return 0;
+}
diff --git a/2004/i/nono/src/ovision/test_group.cc b/2004/i/nono/src/ovision/test_group.cc
new file mode 100644
index 0000000..a6cbd89
--- /dev/null
+++ b/2004/i/nono/src/ovision/test_group.cc
@@ -0,0 +1,39 @@
+// testimg.cc - Classe Image
+// nono - Programme du robot Efrei Robotique I1-I2 2004
+// Copyright (C) 2004 Olivier Gaillard
+
+#include "segmLearn.h"
+#include "segmTable.h"
+#include "oconfig.h"
+#include "group.h"
+#include "imgInterface.h"
+#include <iostream>
+#include "image/raw_loader.h"
+using namespace std;
+
+int
+main(int argc, char **argv)
+{
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Initialisation des classes
+ OConfig oconfig("rc/vision.conf");
+
+ Img img;
+ RawLoader rawLoader("test.rgb", 360, 296, ImageLoader::rgb);
+ img.load(rawLoader);
+
+ SegmTable segmNN (&img, true);
+ segmNN.BuildNN (oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
+// segmNN.LoadColorTableFile ();
+// segmNN.DoColorTable ();
+// segmNN.CreateColorTableFile ();
+
+ Group group(&img, &segmNN);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Cherche les groupes
+ group.JumpPoints(oconfig.groupColor, oconfig.goalColor);
+ group.ShowZones();
+ ////////////////////////////////////////////////////////////////////////////////////////
+}
+
diff --git a/2004/i/nono/src/ovision/test_map.cc b/2004/i/nono/src/ovision/test_map.cc
new file mode 100644
index 0000000..f515c32
--- /dev/null
+++ b/2004/i/nono/src/ovision/test_map.cc
@@ -0,0 +1,50 @@
+
+#include "img.h"
+#include "image/raw_loader.h"
+#include "video4linux/video4linux.h"
+#include "map.h"
+#include "oconfig.h"
+#include "group.h"
+#include "space.h"
+#include "segmLearn.h"
+//#include "imgFile.h"
+int
+main()
+{
+ OConfig oconfig("rc/vision.conf");
+
+ Img img;
+ RawLoader rawLoader("test.rgb", 360, 296, ImageLoader::rgb);
+ img.load(rawLoader);
+
+
+ /*
+ Video4Linux::ColorSpace cs;
+ cs = Video4Linux::rgb;
+ Video4Linux v4l("/dev/video", cs, 30000);
+ v4l.calibrate ();
+ img.load(v4l);
+ */
+// if ((oconfig.colorMode == YUV) && (!img.yuv)) img.RGBtoYUV();
+// img.WriteRGB("test.rgb");
+
+ Space space(img.width, img.height);
+ space.AddSetupPoint (109, 36, 150, 900);
+ space.AddSetupPoint (84, 102, 150, 600);
+ space.AddSetupPoint (50, 259, 150, 300);
+ space.Setup (0.00603759, 0.593767, 291.474);
+
+ SegmLearn segmNN(&img);
+ segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
+// segmNN.DoColorTable ();
+
+ Group group(&img, &segmNN);
+
+ Map map(&space);
+
+ group.JumpPoints(oconfig.groupColor);
+ group.ShowZones();
+
+ map.AddBallsToMap(&group);
+ map.ShowBalls();
+}
diff --git a/2004/i/nono/src/ovision/test_ovision.cc b/2004/i/nono/src/ovision/test_ovision.cc
index 8e4956b..0272af9 100644
--- a/2004/i/nono/src/ovision/test_ovision.cc
+++ b/2004/i/nono/src/ovision/test_ovision.cc
@@ -1,79 +1,12 @@
-#include <cstdio>
+#include "ovision.h"
+
-#include "video4linux/video4linux.h"
-#include "map.h"
-#include "oconfig.h"
-#include "group.h"
-#include "space.h"
-#include <stdlib.h>
int
main()
{
- /////////////////////////////////////////////////////////////////////////////////////////
- /// Initialisation des classes
- OConfig oconfig("rc/vision.conf");
-
- Img img;
- Video4Linux::ColorSpace cs;
- cs = Video4Linux::rgb;
- Video4Linux v4l("/dev/video", cs, 55000);
- v4l.calibrate ();
-
- Space space(img.width, img.height);
- space.AddSetupPoint (356, 23, 300, 300);
- space.AddSetupPoint (283, 171, 600, 600);
- space.AddSetupPoint (253, 234, 1000, 900);
- space.Setup ();
-
- SegmNN segmNN(&img);
- segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
-
- Group group(&img, &segmNN);
-
- Map map(&space);
- /////////////////////////////////////////////////////////////////////////////////////////
-
-
- /////////////////////////////////////////////////////////////////////////////////////////
- /// Prends une image a partir de la camera et l'analyse
- char filename[30];
- int i = 0;
- while (1)
- {
- std::cout << "-----------------------------------------------------------------" << std::endl;
- std::cout << "image n" << i << std::endl;
- img.load(v4l);
- img.load(v4l);
- sprintf(filename, "test%i.rgb", i);
-// img.WriteRGB(filename);
-// segmNN.Segm();
-
- group.JumpPoints(oconfig.groupColor, oconfig.goalColor);
- group.ShowZones();
- std::cout << "-------------\n" << std::endl;
-
- ZONE *pCur = group.zoneListBall;
-
- int j=0;
- while (pCur)
- {
- int x,y;
- x = group.zoneListBall->centerx;
- y = img.height - group.zoneListBall->centery;
- space.GetLoc(x, y, x, y);
- std::cout << j << ":" << x << " " << y << std::endl;
- pCur = pCur->next;
- }
- std::cout << "-------------\n" << std::endl;
- std::cout << "Map:\n" << std::endl;
- map.AddBallsToMap (&group);
- map.ShowBalls ();
+ OVision ovision;
- sleep(1);
-
- i++;
- }
- /////////////////////////////////////////////////////////////////////////////////////////
+ return 0;
}
diff --git a/2004/i/nono/src/ovision/test_ovision_ogl.cc b/2004/i/nono/src/ovision/test_ovision_ogl.cc
index 2f6dd7e..1036350 100644
--- a/2004/i/nono/src/ovision/test_ovision_ogl.cc
+++ b/2004/i/nono/src/ovision/test_ovision_ogl.cc
@@ -114,9 +114,6 @@ main ()
space.LoadFromFile ();
space.Setup (0.00603759, 0.593767, 291.474);
-
- std::cout << "ok" << std::endl;
-
SegmNN segmNN(&img);
segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
diff --git a/2004/i/nono/src/ovision/testcam.cc b/2004/i/nono/src/ovision/testcam.cc
new file mode 100644
index 0000000..fe33e67
--- /dev/null
+++ b/2004/i/nono/src/ovision/testcam.cc
@@ -0,0 +1,72 @@
+// img.cc - Classe Image
+// nono - Programme du robot Efrei Robotique I1-I2 2004
+// Copyright (C) 2004 Olivier Gaillard
+
+#include "segmNN.h"
+#include "oconfig.h"
+#include "group.h"
+#include "video4linux/video4linux.h"
+#include <iostream>
+using namespace std;
+
+
+
+
+int main(int argc, char **argv) {
+ Img img;
+
+ OConfig config("rc/vision.conf");
+
+
+ if (argv[1]) ; //img.ReadRaw(argv[1]);
+ else
+ {
+ Video4Linux v4l ("/dev/video", ImageLoader::yuv, 40000);
+ v4l.calibrate ();
+ img.load (v4l);
+ }
+
+ img.WriteRGB ("toto.yuv");
+ // img.EdgeDetect();
+ // img.RGBtoHSI();
+ // img.RGBtoYUV();
+
+ SegmNN segmNN(&img, &config);
+ segmNN.BuildNN(config.nn_NbCouleurs, LOAD_FROM_FILE);
+
+ // segmNN.TrainNN();
+ // segmNN.ShowNodes();
+ segmNN.Segm();
+ // config.node = segmNN.node;
+ // config.CreateNNFile("rc/poids", config.colorMode, segmNN.nbOutput);
+ // img.WriteSegm("NN.jpg", segmNN.tabSegm);
+
+
+ // segmNN.CreateThresholds("threshold");
+ // config.LoadThFile();
+ // SegmTh segmTh(&img, &config);
+ // segmTh.Segm();
+ // img.WriteSegm("Th.jpg", segmTh.tabSegm);
+ // segmNN.TestNN();
+
+
+ // Group group(&img, &segmNN);
+ // group.JumpPoints(config.groupColor);
+
+ /* Space space(img.width, img.height);
+ //if (group.zoneList) space.SetDist(group.zoneList[0], 10);
+ space.DoDistTab();
+ space.ShowTab();
+ space.DoIndexTab();
+ // cout << space.GetDist(54) << endl;
+ // cout << space.GetDist(155) << endl;
+ */
+ //Vec vOrig(10, 5);
+ //Vec vDir(1,2);
+ // space.GetPos(&vOrig, &vDir, -0.785);
+
+ // img.WriteSegm("group.jpg",group.tabOut);
+
+
+}
+
diff --git a/2004/i/nono/src/ovision/testsave.cc b/2004/i/nono/src/ovision/testsave.cc
new file mode 100644
index 0000000..8bf5191
--- /dev/null
+++ b/2004/i/nono/src/ovision/testsave.cc
@@ -0,0 +1,59 @@
+
+#include "video4linux/video4linux.h"
+#include "map.h"
+#include "oconfig.h"
+#include "group.h"
+#include "space.h"
+
+int
+main()
+{
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Initialisation des classes
+ OConfig oconfig("rc/vision.conf");
+
+ Img img;
+ Video4Linux::ColorSpace cs;
+ cs = Video4Linux::rgb;
+ Video4Linux v4l("/dev/video", cs, 50000);
+ v4l.calibrate ();
+
+ Space space(img.width, img.height, &oconfig);
+ space.AddSetupPoint (109, 36, -150, 900);
+ space.AddSetupPoint (84, 102, -150, 600);
+ space.AddSetupPoint (50, 259, -150, 300);
+ space.Setup ();
+
+ SegmNN segmNN(&img, &oconfig);
+ segmNN.BuildNN(oconfig.nn_NbCouleurs, LOAD_FROM_FILE);
+
+ Group group(&img, &segmNN);
+
+ Map map(&oconfig, &space);
+ /////////////////////////////////////////////////////////////////////////////////////////
+
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ /// Prends une image a partir de la camera et l'analyse
+ char filename[30];
+ int i = 0;
+ while (1)
+ {
+ std::cout << "-----------------------------------------------------------------" << std::endl;
+ std::cout << "image n" << i << std::endl;
+ img.load(v4l);
+ sprintf(filename, "test%i.rgb", i);
+ img.WriteRGB(filename);
+ segmNN.Segm();
+ group.JumpPoints(oconfig.groupColor);
+ group.ShowZones();
+ std::cout << "-------------\n" << std::endl;
+ map.AddBallsToMap(&group);
+ map.ShowBalls();
+
+ getchar();
+ i++;
+ }
+ /////////////////////////////////////////////////////////////////////////////////////////
+}
+
diff --git a/2004/i/nono/src/ovision/ui.cc b/2004/i/nono/src/ovision/ui.cc
index 4475e08..85015a0 100644
--- a/2004/i/nono/src/ovision/ui.cc
+++ b/2004/i/nono/src/ovision/ui.cc
@@ -12,7 +12,7 @@
#include <unistd.h>
#include <signal.h>
-#include "oconfig.h"
+#include "ovision/oconfig.h"
#include "ui.h"
#include "mainui.h"
@@ -23,7 +23,7 @@ using namespace std;
/// Nombre d'items du menu
-#define NBITEMS 13
+#define NBITEMS 14
OConfig oconfig("rc/vision.conf");
@@ -33,6 +33,7 @@ char *itemsName[NBITEMS][2] = {{"Move color", "Permet d'ajuster les niveaux d'un
{"Merge colors", "Groupe plusieurs couleurs ensembles"},
{"Select color ball", "Selectionne l'index de la couleur des balles"},
{"Select color goal", "Selectionne l'index de la couleur des poteaux"},
+ {"Show objets found", "Affiche les balles et poteaux trouvs"},
{"Training", "Entraine le reseau de neurones"},
{"New network", "Regenere de nouveaux poids pour le reseau"},
{"Set dist point", "Fixe les points pour la distance"},
@@ -728,6 +729,52 @@ UI::GoChangeColor()
delwin(colorWindow);
}
+// Affiche les objets et poteaux trouvs
+void
+UI::ShowObjetsFound ()
+{
+ // Creation de la fenetre
+ WINDOW *winInfo = newwin(15, 60, 5, 4);
+
+ // Affichage du texte
+
+ SendSignal ("f\n");
+
+ char buf[20] = "start";
+ bool end = 1;
+ while ( end != 0 )
+ {
+ read (fifo, buf, 20);
+ end = strcmp ("end\n", buf);
+ if (end != 0) wprintw (winInfo, "%s", buf);
+ }
+
+ wrefresh(winInfo);
+
+ // Liberation de la memoire
+ delwin(winInfo);
+}
+
+void
+UI::NewNN (int nbColor)
+{
+ char buf[20];
+ if (nbColor != -1)
+ {
+ sprintf(buf, "p %i\n", nbColor);
+ SendSignal(buf);
+
+ if (oconfig.node) delete [] oconfig.node;
+ oconfig.node = new unsigned char[nbColor*3];
+
+ oconfig.nn_NbCouleurs = nbColor;
+
+ // Synchronisation des poids avec adjust
+ UpdateNodes();
+
+ PrintStatus("Nouveau reseau de neurones charge\n");
+ }
+}
/// Affiche le menu principale
void
@@ -803,18 +850,18 @@ UI::Menu()
case NBITEMS-2: // Quitte le programme
end=1;
break;
-
+
case 0: // Change les valeurs des poids du NN pour une couleur
GoChangeColor();
break;
-
+
case 1: // Supprime une couleur
GoDelColor();
break;
-
+
case 2: // Melange 2 couleurs
GoMergeWindow();
- break;
+ break;
case 3: // Selectionne la couleur a grouper
GoSelectGroup(BALL);
@@ -824,48 +871,39 @@ UI::Menu()
GoSelectGroup(GOAL);
break;
- case 5: // Entraine le NN
+ case 5: // Affiche les balles et potraux trouvs
+ ShowObjetsFound ();
+ wrefresh (mainWindow);
+ break;
+
+ case 6: // Entraine le NN
sprintf(buf, "t\n");
SendSignal(buf);
// Synchronisation des poids avec adjust
UpdateNodes();
-
+
PrintStatus("Reseau de neurones entraine\n");
break;
- case 6: // Regeneration de poids
+ case 7: // Regeneration de poids
int nbColor;
nbColor = ChooseColor(NB_COULEUR, oconfig.nn_NbCouleurs);
- if (nbColor != -1)
- {
- sprintf(buf, "p %i\n", nbColor);
- SendSignal(buf);
-
- if (oconfig.node) delete [] oconfig.node;
- oconfig.node = new unsigned char[nbColor*3];
-
- oconfig.nn_NbCouleurs = nbColor;
-
- // Synchronisation des poids avec adjust
- UpdateNodes();
-
- PrintStatus("Nouveau reseau de neurones charge\n");
- }
+ NewNN (nbColor);
break;
-
- case 7: // Set dist point
+
+ case 8: // Set dist point
SendSignal("v\n");
break;
- case 8: // Change mode rgb/yuv
+ case 9: // Change mode rgb/yuv
SendSignal("y\n");
if (oconfig.colorMode == RGB)
oconfig.colorMode = YUV;
else oconfig.colorMode = RGB;
break;
-
- case 9: // Annuler les changements
+
+ case 10:// Annuler les changements
// Reload du fichier de poids initial
oconfig.LoadNNFile();
@@ -877,7 +915,7 @@ UI::Menu()
PrintStatus("Les changements ont ete annules\n");
break;
- case 10: // Sauver les changements
+ case 11: // Sauver les changements
// Sauvegarde des poids dans le fichier poids
oconfig.CreateNNFile("rc/poids", oconfig.colorMode, oconfig.nn_NbCouleurs);
@@ -908,7 +946,11 @@ UI::Menu()
break;
case 'g':
- SendSignal("z\n");
+ ShowObjetsFound ();
+ break;
+
+ case 'n':
+ NewNN (oconfig.nn_NbCouleurs);
break;
}
diff --git a/2004/i/nono/src/ovision/ui.h b/2004/i/nono/src/ovision/ui.h
index e735368..52071e4 100644
--- a/2004/i/nono/src/ovision/ui.h
+++ b/2004/i/nono/src/ovision/ui.h
@@ -1,11 +1,29 @@
-#ifndef UI_h
-#define UI_h
-// UI.h - classe User Interface
-// nono - Programme du robot Efrei Robotique I1-I2 2004
+// nono2 - programme du robot 2005
+//
// Copyright (C) 2004 Olivier Gaillard
+//
+// Robot APB Team/Efrei 2004.
+// Web: http://assos.efrei.fr/robot/
+// Email: robot AT efrei DOT fr
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#ifndef UI_h
+#define UI_h
-// Realise a l'aide du "NCURSES Programming HOWTO"
+// Realis a l'aide du "NCURSES Programming HOWTO"
#include "menu.h"
@@ -86,6 +104,11 @@ class UI {
/// Envoie une donnee au prog adjust
void SendSignal(char *buf);
+
+ // Affiche les objets et poteaux trouvs
+ void ShowObjetsFound ();
+
+ void NewNN (int nbColor);
};