Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# -*- coding: utf-8 -*-
2"""
3@file
4@brief Ce module determine si un segment est significatif, c'est à dire
5si le nombre de fausses alarmes n'est pas trop élevé.
6"""
7from .geometrie import Segment
10class SegmentNFA(Segment):
11 """
12 Un segment + un nombre de fausses alarmes,
13 servira a memoriser les segments significatifs.
14 """
16 # voir la classe Point pour __slots__
17 __slots__ = ("nfa",)
19 def __init__(self, p1, p2, nfa):
20 """segment + nombre de fausses alarmes"""
21 Segment.__init__(self, p1, p2)
22 self.nfa = nfa
24 def __str__(self):
25 """permet d'afficher ce segment"""
26 s = Segment.__str__(self)
27 s += " nfa = " + str(self.nfa)
28 return s
30 def __lt__(self, o):
31 return self.nfa < o.nfa
34class InformationPoint:
35 """
36 Pour retenir toutes les informations relatives a un segment,
37 une position (pos),
38 la norme du gradient (norme),
39 une information permettant de savoir si le gradient est
40 proche du vecteur normal au segment (aligne)"""
42 # voir la classe Point pour __slots__
43 __slots__ = "pos", "aligne", "norme"
45 def __init__(self, pos, aligne, norme):
46 """constructeur, initialisation"""
47 self.pos, self.aligne, self.norme = pos, aligne, norme
49 def __str__(self):
50 """permet d'afficher cette classe"""
51 s = "aligne " + str(self.aligne)
52 s += " pix " + str(self.pos)
53 s += " gnor " + str(self.norme)
54 return s
57class LigneGradient:
58 """
59 Stocke toutes les informations relatives à un segment de l'image
60 reliant deux points du contour, reçoit les informations
61 de la methode @see me decoupe_gradient.
63 A partir de là, un segment significatif a deux extrémités
64 dont le gradient est dans le bon sens, on parcourt donc
65 tous les couples d'extrémités possibles,
66 d'abord la première (méthode @see me premier_chemin),
67 puis les suivant (méthode @see me next_chemin)
68 jusqu'au dernier couple.
69 """
71 def __init__(self, info_ligne, seuil_norme, seuil_nfa):
72 """constructeur"""
73 self.info_ligne = info_ligne # informations
74 self.nb = len(info_ligne) # nombre de pixels
75 self.seuil_norme = seuil_norme
76 self.seuil_nfa = seuil_nfa
78 def __len__(self):
79 """
80 Retourne le nombre de pixels dans le segment,
81 peut etre different de la liste ``self.info_ligne``,
82 ``self.nb`` est déterminé par @see me decoupe_gradient.
83 """
84 return self.nb
86 def has_aligned_point(self):
87 """
88 Dit s'il existe des points alignés sur le segment.
89 """
90 return any(filter(lambda _: _.aligne, self.info_ligne))
92 def extremite(self):
93 """
94 Comptabilise les indices des extremites possibles,
95 les pixels choisis ont un gradient de la bonne orientation.
96 """
97 ext = []
98 if self.has_aligned_point():
99 for i in range(0, len(self)):
100 if self.info_ligne[i].aligne and \
101 (i == 0 or i == len(self) - 1 or not self.info_ligne[i - 1].aligne or
102 not self.info_ligne[i + 1].aligne):
103 ext.append(i)
104 return ext
106 def premier_chemin(self, ext):
107 """Retourne la premiere d'extremite possible."""
108 return (0, 1)
110 def next_chemin(self, ext, ij):
111 """Retourne le couple suivant d'extrémités possibles,
112 None, dans le cas contraire."""
113 if ij[1] < len(ext) - 1:
114 return (ij[0], ij[1] + 1)
115 elif ij[0] < len(ext) - 2:
116 return (ij[0] + 1, ij[0] + 2)
117 else:
118 return None
120 def calcule_NFA(self, ext, ij, binomiale, nb_seg):
121 """
122 ``ext[ij[0]]``: premier indice du segment,
123 ``ext[ij[1]]``: dernier indice du segment,
124 calcule le nombre de NFA de ce segment
125 (nombre de fausses alarmes).
126 """
127 ln = 0
128 n = 0
129 for i in range(ext[ij[0]], ext[ij[1]] + 1):
130 if self.info_ligne[i].norme < self.seuil_norme:
131 # on evite les petits gradients
132 continue
133 ln += 1
134 if self.info_ligne[i].aligne:
135 # on calcule un gradient dans le bon sens
136 n += 1
138 # on determine ensuite la probabilite d'un tel agencement
139 # de fausses alarmes
140 nfa = binomiale[(ln, n)]
141 nfa *= nb_seg
142 return nfa
144 def segments_significatifs(self, binomiale, nb_seg):
145 """
146 Comptabilise le nombre de segments significatifs sur une ligne
147 et les mémorise.
148 """
150 # on recense les extrémités possibles
151 ext = self.extremite()
153 if len(ext) < 2:
154 # s'il n'y a qu'une extrémité possible,
155 # ce n'est pas suffisant pour faire un segment
156 return []
158 # premier couple d'extrémités
159 ij = self.premier_chemin(ext)
160 res = [] # pour memoriser les segments significatifs
162 while ij is not None: # tant qu'il reste un couple d'extremite
163 # probabilite de fausses alarmes pour ce segment
164 nfa = self.calcule_NFA(ext, ij, binomiale, nb_seg)
166 if nfa < self.seuil_nfa:
167 # si cette proba est suffisamment faible,
168 # l'agencement est un cas rare (non aleatoire),
169 # il est significatif
170 seg = SegmentNFA(self.info_ligne[ext[ij[0]]].pos,
171 self.info_ligne[ext[ij[1]]].pos,
172 nfa)
173 # on l'ajoute a la liste
174 res.append(seg)
176 # on passe au segment suivant
177 ij = self.next_chemin(ext, ij)
179 # fin
180 return res