Dithering.cpp

1  /***********************************************************************************  
2      ImAnalyse : software in image processing and image analysis 
3     
4      Copyright (C) 27 avril 2008  <Vincent MORARD> 
5    Version: 2.0 
6      Contact: vincent<POINT>morard<AROBAS>cpe<POINT>fr 
7    Website: http://pistol.petesampras.free.fr 
8   
9      This program is free software: you can redistribute it and/or modify 
10      it under the terms of the GNU General Public License as published by 
11      the Free Software Foundation, either version 3 of the License, or 
12      (at your option) any later version. 
13   
14      This program is distributed in the hope that it will be useful, 
15      but WITHOUT ANY WARRANTY; without even the implied warranty of 
16      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
17      GNU General Public License for more details. 
18   
19      You should have received a copy of the GNU General Public License 
20      along with this program.  If not, see <http://www.gnu.org/licenses/ 
21  **********************************************************************************/ 
22  #include "Cimage.h" 
23   
24   
25   
26   
27  typedef struct TAG_CoefDithering 
28  { 
29    int Offx,Offy; 
30    double Coef; 
31    void *Next; 
32  }CoefDithering; 
33   
34   
35  //****************************************************************************** 
36  //Add  : Ajout d'un element  
37  // 
38  //INPUT 
39  //-- CD adresse du premier element 
40  //-- Offx,Offy,Coef : Info de l'element 
41  //****************************************************************************** 
42  void Add(CoefDithering **CD,int Offx,int Offy,double Coef) 
43  { 
44    CoefDithering *Current=new CoefDithering; 
45    Current->Offx=Offx; 
46    Current->Offy=Offy; 
47    Current->Coef=Coef; 
48    Current->Next=NULL; 
49    if(*CD==NULL)     //First 
50      *CD=Current; 
51    else { 
52      CoefDithering *Search=*CD; 
53      while(Search->Next)Search=(CoefDithering*)Search->Next; 
54      Search->Next=Current; 
55    } 
56  } 
57   
58   
59   
60  //****************************************************************************************** 
61  //DitherMethod  : Effectue le chainage des coefficients suivant la methode desire 
62  // 
63  //INPUT 
64  //-- Method : Methode desire : Floyd_Steinberg ou Bill_Atkinson ou Stucki ou Burkes ou Morard 
65  //-- CD     : Adresse du premier element  
66  //OUPUT 
67  //-- WidthSup: Nombre de pixel qui sortiront de l'image en largeur 
68  //-- HeightSup:Nombre de pixel qui sortiront de l'image en haUteur 
69  //******************************************************************************************* 
70  void DitherMethod(int Method,CoefDithering **CD,int *WidthSup,int *HeightSup) 
71  { 
72    switch(Method) 
73    { 
74    //The Floyd-Steinberg filter 
75    //          *   7 
76    //      3   5   1     (1/16) 
77    case Floyd_Steinberg: 
78      Add(CD,1 ,0 ,7.0/16.0); 
79      Add(CD,-1,1 ,3.0/16.0); 
80      Add(CD,0 ,1 ,5.0/16.0); 
81      Add(CD,1 ,1 ,1.0/16.0); 
82   
83      //Pour ne pas sortir de l'image. 
84      *WidthSup = 1; 
85      *HeightSup= 1; 
86    break; 
87   
88    //Bill Atkinson dithering 
89    //  *  1/8  1/8 
90    //  1/8  1/8  1/8 
91    //    1/8  
92    case Bill_Atkinson: 
93      Add(CD,1 ,0 ,1.0/8.0); 
94      Add(CD,2 ,0 ,1.0/8.0); 
95      Add(CD,0,1 ,1.0/8.0); 
96      Add(CD,1 ,1 ,1.0/8.0); 
97      Add(CD,2 ,1 ,1.0/8.0); 
98      Add(CD,1 ,2 ,1.0/8.0); 
99   
100      //Pour ne pas sortir de l'image. 
101      *WidthSup = 2; 
102      *HeightSup= 2; 
103      break; 
104   
105    //The Jarvis, Judice, and Ninke filter 
106    //             *   7   5  
107    //     3   5   7   5   3 
108    //     1   3   5   3   1   (1/48) 
109    case Jarvis_Judice_Ninke: 
110      Add(CD,1 ,0 ,  7.0/48.0); 
111      Add(CD,2 ,0 ,  5.0/48.0); 
112      Add(CD,-2,1 ,  3.0/48.0); 
113      Add(CD,-1,1 ,  5.0/48.0); 
114      Add(CD,0 ,1 ,  7.0/48.0); 
115      Add(CD,1 ,1 ,  5.0/48.0); 
116      Add(CD,2 ,1 ,  3.0/48.0); 
117   
118      Add(CD,-2,2 ,  1.0/48.0); 
119      Add(CD,-1,2 ,  3.0/48.0); 
120      Add(CD,0 ,2 ,  5.0/48.0); 
121      Add(CD,1 ,2 ,  3.0/48.0); 
122      Add(CD,2 ,2 ,  1.0/48.0); 
123   
124      //Pour ne pas sortir de l'image. 
125      *WidthSup = 2; 
126      *HeightSup= 2; 
127      break; 
128   
129   
130    //The Stucki filter 
131    //             *   8   4 
132    //     2   4   8   4   2 
133    //     1   2   4   2   1   (1/42) 
134    case Stucki: 
135      Add(CD,1 ,0 ,  8.0/42.0); 
136      Add(CD,2 ,0 ,  4.0/42.0); 
137   
138      Add(CD,-2,1 ,  2.0/42.0); 
139      Add(CD,-1,1 ,  3.0/42.0); 
140      Add(CD,0 ,1 ,  8.0/42.0); 
141      Add(CD,1 ,1 ,  4.0/42.0); 
142      Add(CD,2 ,1 ,  2.0/42.0); 
143   
144      Add(CD,-2,2 ,  1.0/42.0); 
145      Add(CD,-1,2 ,  2.0/42.0); 
146      Add(CD,0 ,2 ,  4.0/42.0); 
147      Add(CD,1 ,2 ,  2.0/42.0); 
148      Add(CD,2 ,2 ,  1.0/42.0); 
149   
150      //Pour ne pas sortir de l'image. 
151      *WidthSup = 2; 
152      *HeightSup= 2; 
153   
154      break; 
155   
156    //The Burkes filter 
157    //             *   8   4 
158    //     2   4   8   4   2  (1/32) 
159    case Burkes: 
160      Add(CD,1 ,0 ,  8.0/32.0); 
161      Add(CD,2 ,0 ,  4.0/32.0); 
162   
163      Add(CD,-2,1 ,  2.0/32.0); 
164      Add(CD,-1,1 ,  3.0/32.0); 
165      Add(CD,0 ,1 ,  8.0/32.0); 
166      Add(CD,1 ,1 ,  4.0/32.0); 
167      Add(CD,2 ,1 ,  2.0/32.0); 
168   
169      //Pour ne pas sortir de l'image. 
170      *WidthSup = 2; 
171      *HeightSup= 1; 
172   
173      break; 
174   
175    //The Sierra filters 
176      //        *   5   3                  The Sierra3 filter 
177      //2   4   5   4   2 
178      //    2   3   2       (1/32) 
179    case Sierra: 
180      Add(CD,1 ,0 ,  5.0/32.0); 
181      Add(CD,2 ,0 ,  3.0/32.0); 
182   
183      Add(CD,-2 ,1 ,  2.0/32.0); 
184      Add(CD,-1 ,1 ,  4.0/32.0); 
185      Add(CD,0 ,1 ,   5.0/32.0); 
186      Add(CD,1 ,1 ,  4.0/32.0); 
187      Add(CD,2 ,1 ,  2.0/32.0); 
188   
189      Add(CD,-1 ,2 ,  2.0/32.0); 
190      Add(CD,0 ,2 ,  3.0/32.0); 
191      Add(CD,1 ,2 ,  2.0/32.0); 
192   
193      //Pour ne pas sortir de l'image. 
194      *WidthSup = 2; 
195      *HeightSup= 2; 
196   
197      break; 
198   
199   
200    // *   2                          The Sierra-2-4A filter 
201      // 1   1               (1/4) 
202    case Sierra_Lite: 
203   
204      Add(CD,1 ,0 ,  2.0/4.0); 
205   
206      Add(CD,0 ,1 ,  1.0/32.0); 
207      Add(CD,1 ,1 ,  1.0/32.0); 
208   
209      //Pour ne pas sortir de l'image. 
210      *WidthSup = 1; 
211      *HeightSup= 1; 
212      break; 
213   
214      //     *   1   2  
215      //4   2   1   2   4 
216      //8  4   2   4   8        /42 
217    case Morard: 
218   
219      Add(CD,1 ,0 ,  1.0/42.0); 
220      Add(CD,2 ,0 ,  2.0/42.0); 
221   
222      Add(CD,-2 ,1 ,  4.0/42.0); 
223      Add(CD,-1 ,1 ,  3.0/42.0); 
224      Add(CD,0 ,1 ,   1.0/42.0); 
225      Add(CD,1 ,1 ,  3.0/42.0); 
226      Add(CD,2 ,1 ,  4.0/42.0); 
227   
228      Add(CD,-2 ,2 ,  8.0/42.0); 
229      Add(CD,-1 ,2 ,  4.0/42.0); 
230      Add(CD,0 ,2 ,  2.0/42.0); 
231      Add(CD,1 ,2 ,  4.0/42.0); 
232      Add(CD,2 ,2 ,  8.0/42.0); 
233   
234      //Pour ne pas sortir de l'image. 
235      *WidthSup = 2; 
236      *HeightSup= 2; 
237      break; 
238   
239   
240    } 
241  } 
242   
243   
244  //********************************************************************************************************************* 
245  //DITHERING 
246  // 
247  //INPUT 
248  //--Method  :Si Method <=0 ou >8 alors il n'y a pas de diffusion d'erreur => on prend la valeur du pixel le plus proche 
249  //       Floyd_Steinberg ou Bill_Atkinson ou Stucki  ou Burkes ou Morard 
250  //--Type    :si RGBi  :On effectuera la diffusion d'erreur sur les 3 canaux de facon independante  
251  //         GRAY    :On convertie l'image en niveau de gris puis on effectue la diffusion d'erreur 
252  //--Niveau  :tableau de int rassemblant tous les niveaux de sortie de l'image 
253  //--NbNiveau:Nombre d'element du tableau niveau 
254  //--ImgDest :Image ou sera stoker le traitement. Si 0 sera stoker dans l'image source  
255  // 
256  //OUTPUT  
257  //1 = reussit, 0 = echec 
258  //**********************************************************************************************************************   
259  bool CImage::Dithering(int Type,int Method,int *Niveau,int NbNiveau,CImage *ImgDest) 
260  { 
261   
262   
263    if(hBmp==0){ 
264      MessageBox(NULL,"Dither : L'image source est vide", 
265            NULL,MB_OK|MB_ICONWARNING); 
266      return 0; 
267    } 
268   
269    if(ImgDest!=0 && ImgDest!=this) 
270      ImgDest->Copy(this); 
271    if(ImgDest==0) 
272      ImgDest=this; 
273   
274    if(ImgType == GRAY) 
275      Type=GRAY; 
276    //Préparation de la diffusion suivant la méthode retenu. 
277    int WidthSup,HeightSup; 
278    CoefDithering *CD=NULL,*CurrentCD=NULL; 
279    DitherMethod(Method,&CD,&WidthSup,&HeightSup); 
280   
281   
282    //recupération des pixels 
283    GetBitmapBits(ImgDest->hBmp,Width*Height*4,ImgDest->ucBits); 
284   
285    int i,j,Error,LastVal,Min,NewVal,k,Diff; 
286   
287    int LastValR,LastValG,DiffR,DiffG,NewValR,NewValG,MinR,MinG;    //Si RGB 
288   
289    for(j=0;j<Height;j++) 
290      for(i=0;i<Width;i++) 
291      { 
292        LastVal=ImgDest->ucBits[(i+j*Width)*4]; 
293        Min=256; 
294   
295        switch(Type) 
296        { 
297          case RGBi: 
298            MinR=256; 
299            MinG=256; 
300            LastValR=ImgDest->ucBits[(i+j*Width)*4+2]; 
301            LastValG=ImgDest->ucBits[(i+j*Width)*4+1]; 
302            for(k=0;k<NbNiveau;k++) 
303            { 
304              Diff = abs(LastVal-Niveau[k]); 
305              DiffG = abs(LastValG-Niveau[k]); 
306              DiffR = abs(LastValR-Niveau[k]); 
307              if(DiffR < MinR) { 
308                MinR=DiffR; 
309                NewValR=Niveau[k]; 
310              } 
311              if(DiffG < MinG) { 
312                MinG=DiffG; 
313                NewValG=Niveau[k]; 
314              } 
315              if(Diff < Min) { 
316                Min=Diff; 
317                NewVal=Niveau[k]; 
318              } 
319            } 
320            ImgDest->ucBits[(i+j*Width)*4]=NewVal; 
321            ImgDest->ucBits[(i+j*Width)*4+1]=NewValG; 
322            ImgDest->ucBits[(i+j*Width)*4+2]=NewValR; 
323            break; 
324          default: 
325            for(k=0;k<NbNiveau;k++) 
326            { 
327              Diff = abs(LastVal-Niveau[k]); 
328              if(Diff < Min) { 
329                Min=Diff; 
330                NewVal=Niveau[k]; 
331              } 
332            } 
333            ImgDest->ucBits[(i+j*Width)*4]=NewVal; 
334            ImgDest->ucBits[(i+j*Width)*4+1]=NewVal; 
335            ImgDest->ucBits[(i+j*Width)*4+2]=NewVal; 
336            break; 
337        } 
338   
339   
340   
341   
342   
343   
344        //Gestion des bordures ==> Simple seuillage pas de diffusion de l'erreur 
345        if(i>=Width-WidthSup || j>=Height-HeightSup || i<WidthSup) 
346          continue; 
347   
348   
349   
350        CurrentCD=CD; 
351        Error=LastVal-ImgDest->ucBits[(i+j*Width)*4]; 
352   
353   
354        while(CurrentCD)      //BLUE 
355        { 
356          //On repartie l'erreur sur les pixels suivant: 
357          NewVal=Limit((int)(CurrentCD->Coef * Error + ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4])); 
358          ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4]=NewVal; 
359   
360          CurrentCD=(CoefDithering*)CurrentCD->Next; 
361        } 
362   
363   
364   
365        if(Type==RGBi) 
366        { 
367          CurrentCD=CD; 
368          Error=LastValG-ImgDest->ucBits[(i+j*Width)*4+1]; 
369          while(CurrentCD)      //GREEN 
370          { 
371   
372            //On repartie l'erreur sur les pixels suivant: 
373            NewVal=Limit((int)(CurrentCD->Coef * Error + ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4+1])); 
374            ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4+1]=NewVal; 
375   
376            CurrentCD=(CoefDithering*)CurrentCD->Next; 
377          } 
378   
379          CurrentCD=CD; 
380          Error=LastValR-ImgDest->ucBits[(i+j*Width)*4+2]; 
381          while(CurrentCD)      //RED 
382          { 
383   
384            //On repartie l'erreur sur les pixels suivant: 
385            NewVal=Limit((int)(CurrentCD->Coef * Error + ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4+2])); 
386            ImgDest->ucBits[(i+CurrentCD->Offx+(j+CurrentCD->Offy)*Width)*4+2]=NewVal; 
387   
388            CurrentCD=(CoefDithering*)CurrentCD->Next; 
389          } 
390        } 
391      } 
392   
393   
394    while(CD)      //liberation de la memoire 
395    { 
396      CurrentCD=(CoefDithering*)CD->Next; 
397      delete CD; 
398      CD=CurrentCD; 
399    } 
400    if(Type==GRAY) 
401      ImgDest->ImgType=GRAY; 
402    if(Type==GRAY && NbNiveau==2 && Niveau[0]==0 && Niveau[1]==255) 
403      ImgDest->ImgType=BIN; 
404    SetBitmapBits(ImgDest->hBmp,Width*Height*4,ImgDest->ucBits); 
405    return 1; 
406  } 
407   
408   
409   
410  //random 
411  bool CImage::Dithering2(int Type,CImage *ImgDest) 
412  { 
413   
414    if(hBmp==0){ 
415      MessageBox(NULL,"Dithering : L'image source est vide", 
416            NULL,MB_OK|MB_ICONWARNING); 
417      return 0; 
418    } 
419   
420    if(ImgDest!=0 && ImgDest!=this) 
421      ImgDest->Copy(this); 
422    if(ImgDest==0) 
423      ImgDest=this; 
424   
425   
426   
427    //recupération des pixels 
428    GetBitmapBits(ImgDest->hBmp,Width*Height*4,ImgDest->ucBits); 
429   
430    int i,j; 
431    for(i=0;i<Width;i++) 
432      for(j=0;j<Height;j++) 
433      { 
434        if(ImgDest->ucBits[(i+j*Width)*4]>rand()%256) { 
435          ImgDest->ucBits[(i+j*Width)*4]=255; 
436          ImgDest->ucBits[(i+j*Width)*4+1]=255; 
437          ImgDest->ucBits[(i+j*Width)*4+2]=255; 
438        } 
439        else { 
440          ImgDest->ucBits[(i+j*Width)*4]=0; 
441          ImgDest->ucBits[(i+j*Width)*4+1]=0; 
442          ImgDest->ucBits[(i+j*Width)*4+2]=0; 
443        } 
444      } 
445   
446   
447    SetBitmapBits(ImgDest->hBmp,Width*Height*4,ImgDest->ucBits); 
448    return 1; 
449  } 

lien1 lien2 lien3