Canny.cpp

1  /***********************************************************************************  
2      ImAnalyse : software in image processing and image analysis 
3     
4      Copyright (C) 10 juillet 2008  <Vincent MORARD> 
5    Version: 2.1 
6      Contact: vincent<POINT>morard<AROBAS>cpe<POINT>fr 
7    Website: http://ImAnalyse.free.fr 
8    Website: http://pistol.petesampras.free.fr 
9   
10      This program is free software: you can redistribute it and/or modify 
11      it under the terms of the GNU General Public License as published by 
12      the Free Software Foundation, either version 3 of the License, or 
13      (at your option) any later version. 
14   
15      This program is distributed in the hope that it will be useful, 
16      but WITHOUT ANY WARRANTY; without even the implied warranty of 
17      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
18      GNU General Public License for more details. 
19   
20      You should have received a copy of the GNU General Public License 
21      along with this program.  If not, see <http://www.gnu.org/licenses/ 
22  **********************************************************************************/ 
23   
24  #include "../CImage.h" 
25  #include "../AdvancEdge.h" 
26   
27   
28  //**************************************************************************** 
29  //Canny.cpp 
30  //Ce fichier regroupe les fonctions permettant de calculer la détection optimale  
31  //des contours suivant la méthode instaurée par Canny. 
32  //Le principe est le suivant:  
33  //On applique à l'image source, un filtre passe bas, gaussien d'écart type s. 
34  //Le noyau de convolution sera "Gaussien" et le résultat de la convolution sera 
35  //FlouX et FlouY. En effet le noyau est à 1 dimension donc on peut l'appliquer 
36  //suivant les x et suivant les y. 
37  // 
38  //On crée un second noyau qui sera cette fois-ci la dérivée d'une gaussienne :  
39  //DGaussien 
40  //Pour repérer les contours, on applique à l'image FlouX et FlouY le noyau  
41  //DGaussien 
42  //on obtient alors les images dX et dY. 
43  //On supprime alors les maxima de la norme des images dX et dY pour obtenir  
44  //notre image finale. 
45  // 
46  //MAX_SIZE_MASK. On n'autorise pas à avoir un masque de dimension supérieur à  
47  //MAX_SIZE_MASK en effet plus sigma (s) est grand, plus le masque de convolution  
48  //sera grand. 
49  //****************************************************************************** 
50   
51   
52   
53  #define MAX_SIZE_MASK      20 
54  #define MAG_SCALE    1 
55  #define ORI_SCALE    1 
56   
57   
58  //************************************************************************* 
59  //Canny:  
60  //Cette fonction comporte 3 paramètres: 
61  //ImgNorme : il s'agit de l'image destination: l'image des contours 
62  //ImgOrientation : Il s'agit de la direction du gradient.(Peu utilisable) atan(dY/dX) 
63  //s : il s'agit de l'écart type de l'image. Plus s sera grand, plus le  
64  //filtrage sera fort et moins il y aura de détails. Au contraire si s est  
65  //faible, le filtrage sera faible et on observera plus de contours. 
66  //************************************************************************* 
67  bool CImage::Canny(CImage *ImgNorme,CImage *ImgOrientation,float s) 
68  { 
69    float Gaussien[MAX_SIZE_MASK],dGaussien[MAX_SIZE_MASK]; 
70    int i; 
71    float **dX=0,**dY=0,**FlouX=0,**FlouY=0; 
72    int taille=0; 
73   
74    if(hBmp==0){ 
75      MessageBox(NULL,"Canny : L'image source est vide", 
76        NULL,MB_OK|MB_ICONWARNING); 
77      return 0; 
78    } 
79   
80    if(ImgNorme != 0 && ImgNorme != this) 
81      ImgNorme->Copy(this); 
82    if(ImgOrientation != 0 && ImgOrientation != this) 
83      ImgOrientation->Copy(this); 
84   
85    if(ImgNorme == 0) 
86      ImgNorme=this; 
87   
88   
89    //Recuperation des pixels 
90    GetBitmapBits(hBmp,Width*Height*4,ucBits); 
91   
92   
93   
94   
95    CImage ImgTmpNorme,ImgTmpOrientation; 
96   
97    ImgTmpNorme.Copy(this); 
98   
99    if(ImgOrientation !=0) 
100      ImgTmpOrientation.Copy(this); 
101   
102    //Creation du masque gaussian 
103    for(i=0;i<MAX_SIZE_MASK;i++) 
104    { 
105      Gaussien[i]=MeanGauss((float)i,s); 
106      dGaussien[i]=dGauss((float)i,s); 
107      if(Gaussien[i]<0.005) 
108      { 
109        taille=i; 
110        break; 
111      } 
112    } 
113   
114    //Allocation pour les images floutees. On garde les chiffres apres la virgule  
115    FlouX=AllocT_float(Width,Height); 
116    FlouY=AllocT_float(Width,Height); 
117   
118    //On applique le filtre gaussien à l'image pour supprimer le bruit 
119    //on effectue 1 convolution 2D qui nous permettra de trouver la composante X et Y 
120    this->SeparableConvolution(Gaussien,taille,FlouX,FlouY); 
121   
122    //On effectue maintenant la convolution avec la derivé d'un filtre gaussien sur 
123    //l'image précédement obtenue 
124    dX=AllocT_float(Width,Height); 
125    dY=AllocT_float(Width,Height); 
126    this->SeparableConvolution_dxy(FlouX,dGaussien,taille,dX,TRUE); 
127    this->SeparableConvolution_dxy(FlouY,dGaussien,taille,dY,FALSE); 
128   
129    //deallocation de la mémoire 
130    DesAllocT_float(FlouX,Width); 
131    DesAllocT_float(FlouY,Width); 
132   
133   
134   
135   
136    //Suppression des non maxima : on ne garde que les maxima locaux 
137    this->NonMaxSuppress(dX,dY,&ImgTmpNorme,&ImgTmpOrientation); 
138   
139    ImgTmpNorme.ContrasteAuto(GRAY,ImgNorme); 
140   
141    if(ImgOrientation !=0) 
142    { 
143      ImgTmpOrientation.ContrasteAuto(GRAY,ImgOrientation); 
144      SetBitmapBits(ImgOrientation->hBmp,Width*Height*4,ImgOrientation->ucBits); 
145      ImgOrientation->ImgType=GRAY; 
146    } 
147    SetBitmapBits(ImgNorme->hBmp,Width*Height*4,ImgNorme->ucBits); 
148   
149    ImgNorme->ImgType=GRAY; 
150   
151   
152    DesAllocT_float(dX,Width); 
153    DesAllocT_float(dY,Width); 
154    return 1; 
155   
156  } 
157   
158  //**************************************************************************** 
159  //Norm: 
160  //Calcul de la norme d'un vecteur  
161  //**************************************************************************** 
162  float Norm(float x,float y) 
163  { 
164    return (float) sqrt((double)(x*x+y*y)); 
165  } 
166   
167  //**************************************************************************** 
168  //Gauss: 
169  //on calcule la valeur en x de la fonction de gauss 
170  //**************************************************************************** 
171  float Gauss(float x,float Sigma) 
172  { 
173    return (float)exp((double)((-x*x)/(2*Sigma*Sigma))); 
174  } 
175   
176  //**************************************************************************** 
177  //MeanGauss: 
178  //On calcule la moyenne de trois valeurs de gauss autour de x 
179  //**************************************************************************** 
180  float MeanGauss(float x,float Sigma) 
181  { 
182    float z; 
183    z=(float)((Gauss(x,Sigma)+Gauss((float)(x+0.5),Sigma)+Gauss((float)(x-0.5),Sigma))/(float)3.0); 
184    return (float)(z/(PI*2.0*Sigma*Sigma)); 
185  } 
186   
187  //**************************************************************************** 
188  //dGauss 
189  //On calcule ici la valeur de la derivée de la fonction de gauss au point x. 
190  //**************************************************************************** 
191  float dGauss(float x,float Sigma) 
192  { 
193    return -x/(Sigma*Sigma)*Gauss(x,Sigma); 
194  } 
195   
196  //**************************************************************************** 
197  //Separable convolution 
198  //Cette fonction permet de convoluer l'image par un masque (gau) 
199  //Le résultat sera stoké dans les variables GauX et GauY 
200  //**************************************************************************** 
201  void CImage::SeparableConvolution(float *gau,int taille,float **GauX,float **GauY) 
202  { 
203    int i,j,k,I1,I2; 
204   
205    float x,y; 
206   
207   
208    //Pour tous les pixels 
209    for(i=0;i<Width;i++) 
210      for(j=0;j<Height;j++) 
211      { 
212        x=(float)(gau[0]*GetPixel(i,j,BLUE)); 
213        y=x; 
214        for(k=1;k<taille;k++)        //Pour tous les pixels du noyau 
215        { 
216          I1=(i+k)%Width; 
217          I2=(i-k+Width)%Width; 
218          if(I1>=0 && I1<Width && I2>=0 && I2<Width) 
219            y+=(float)(gau[k]*GetPixel(I1,j,BLUE)+gau[k]*GetPixel(I2,j,BLUE)); 
220   
221          I1=(j+k)%Width; 
222          I2=(j-k+Width)%Width; 
223          if(I1>=0 && I1<Height && I2>=0 && I2<Height) 
224            x+=(float)(gau[k]*GetPixel(i,I1,BLUE)+gau[k]*GetPixel(i,I2,BLUE)); 
225        } 
226        GauX[i][j]=x; 
227        GauY[i][j]=y; 
228      } 
229  } 
230   
231  //**************************************************************************** 
232  //SeparableConvolution_dxy 
233  //On calcule le résultat de la convolution de l'image avec un masque 
234  //Le resultat sera stocker dans la variable appelé Derive 
235  //**************************************************************************** 
236  void CImage::SeparableConvolution_dxy(float **Image,float *gau,int taille,float **Derive,BOOL Direction) 
237  { 
238    int i,j,k,I1,I2; 
239    float x; 
240   
241    for(i=0;i<Width;i++) 
242      for(j=0;j<Height;j++) 
243      { 
244        x=0.0; 
245        for(k=1;k<taille;k++) 
246        { 
247          if(!Direction) 
248          { 
249            I1=(i+k)%Width; 
250            I2=(i-k+Width)%Width; 
251            if(I1>=0 && I1<Width && I2>=0 && I2<Width) 
252              x+=-gau[k]*Image[I1][j]+gau[k]*Image[I2][j]; 
253          } 
254          else 
255          { 
256            I1=(j+k)%Height; 
257            I2=(j-k+Height)%Height; 
258            if(I1>=0 && I1<Height && I2>=0 && I2<Height) 
259              x+=-gau[k]*Image[i][I1]+gau[k]*Image[i][I2]; 
260          } 
261   
262        } 
263        Derive[i][j]=x; 
264   
265      } 
266  } 
267   
268  //************************************************************************* 
269  //On regarde les 2 pixels dans la direction du gradient du pixel central 
270  //1 de chaque coté. On garde le pixel central (i) si le gradient de i est  
271  //supérieur au gradient des 2 autres pixels 
272  //On effectue une interpolation dans le cas où le pixel se situant dans la  
273  //direction du gradient tombe au milieu de 2 pixels. 
274  //************************************************************************* 
275  void CImage::NonMaxSuppress(float **dX,float **dY,CImage *ImgNorme,CImage *ImgOrientation) 
276  { 
277    int i,j,Res; 
278   
279    float x,y,N,N1,N2,N3,N4,VectX,VectY; 
280   
281    for(i=1;i<Width-1;i++) 
282      for(j=1;j<Height-1;j++) 
283      { 
284        ImgNorme->SetPixel(i,j,0,0,0); 
285        ImgOrientation->SetPixel(i,j,0,0,0); 
286   
287        //On considère dx et dy comme des vecteurs 
288        VectX=dX[i][j]; 
289        VectY=dY[i][j]; 
290        if(fabs(VectX)<0.01 && fabs(VectY)<0.01) 
291          continue; 
292   
293        N=Norm(VectX,VectY); 
294   
295        //on suit la direction du gradient qui est indiqué par les vecteurs 
296        //VectX et VectY et retient uniquement les pixels qui correspondent  
297        //à des maximum locaux 
298   
299        if(fabs(VectY) > fabs(VectX))  //la direction du vecteur est verticale 
300        { 
301          x=fabs(VectX)/fabs(VectY); 
302          y=1; 
303          N2=Norm(dX[i-1][j],dY[i-1][j]); 
304          N4=Norm(dX[i+1][j],dY[i+1][j]); 
305   
306          if(VectX*VectY>0) 
307          { 
308            N3=Norm(dX[i+1][j+1],dY[i+1][j+1]); 
309            N1=Norm(dX[i-1][j-1],dY[i-1][j-1]); 
310          } 
311          else 
312          { 
313            N3=Norm(dX[i+1][j-1],dY[i+1][j-1]); 
314            N1=Norm(dX[i-1][j+1],dY[i-1][j+1]); 
315          } 
316        } 
317        else        //la direction du vecteur est horizontale 
318        { 
319          x=fabs(VectY)/fabs(VectX); 
320          y=1; 
321          N2=Norm(dX[i][j+1],dY[i][j+1]); 
322          N4=Norm(dX[i][j-1],dY[i][j-1]); 
323   
324          if(VectX*VectY>0) 
325          { 
326            N3=Norm(dX[i-1][j-1],dY[i-1][j-1]); 
327            N1=Norm(dX[i+1][j+1],dY[i+1][j+1]); 
328          } 
329          else 
330          { 
331            N3=Norm(dX[i+1][j-1],dY[i+1][j-1]); 
332            N1=Norm(dX[i-1][j+1],dY[i-1][j+1]); 
333          } 
334        } 
335   
336        //On calcul l'interpolation du gradient 
337        if(N > (x*N1+(y-x)*N2) && N > (x*N3+(y-x)*N4) ) 
338        { 
339          if((Res=(int)N*MAG_SCALE)<=255) 
340            ImgNorme->SetPixel(i,j,Res,Res,Res); 
341          else 
342            ImgNorme->SetPixel(i,j,255,255,255); 
343          Res=(int)(atan2(VectY,VectX)*ORI_SCALE); 
344          ImgOrientation->SetPixel(i,j,Res,Res,Res); 
345        } 
346      } 
347  } 
348   
349   
350  //************************************************************************************ 
351  //Fonction qui calcule le gradient de Sobel en ne gardant que les maximas locaux. 
352  //************************************************************************************ 
353  bool CImage::SobelOptimal(CImage *ImgNorme,CImage *ImgOrientation) 
354  { 
355    float **dX=0,**dY=0; 
356   
357   
358    int i,j,k,l; 
359    int SobX[3][3]={{1,0,-1},{2,0,-2},{1,0,-1}}; 
360    int SobY[3][3]={{1,2,1},{0,0,0},{-1,-2,-1}}; 
361   
362    if(hBmp==0){ 
363      MessageBox(NULL,"SobelOptimal : L'image source est vide", 
364        NULL,MB_OK|MB_ICONWARNING); 
365      return 0; 
366    } 
367   
368    if(ImgNorme != 0 && ImgNorme != this) 
369      ImgNorme->Copy(this); 
370    if(ImgOrientation != 0 && ImgOrientation != this) 
371      ImgOrientation->Copy(this); 
372   
373    if(ImgNorme == 0) 
374      ImgNorme=this; 
375   
376    GetBitmapBits(hBmp,Width*Height*4,ucBits); 
377   
378   
379    CImage ImgTmpNorme,ImgTmpOrientation; 
380   
381    ImgTmpNorme.Copy(this); 
382   
383    if(ImgOrientation !=0) 
384      ImgTmpOrientation.Copy(this); 
385   
386    dY=AllocT_float(Width,Height); 
387    dX=AllocT_float(Width,Height); 
388   
389   
390    for(i=1;i<Width-1;i++) 
391      for(j=1;j<Height-1;j++) 
392      { 
393        dX[i][j]=0; 
394        dY[i][j]=0; 
395        for(k=-1;k<=1;k++) 
396          for(l=-1;l<=1;l++) 
397            if(i+k>=0 && i+k<Width && j+l>=0 && j+l<Height) 
398            { 
399              dX[i][j]+=(float)(GetPixel(i+k,j+l,GRAY)*SobX[k+1][l+1]); 
400              dY[i][j]+=(float)(GetPixel(i+k,j+l,GRAY)*SobY[k+1][l+1]); 
401            } 
402   
403      } 
404   
405   
406    NonMaxSuppress(dX,dY,&ImgTmpNorme,&ImgTmpOrientation); 
407   
408    ImgTmpNorme.ContrasteAuto(GRAY,ImgNorme); 
409    if(ImgOrientation!=0) 
410    { 
411      ImgTmpOrientation.ContrasteAuto(GRAY,ImgOrientation); 
412      SetBitmapBits(ImgOrientation->hBmp,Width*Height*4,ImgOrientation->ucBits); 
413      ImgOrientation->ImgType=GRAY; 
414    } 
415    SetBitmapBits(ImgNorme->hBmp,Width*Height*4,ImgNorme->ucBits); 
416   
417    ImgNorme->ImgType=GRAY; 
418   
419   
420    DesAllocT_float(dY,Width); 
421    DesAllocT_float(dX,Width); 
422    return 1; 
423  } 
424   
425   
426  float **AllocT_float(int Largeur,int Hauteur) 
427  { 
428    float **x=0; 
429    int i; 
430   
431    x=new float*[Largeur]; 
432   
433    for(i=0;i<Largeur;i++) 
434      x[i]=new float[Hauteur]; 
435   
436    return x; 
437   
438  } 
439   
440  void DesAllocT_float(float **T,int Largeur) 
441  { 
442    for(int i=0;i<Largeur;i++) 
443      delete []T[i]; 
444    delete[]T; 
445   
446  } 
447  int **AllocT_int(int Largeur,int Hauteur) 
448  { 
449    int **x=0; 
450    int i; 
451   
452    x=new int*[Largeur]; 
453   
454    for(i=0;i<Largeur;i++) 
455      x[i]=new int[Hauteur]; 
456   
457    return x; 
458   
459  } 
460   
461  void DesAllocT_int(int **T,int Largeur) 
462  { 
463    for(int i=0;i<Largeur;i++) 
464      delete []T[i]; 
465    delete[]T; 
466   
467  } 

lien1 lien2 lien3