Hide keyboard shortcuts

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 

8 

9 

10class SegmentNFA(Segment): 

11 """ 

12 Un segment + un nombre de fausses alarmes, 

13 servira a memoriser les segments significatifs. 

14 """ 

15 

16 # voir la classe Point pour __slots__ 

17 __slots__ = ("nfa",) 

18 

19 def __init__(self, p1, p2, nfa): 

20 """segment + nombre de fausses alarmes""" 

21 Segment.__init__(self, p1, p2) 

22 self.nfa = nfa 

23 

24 def __str__(self): 

25 """permet d'afficher ce segment""" 

26 s = Segment.__str__(self) 

27 s += " nfa = " + str(self.nfa) 

28 return s 

29 

30 def __lt__(self, o): 

31 return self.nfa < o.nfa 

32 

33 

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)""" 

41 

42 # voir la classe Point pour __slots__ 

43 __slots__ = "pos", "aligne", "norme" 

44 

45 def __init__(self, pos, aligne, norme): 

46 """constructeur, initialisation""" 

47 self.pos, self.aligne, self.norme = pos, aligne, norme 

48 

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 

55 

56 

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. 

62 

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 """ 

70 

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 

77 

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 

85 

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)) 

91 

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 

105 

106 def premier_chemin(self, ext): 

107 """Retourne la premiere d'extremite possible.""" 

108 return (0, 1) 

109 

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 

119 

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 

137 

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 

143 

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 """ 

149 

150 # on recense les extrémités possibles 

151 ext = self.extremite() 

152 

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 [] 

157 

158 # premier couple d'extrémités 

159 ij = self.premier_chemin(ext) 

160 res = [] # pour memoriser les segments significatifs 

161 

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) 

165 

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) 

175 

176 # on passe au segment suivant 

177 ij = self.next_chemin(ext, ij) 

178 

179 # fin 

180 return res