/* contours-tp.c - Edouard Thiel - 29/05/2001 Compilation : cc bsutil.c contours-tp.c -o contours-tp \ `~/helium/helium-cfg --cflags --libs` -lm Exécution : contours-tp image.pgm */ #include #include #include "bsutil.h" He_node *princ, *canvas, *panel, *text1, *text2, *tog1, *tog2; int c_gris, c_rouge, c_vert, c_bleu, c_jaune, c_cyan, c_magenta; BsMap *bm_orig = NULL, *bm_res1 = NULL, *bm_res2 = NULL; enum { AFFI_ORIGINAL, AFFI_POINT_CONTOUR, AFFI_SUIVI_CONTOUR, AFFI_APPROX_POLYG, AFFI_RELAXATION, AFFI_TRACE_POLYG, AFFI_VORONOI_DISCRET }; int etat_affi = AFFI_ORIGINAL; enum { DIST_4, DIST_8, DIST_34, DIST_5711 }; int etat_dist = DIST_34; #define POLYG_VECMAX 20000 int polyg_x[POLYG_VECMAX], polyg_y[POLYG_VECMAX], polyg_d[POLYG_VECMAX], polyg_n = 0; #define CHA_MAX 100 int cha_x[CHA_MAX], cha_y[CHA_MAX], cha_w[CHA_MAX], cha_n = 0; /*--------------------------- A F F I C H A G E ------------------------------*/ #define GROS_POINT 3 #define MIL_POINT (GROS_POINT/2) #define MAX_WIDTH 1000 #define MAX_HEIGHT 700 void init_couleurs () { c_gris = HeAllocRgb (150, 150, 150, he_white); c_rouge = HeAllocRgb (255, 0, 0, he_black); c_vert = HeAllocRgb (0, 255, 0, he_black); c_bleu = HeAllocRgb (0, 0, 255, he_black); c_jaune = HeAllocRgb (255, 255, 0, he_black); c_cyan = HeAllocRgb (0, 255, 255, he_black); c_magenta = HeAllocRgb (255, 0, 255, he_black); } /* * Dessin d'un gros point */ void dessin_point (Window win, int x, int y, int coul) { XSetForeground (he_display, he_gc, coul); XFillRectangle (he_display, win, he_gc, x * GROS_POINT, y * GROS_POINT, GROS_POINT, GROS_POINT); } /* * Dessin d'un trait fin dont les extrémités sont centrées * sur les gros points. */ void dessin_trait (Window win, int x1, int y1, int x2, int y2, int coul) { XSetForeground (he_display, he_gc, coul); XDrawLine (he_display, win, he_gc, x1 * GROS_POINT + MIL_POINT, y1 * GROS_POINT + MIL_POINT, x2 * GROS_POINT + MIL_POINT, y2 * GROS_POINT + MIL_POINT); } /* Affichage point par point en noir/gris : * 0 : noir, >0 : gris * (pas aussi rapide que par une XImage, mais suffisant ici) */ void dessin_image_binaire (Window win, BsMap *bm) { int x, y; for (y = 0; y < bm->ysiz; y++) for (x = 0; x < bm->xsiz; x++) dessin_point (win, x, y, bm->tab[y][x] == 0 ? he_black : c_gris); } /* Affichage point par point en noir/gris/couleurs (modulo 6): * 0 noir, 1 gris, 2 bleu, 3 jaune, 4 cyan, 5 magenta, 6 rouge, 7 vert, ... */ void dessin_image_couleur (Window win, BsMap *bm) { int x, y, v, v6, c; for (y = 0; y < bm->ysiz; y++) for (x = 0; x < bm->xsiz; x++) { v = bm->tab[y][x]; v6 = v % 6; c = v == 0 ? he_black : v == 1 ? c_gris : v6 == 0 ? c_rouge : v6 == 1 ? c_vert : v6 == 2 ? c_bleu : v6 == 3 ? c_jaune : v6 == 4 ? c_cyan : v6 == 5 ? c_magenta : he_white; dessin_point (win, x, y, c); } } /* * Dessine la polygonalisation avec des traits fins en alternance vert/rouge. * On relie les sommets tels que polyg_d[] > 0 ; on ne relie pas les sommets * tels que polyg_d[] sont != , pour séparer les différents contours. */ void dessin_traits_polyg (Window win) { int i, k = -1, c = c_vert; for (i = 0; i < polyg_n; i++) { if (polyg_d[i] == 0) continue; if (polyg_d[k] == polyg_d[i]) dessin_trait (win, polyg_x[k], polyg_y[k], polyg_x[i], polyg_y[i], c); k = i; c = c == c_vert ? c_rouge : c_vert; } } /*-------------------------- A L G O R I T H M E S ---------------------------*/ /* Directions de Freeman, en 3 2 1 tournant dans le sens inverse 4 p 0 des aiguilles d'une montre. 5 6 7 */ /* A COMPLETER */ /* * Met le fond à 0, la forme à 1, le contour à 2 * on considère la connexité 8 pour la forme et 4 pour le fond. * Les images ont un bord extérieur déjà initialisé à 0. */ void calcul_points_contour (BsMap *d, BsMap *r) { int x, y; for (y = 0; y < d->ysiz; y++) for (x = 0; x < d->xsiz; x++) { if (d->tab[y][x] == 0) r->tab[y][x] = 0; else { /* A COMPLETER */ /* n'importe quoi juste pour avoir qq chose */ r->tab[y][x] = (d->tab[y][x-1] == 0) ? 2 : 1; } } } /* Suivi d'un contour à partir de xP,yP et de la direction fP ; * on mémorise le contour dans polyg_x,y et on étiquète à nc * dans polyg_d et l'image r */ void Suivi1C (BsMap *r, int xP, int yP, int fP, int nc) { /* A COMPLETER */ } /* * Met le fond à 0, la forme à 1, le 1er contour à 2, le suivant à 3, etc * Stocke les points du contour dans polyg_x,y[] * Stocke le numero du contour dans polyg_d[] (2, 3, ...) * * Les images ont un bord extérieur déjà initialisé à 0. * * Restriction : pour que le contour d'un trou soit détecté, il doit posséder * au moins un point qui n'appartient pas au contour extérieur. */ void calcul_suivi_contour (BsMap *d, BsMap *r) { int x, y; /* init */ for (y = 0; y < d->ysiz; y++) for (x = 0; x < d->xsiz; x++) r->tab[y][x] = d->tab[y][x] == 0 ? 0 : 1; for (y = 0; y < d->ysiz; y++) for (x = 0; x < d->xsiz; x++) if (r->tab[y][x] == 1) { /* A COMPLETER */ /* n'importe quoi juste pour avoir qq chose */ r->tab[y][x] = 2; } } /* * Renvoie la distance euclidienne du point C à la droite (AB) */ double dist_point_droite (int xC, int yC, int xA, int yA, int xB, int yB) { /* A COMPLETER */ return 0; } /* * Renvoie l'indice i, i compris entre a et b, du point le plus éloigné de la * droite passant par les points d'indice a et b. */ int point_plus_distant (int a, int b) { /* A COMPLETER */ return 0; } /* * Découpage polygonal récursif de a..b avec le seuil s */ void decoupe_rec (int a, int b, double s) { /* A COMPLETER */ } /* * Calcule l'approximation polygonale, à partir de polyg_x,y[] * Cela consiste à mettre les points non retenus comme sommets à 0 * dans polyg_d[] */ void calcul_approx_polyg (double s) { /* A COMPLETER */ /* n'importe quoi juste pour avoir qq chose */ polyg_x[0] = 63; polyg_y[0] = 15; polyg_d[0] = 2; polyg_x[1] = 58; polyg_y[1] = 13; polyg_d[1] = 2; polyg_x[2] = 50; polyg_y[2] = 11; polyg_d[2] = 0; polyg_x[3] = 40; polyg_y[3] = 12; polyg_d[3] = 2; polyg_x[4] = 35; polyg_y[4] = 12; polyg_d[4] = 2; polyg_n = 5; } /* * Fait la relaxation sur l'approximation polygonale : * cela consiste à mettre à 0 certains sommets jugés inutiles dans polyg_d[] */ void calcul_relax_polyg (double s) { /* A COMPLETER */ } /* * Colorie les points mémorisés dans polyg_x,y[] en changeant de * couleur chaque fois que polyg_d[] != 0 */ void calcul_trace_polyg (BsMap *r) { /* A COMPLETER */ } /* * Affecte les poids du masque de chanfrein */ void fixe_masque_chanfrein () { cha_n = 0; switch (etat_dist) { case DIST_4 : cha_x[cha_n] = 1; cha_y[cha_n] = 0; cha_w[cha_n++] = 1; cha_x[cha_n] = 0; cha_y[cha_n] = 1; cha_w[cha_n++] = 1; break; case DIST_8 : /* A COMPLETER */ break; case DIST_34 : break; case DIST_5711 : break; } } /* * Calcule le Diagramme de Voronoi généralisé discret avec * une distance de chanfrein sur l'image de l'approximation polygonale. * * Les images ont un bord extérieur suffisant, déjà initialisé à 0. * * vor = image des contours (fond à 0, intérieur = 1, contours > 1) * dans laquelle on fait le Voronoi Discret * idt = image dans laquelle on fait la transformation de distances */ void calcul_voronoi_discret (BsMap *vor, BsMap *idt) { /* A COMPLETER */ } /* * On recalcule tout à chaque changement d'état : ça évite de devoir * cliquer sur les boutons radio dans un certain ordre. */ void applique_filtres () { double seuil = atof(HeGetTextValue(text2)); /* Sécurité */ if (bm_orig == NULL) return; InitExterBsMap (bm_orig, 0); switch (etat_affi) { case AFFI_POINT_CONTOUR : { calcul_points_contour (bm_orig, bm_res1); } break; case AFFI_SUIVI_CONTOUR : { calcul_suivi_contour (bm_orig, bm_res1); } break; case AFFI_APPROX_POLYG : { calcul_suivi_contour (bm_orig, bm_res1); calcul_approx_polyg (seuil); } break; case AFFI_RELAXATION : { calcul_suivi_contour (bm_orig, bm_res1); calcul_approx_polyg (seuil); calcul_relax_polyg (seuil); } break; case AFFI_TRACE_POLYG : { calcul_suivi_contour (bm_orig, bm_res1); calcul_approx_polyg (seuil); calcul_relax_polyg (seuil); calcul_trace_polyg (bm_res1); } break; case AFFI_VORONOI_DISCRET : { calcul_suivi_contour (bm_orig, bm_res1); calcul_approx_polyg (seuil); calcul_relax_polyg (seuil); calcul_trace_polyg (bm_res1); fixe_masque_chanfrein (); calcul_voronoi_discret (bm_res1, bm_res2); } break; } /* MAJ affichage */ HePostRepaint (canvas); } /*--------------------------- C A L L B A C K S ------------------------------*/ void tog1_proc (He_node *hn) { char *nom = HeGetToggleLabel (hn); if (HeGetToggleValue (hn) == FALSE) return; if (strcmp (nom, "Original") == 0) etat_affi = AFFI_ORIGINAL; else if (strcmp (nom, "Points contour") == 0) etat_affi = AFFI_POINT_CONTOUR; else if (strcmp (nom, "Suivi contour") == 0) etat_affi = AFFI_SUIVI_CONTOUR; else if (strcmp (nom, "Approx polygonale") == 0) etat_affi = AFFI_APPROX_POLYG; else if (strcmp (nom, "Relaxation") == 0) etat_affi = AFFI_RELAXATION; else if (strcmp (nom, "Tracé") == 0) etat_affi = AFFI_TRACE_POLYG; else if (strcmp (nom, "Voronoi discret") == 0) etat_affi = AFFI_VORONOI_DISCRET; applique_filtres (); } void tog2_proc (He_node *hn) { char *nom = HeGetToggleLabel (hn); if (HeGetToggleValue (hn) == FALSE) return; if (strcmp (nom, "d4") == 0) etat_dist = DIST_4; else if (strcmp (nom, "d8") == 0) etat_dist = DIST_8; else if (strcmp (nom, "d3,4") == 0) etat_dist = DIST_34; else if (strcmp (nom, "d5,7,11") == 0) etat_dist = DIST_5711; applique_filtres (); } void canvas_repaint (He_node *hn, Window win) { if (bm_orig == NULL) return; switch (etat_affi) { case AFFI_ORIGINAL : { dessin_image_binaire (win, bm_orig); } break; case AFFI_POINT_CONTOUR : { dessin_image_couleur (win, bm_res1); } break; case AFFI_SUIVI_CONTOUR : { dessin_image_couleur (win, bm_res1); } break; case AFFI_APPROX_POLYG : { dessin_image_binaire (win, bm_orig); dessin_traits_polyg (win); } break; case AFFI_RELAXATION : { dessin_image_binaire (win, bm_orig); dessin_traits_polyg (win); } break; case AFFI_TRACE_POLYG : { dessin_image_couleur (win, bm_res1); } break; case AFFI_VORONOI_DISCRET : { dessin_image_couleur (win, bm_res1); } break; } } void quit_proc (He_node *hn) { HeQuit(0); } void lire_proc (He_node *hn) { char *nomf = HeGetTextValue (text1); int k; /* Destruction anciennes données */ FreeBsMap (bm_orig); bm_orig = NULL; FreeBsMap (bm_res1); bm_res1 = NULL; FreeBsMap (bm_res2); bm_res2 = NULL; /* Lecture */ bm_orig = ReadBsMapFromPGM (nomf, 2); /* Test réussite */ if (bm_orig == NULL) { HeSimpleDialog ( HE_DIALOG_BELL, HE_DIALOG_TITLE, "Attention", HE_DIALOG_MESSAGE, "Erreur de lecture du fichier", HE_DIALOG_QUOTED, nomf, HE_DIALOG_BUTTON, "OK", 0); return; } /* Création de bm_res1 et bm_res2 à la taille de bm_orig */ bm_res1 = NewBsMap (bm_orig->ysiz, bm_orig->xsiz, 2); bm_res2 = NewBsMap (bm_orig->ysiz, bm_orig->xsiz, 2); if (bm_res1 == NULL || bm_res2 == NULL) HeQuit(1); /* Taille canvas et fenêtre */ k = bm_orig->xsiz * GROS_POINT; if (k > MAX_WIDTH) k = MAX_WIDTH; HeSetWidth (canvas, k); k = bm_orig->ysiz * GROS_POINT; if (k > MAX_HEIGHT) k = MAX_HEIGHT; HeSetHeight (canvas, k); HeSetX (canvas, 0); HeFit (princ); HeSetX (canvas, (HeGetWidth(princ) - HeGetWidth(canvas)-2)/2); applique_filtres (); } void text2_proc (He_node *hn) { applique_filtres (); } /*--------------------------------- M A I N ----------------------------------*/ int main (int argc, char **argv) { int code_sortie; /* Init Helium, ouverture display X11, décodage arguments */ HeInit (&argc, &argv); /* Les arguments qui restent n'ont pas été reconnus par Helium */ if (argc != 2){ printf ("USAGE : %s image.pgm\n", argv[0]); HeQuit(1); } /* Création widgets */ princ = HeCreateFrame (); HeSetFrameLabel (princ, "Contours"); panel = HeCreatePanel (princ); HeCreateMessageP (panel, "Image binaire :", TRUE); text1 = HeCreateText (panel); HeSetTextVisibleLen (text1, 40); HeSetTextValue (text1, argv[1]); HeSetTextCompletion (text1, TRUE); HeSetTextNotifyProc (text1, lire_proc); HeCreateButtonP (panel, "Lire", lire_proc, NULL); HeCreateButtonP (panel, "Quit", quit_proc, NULL); HeSetPanelLayout (panel, HE_LINE_FEED); HeCreateMessageP (panel, "Afficher :", TRUE); tog1 = HeCreateToggleRadioP (panel, "Original", tog1_proc, NULL); HeCreateToggleRadioP (panel, "Points contour", tog1_proc, tog1); HeCreateToggleRadioP (panel, "Suivi contour", tog1_proc, tog1); HeSetPanelLayout (panel, HE_LINE_FEED); HeCreateToggleRadioP (panel, "Approx polygonale", tog1_proc, tog1); HeCreateToggleRadioP (panel, "Relaxation", tog1_proc, tog1); HeCreateToggleRadioP (panel, "Tracé", tog1_proc, tog1); HeCreateToggleRadioP (panel, "Voronoi discret", tog1_proc, tog1); HeSetPanelLayout (panel, HE_LINE_FEED); HeCreateMessageP (panel, "Seuil :", TRUE); text2 = HeCreateText (panel); HeSetTextVisibleLen (text2, 8); HeSetTextValue (text2, "1.0"); HeSetTextNotifyProc (text2, text2_proc); HeCreateMessageP (panel, " Distance :", TRUE); tog2 = HeCreateToggleRadioP (panel, "d4", tog2_proc, NULL); HeCreateToggleRadioP (panel, "d8", tog2_proc, tog2); HeCreateToggleRadioP (panel, "d3,4", tog2_proc, tog2); HeCreateToggleRadioP (panel, "d5,7,11", tog2_proc, tog2); HeFit (panel); canvas = HeCreateCanvas (princ); HeSetCanvasRepaintProc (canvas, canvas_repaint); HeSetY (canvas, HeGetHeight(panel) + 5); HeFit (princ); /* Lecture et affichage */ init_couleurs (); lire_proc (NULL); /* Boucle d'évènements */ code_sortie = HeMainLoop (princ); /* Le display X11 est maintenant fermé ; on libère la mémoire */ FreeBsMap (bm_orig); FreeBsMap (bm_res1); FreeBsMap (bm_res2); return code_sortie; }