a code to reduce the image colors

of course I could use the EncodeImage function ()
but I wanted to do something with myself even
the load button to load an image
the string "nbr_couleur" for desired number of colors (2 are added automatically)
cuseur the "tolerance" to choose a tolerance
operation:
depending on the number of colors chosen, the PRG will capture as many pixels randomly
in the picture
then use them to form a pallet
this palette is that it will use to redraw the image
a distance calculation between the true color and a color of the palette is carried out
if the result is a lower tolerance, PRG uses the corresponding color
palette
less color is used, the more necessary to increase the tolerance
exemples obtenu :
original picture

256 colors

64 colors

32 colors

16 colors

the impact parameter of tolerance :
tolerance = 40 // 8 colors

tolerance = 50 // 8 colors

tolerance = 60 // 8 colors

tolerance = 70 // 8 colors

tolerance = 70 + Floyd // 8 colors (with Floyd)



Code: Select all
;***********************************************
;Titre :*Pure_reductor_interface
;Auteur : Dobro
;Date :17/03/2016
;Heure :18:01:29
;Version Purebasic : PureBasic 5.42 LTS (Windows - x86)
;Version de l'editeur :EPB V2.62
; Libairies necessaire : Aucune
;***********************************************
;{- Enumerations / DataSections
;{ Windows
Enumeration
#Win
EndEnumeration
;}
;{ Gadgets
Enumeration
#Button_Load
#CheckBox_Floyd
#CheckBox_Flou
#TrackBar_tolerance
#Text_info
#Text_info2
#Text_tol_val
#Text_tolerance
#Text_Titre
#Container_8
#Button_Run
#String_nb_couleur
#Text_color
#Button_Save
#zone_acenseur
#image_palette
#Button_pick
#Image_ori
EndEnumeration
;}
;{ Fonts
Enumeration
#Font_Text_Titre
EndEnumeration
;}
Define.l Event
InitMouse()
UseJPEGImageDecoder():UsePNGImageDecoder():UseTGAImageDecoder():UseTIFFImageDecoder()
Resultat = InitSprite()
Declare make_palette(nb_image,nbr_couleur.i,palette )
Declare Dessin(nb_image,nbr_couleur,tolerance,Floyd,flou)
Declare mise_en_tab_ori(nb_image,Largeur_image,Hauteur_image)
Declare mise_en_tab(nb_image,largeur_image,hauteur_image)
Declare flou(nb_image,imagex,imagey, niveau)
Declare doomohundro(himg) ; floyd
Declare.i truncf0255 (a.f)
Declare rgbtolum(c.l) ;get luminosity (the min and max rgb levels / 2)
Enumeration
#Image
#image_resultat
#Fenetre
#file
EndEnumeration
Global NewList palette()
Global NewList des()
Global Flag_pick=-1,compteur,vertical,pas,compteur_coul,nbr_couleur,Flag_pick_pick
;}
Procedure OpenWindow_Win()
If OpenWindow(#Win, 180, 5, 1096, 672, "Pure_reductor By Dobro", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_TitleBar)
;If CreateGadgetList(WindowID(#Win))
ButtonGadget(#Button_Load, 15, 50, 90, 35, "Load Img")
CheckBoxGadget(#CheckBox_Floyd, 15, 135, 105, 25, "Floyd Tramage")
CheckBoxGadget(#CheckBox_Flou, 15, 160, 105, 25, "Flou")
TrackBarGadget(#TrackBar_tolerance, 10, 235, 45, 310, 0,442, #PB_TrackBar_Ticks|#PB_TrackBar_Vertical)
TextGadget(#Text_tolerance, 10, 210, 75, 25, "Tolerance")
TextGadget(#Text_tol_val,90, 210, 40, 30, "0")
SetGadgetState(#TrackBar_tolerance,35) :SetgadgetText (#Text_tol_val,str(35))
TextGadget(#Text_Titre, 430, 15, 195, 40, "Pure_reductor")
TextGadget(#Text_info, 20, 15, 100, 25, "Info")
TextGadget(#Text_info2, 150,5, 195,40, "Info2")
ButtonGadget(#Button_Run, 10, 545, 90, 35, "Run")
StringGadget(#String_nb_couleur, 85, 90, 35, 20, "256")
TextGadget(#Text_color, 15, 90, 70, 20, "Nbr Couleurs")
ButtonGadget(#Button_Save, 10, 595, 90, 35, "Save Img")
ButtonGadget(#Button_pick, 55, 240, 50, 20, "Pick",#PB_Button_Toggle )
ScrollAreaGadget(#zone_acenseur,130, 55, 940, 595,0, 0, 30)
CanvasGadget(#image_palette, 0,55, 8*16, 8*256)
ContainerGadget(#Container_8, 130, 55, 940, 595,#PB_Container_Double)
OpenWindowedScreen(gadgetID(#Container_8),0, 0, 940, 595, 0, 0, 0 ) ; on met un ecrant dedans
CloseGadgetList()
; Gadget Fonts
SetGadgetFont(#Text_Titre, LoadFont(#Font_Text_Titre, "Microsoft Sans Serif", 22, #PB_Font_HighQuality))
;EndIf
EndIf
EndProcedure
OpenWindow_Win()
;{- Event loop
Repeat
Event = WaitWindowEvent(12)
Select Event
; ///////////////////
Case #PB_Event_Gadget
Select EventGadget()
Case #Button_Load
Flag_pick_pick=0 :vertical=0:compteur=0
if FileSize("chemin.inf")<>-1
if Openfile(#file,"chemin.inf")
chemin$=ReadString(#file)
CloseFile(#file)
NomFichier$=OpenFileRequester("Charge une image", chemin$+"*.*", "", 0)
Endif
chemin$=GetPathPart(NomFichier$)
Openfile(#file,"chemin.inf")
WriteStringN(#file,chemin$)
CloseFile(#file)
Else
NomFichier$ = OpenFileRequester("Charge une image", "*.*", "", 0) ; on choisi un ficher
chemin$=GetPathPart(NomFichier$)
Openfile(#file,"chemin.inf")
WriteStringN(#file,chemin$)
CloseFile(#file)
Endif
If NomFichier$<>""
Resultat = LoadImage(#Image, NomFichier$ ) ; on charge une image
global format$=GetExtensionPart(NomFichier$)
EndIf
if IsImage(#image)
Global largeur_image=ImageWidth(#Image)
Global Hauteur_image=ImageHeight(#Image)
if largeur_image>Hauteur_image : ResizeImage(#image,1024,768):endif
if largeur_image<Hauteur_image : ResizeImage(#image,768,1024):endif
Global largeur_image=ImageWidth(#Image)
Global Hauteur_image=ImageHeight(#Image)
CloseScreen()
OpenWindowedScreen(gadgetID(#Container_8),0, 0, largeur_image,Hauteur_image, 0, 0, 0 ) ; on met un ecran dedans
CopyImage(#Image,#Image_ori)
StartDrawing(ScreenOutput()) ;
DrawImage(ImageID(#Image), 0, 0)
StopDrawing()
ResizeGadget(#container_8, #PB_Ignore, #PB_Ignore,Largeur_image, Hauteur_image)
SetGadgetAttribute(#zone_acenseur, #PB_ScrollArea_InnerWidth , Largeur_image+(Largeur_image/2))
SetGadgetAttribute(#zone_acenseur, #PB_ScrollArea_InnerHeight ,Hauteur_image+(Hauteur_image/2))
Else
MessageRequester("erreur", "l'image n'a pas pu etre chargée")
End
Endif
GLobal Dim TAB_ori(Largeur_image,Hauteur_image) ; le Tableau
GLobal Dim TAB(Largeur_image,Hauteur_image) ; le Tableau
StartDrawing(ImageOutput(nb_image))
mise_en_tab_ori(nb_image,largeur_image,Hauteur_image) ; apres ça l'image 16 millions de couleurs est dans le Tableau Tab_ori()
StopDrawing()
CopyArray(TAB_ori(), TAB())
FlipBuffers()
Case #Button_pick
if IsImage(#image_ori)
ClearScreen($0)
CopyArray(TAB_ori(), TAB())
CopyImage(#Image_ori,#Image)
Flag_pick=-Flag_pick
if Flag_pick>0 ; mode on recup les couleurs
;on nettoie le canvas qui contient la palette
; StartDrawing(CanvasOutput(#image_palette))
; Box(0,0,GadgetWidth(#image_palette),GadgetHeight(#image_palette),$0)
; StopDrawing()
; on nettoie aussi les listes concernant la palette
ClearList(palette())
ClearList(Des())
Endif
Endif
Case #zone_acenseur
;debug "ascenseur"
; if IsImage(nb_image)
; StartDrawing(ScreenOutput()) ;
; DrawImage(ImageID(nb_image), 0, 0)
; StopDrawing()
; FlipBuffers()
; Endif
Case #CheckBox_Floyd
statF=GetGadgetstate(#CheckBox_Floyd)
if statf=#PB_CheckBox_Checked
floyd=1
Else
floyd=0
Endif
Case #CheckBox_Flou
statfl=GetGadgetstate(#CheckBox_Flou)
if statfl=#PB_CheckBox_Checked
Flou=1
Else
Flou=0
Endif
Case #TrackBar_tolerance
tolerance=GetGadgetstate(#TrackBar_tolerance)
SetgadgetText (#Text_tol_val,str(tolerance))
Case #Text_tolerance
Case #Text_Titre
Case #Container_8
Case #Button_Run
if Flag_pick>0
SetGadgetState(#Button_pick, 0)
Flag_pick=-Flag_pick
Endif
if isimage(#image)
;-bouton RUN
if Flag_pick_pick=0
ClearList(palette())
ClearList(Des())
CopyArray(TAB_ori(), TAB())
Endif
ClearScreen($0)
FlipBuffers()
;Tolerance
tolerance=GetGadgetstate(#TrackBar_tolerance)
; Flou
statfl=GetGadgetstate(#CheckBox_Flou)
if statfl=#PB_CheckBox_Checked
Flou=1
Else
Flou=0
Endif
;FLoyd
statF=GetGadgetstate(#CheckBox_Floyd)
if statf=#PB_CheckBox_Checked
floyd=1
Else
floyd=0
Endif
; Nbr_couleur
nbr_couleur=val(GetGadgetText(#String_nb_couleur))
; Run
palette=1; pour afficher la palette dans l'image
if Flag_pick_pick=0
make_palette(#image,nbr_couleur,palette)
Endif
dessin(#image,nbr_couleur,tolerance,Floyd,flou ) ;
Else
MessageRequester("info", "Chargez d'abord une image")
ENdif
Case #String_nb_couleur
nbr_couleur=val(GetGadgetText(#String_nb_couleur))
Case #Text_color
Case #Button_Save
if isimage(#image)
SaveImage(#image,"resultat_en_"+str(nbr_couleur)+"_"+str(tolerance)+".bmp",#PB_ImagePlugin_BMP)
MessageRequester("info", "Image sauvé avec le nom :"+chr(10)+"resultat_en_"+str(nbr_couleur)+"_"+str(tolerance)+".bmp")
Else
MessageRequester("info", "Chargez d'abord une image")
Endif
EndSelect
; ////////////////////////
Case #PB_Event_CloseWindow
Select EventWindow()
Case #Win
CloseWindow(#Win)
Break
EndSelect
Case #WM_LBUTTONDOWN
if Flag_pick>0 ; on pick
GetCursorPos_(@cp.POINT)
GetWindowRect_(GadgetID(#Container_8),gr.RECT)
PtInRect_(@gr, cp\x<<32+cp\Y)
MapWindowPoints_(#Null, GadgetID(#Container_8), cp, 1) ; cp now contains gadget coords
if ( cp\x>=0 and cp\x<=ImageWidth(#image)) and (cp\y>=0 and cp\y<=ImageHeight(#image) )
StartDrawing(ScreenOutput()) ;
couleur_pick=point(cp\x,cp\Y)
StopDrawing()
coul.i=couleur_pick
Endif
ForEach palette()
if palette()=coul.i ; c'est une couleurs nouvelle pour la palette ?
flag_ok=1 ; non on l'a deja !
Endif
Next
; *************************************************
if flag_ok=1 ; couleur deja presente dans la palette
flag_ok=0
Else ; couleur nouvelle trouvée
Flag_pick_pick=1
AddElement(palette()) ; on la stock
palette()=coul.i
compteur_coul=compteur_coul+1
nbr_couleur=compteur_coul
SetGadgetText( #String_nb_couleur,str(nbr_couleur))
Endif
SortList(palette(), #PB_Sort_Descending)
StartDrawing(CanvasOutput(#image_palette))
Box(0,0,GadgetWidth(#image_palette),GadgetHeight(#image_palette),$0)
compteur=0:vertical=0
ForEach palette()
pas=8
Box (1+compteur,vertical,pas,pas,palette())
compteur=compteur+pas
if compteur => GadgetWidth(#image_palette)
compteur=0
vertical=vertical+pas
compteur=0
Endif
Next
StopDrawing()
Endif
EndSelect
if IsImage(#image)
StartDrawing(ScreenOutput()) ;
DrawImage(ImageID(#Image), 0, 0)
StopDrawing()
FlipBuffers()
Endif
Forever
;
;}
;-Procedures Zone
;-make palette
Procedure make_palette(nb_image,nbr_couleur.i,palette)
;if nbr_couleur.i=0 : nbr_couleur.i=256:Endif
; By Dobro
largeur_image=ImageWidth(nb_image)
Hauteur_image=ImageHeight(nb_image)
; mise en tableau
StartDrawing(ImageOutput(nb_image))
;mise_en_tab(nb_image,largeur_image,Hauteur_image) ; apres ça l'image 16 millions de couleurs est dans le Tableau Tab()
RandomSeed(13)
; ajout obligatoire du blanc, et du noir !
AddElement(palette()) ; on la stock
palette()=$0
AddElement(palette()) ; on la stock
palette()=rgb(255,255,255)
for i=1 to nbr_couleur.i-2
recommence:
x=random(largeur_image-1,1) ; choisi x couleurs au hazard dans l'image pour fabriquer la palette
y=random(Hauteur_image-1,1)
ForEach des()
xxx=des()
yyy=des()
if xxx=x and yyy=y ; coordonée deja dans la liste !
fl_px=1
Break
Endif
Next
if fl_px=1; si coordonée deja dans la liste !
fl_px=0
Goto recommence
Else ; nouvelle coordonnée ajouté
AddElement(des())
des()=x
AddElement(des())
des()=y
Endif
; ici en principe, on est sur que 2 fois le meme pixel (et donc 2 fois la meme couleur, ne sera pas dans la palette
coul.i=TAB(x,y) ; recupere la couleur dans l'image d'origine (celle des 16 millions de couleurs )
; **** Scan la liste pour chercher des couleurs nouvelles **
ForEach palette()
if palette()=coul.i ; c'est une couleurs nouvelle pour la palette ?
flag_ok=1 ; non on l'a deja !
Endif
Next
; *************************************************
if flag_ok=1 ; couleur deja presente dans la palette
flag_ok=0
Goto recommence
Else ; couleur nouvelle trouvée
AddElement(palette()) ; on la stock
palette()=coul.i
compteur_coul=compteur_coul+1
Endif
Next i
SortList(palette(), #PB_Sort_Descending)
if palette=1 ; Dessine la palette
pos=0:compteur=0:vertical=0
StopDrawing() ; ferme l'ecran pour le moment
StartDrawing(CanvasOutput(#image_palette))
Box(0,0,GadgetWidth(#image_palette),GadgetHeight(#image_palette),$0)
ForEach palette()
if nbr_couleur.i<=512
pas=8
Else
Pas=4
Endif
Box (1+compteur,vertical,pas,pas,palette())
compteur=compteur+pas
if compteur => GadgetWidth(#image_palette)
compteur=0
vertical=vertical+pas
Endif
Next
compteur=0
vertical=0
StopDrawing()
StartDrawing(ImageOutput(nb_image)) ; reouvre l'ecran
Endif
; a ce stade nous devrions avoir le bon nombre de couleurs
; *************************************************
StopDrawing()
taa$= " il y a "+str(compteur_coul+2)+" couleurs dans la palette"
taa1$= "ok la palette est créé j'attaque le dessin ... patience"
SetgadgetText (#Text_info2,taa$+chr(10)+taa1$)
Endprocedure
;- Dessin
Procedure Dessin(nb_image,nbr_couleur,tolerance,Floyd,flou)
ClearScreen(0)
FlipBuffers()
largeur_image=ImageWidth(nb_image)
Hauteur_image=ImageHeight(nb_image)
FreeImage(nb_image) ; efface l'image pour etre sur d'avoir une nouvelle base
CreateImage(nb_image, largeur_image,Hauteur_image) ; recreer l'image vierge
taa2$= "debut du dessin"
;SetgadgetText (#Text_info2,taa$+ " "+taa1$+ " "+taa2$)
StartDrawing(ImageOutput(nb_image))
For y=0 to Hauteur_image-1
For x=0 to largeur_image-1
coul.i=TAB(x,y)
rf.c=red(coul.i)
vf.c=green(coul.i)
bf.c=blue(coul.i)
;if nbr_couleur>8
ForEach palette()
r.c=red(palette()) :v.c=green(palette()):b.c=blue(palette())
distance=sqr(pow(rf-r.c,2)+ pow(vf-v.c,2)+pow(bf-b.c,2)) ; calcul de la distqnce qui separe la couleur de l'image avec la couleur de la palette
if distance<=tolerance
plot(x,y,rgb(r.c,v.c,b.c)) ; dessine avec la palette
mem_couleur=rgb(r.c,v.c,b.c)
BReak
Else
plot(x,y,mem_couleur) ; sinon dessine avec la derniere couleur utilisé (c'est ce qui occasionne les a-plat lorsque le parametre tolerance est trop faible ;o)
Endif
Next
compteur_activity=compteur_activity+1
ccco=ccco+1
if ccco=1000
WaitWindowEvent(5)
ccco=0
Endif
Next x
taa$=str(compteur_activity/100)+"/"+str(largeur_image * Hauteur_image/100) ; affiche l'activité
SetgadgetText (#Text_info,taa$)
Next y
taa$= "Fin dessin"
SetgadgetText (#Text_info,taa$)
SetgadgetText (#Text_info2,"")
if Floyd=1 ; dessine le Floyd
mise_en_tab(nb_image,largeur_image,Hauteur_image)
DoOmohundro(nb_image)
Endif
If Flou=1 ; ajoute du Flou
mise_en_tab(nb_image,largeur_image,Hauteur_image)
Niveau=1
Flou(nb_image,largeur_image,Hauteur_image, Niveau)
Endif
StopDrawing()
EndProcedure
Procedure mise_en_tab_ori(nb_image,Largeur_image,Hauteur_image)
; By Dobro
; mise en tableau du resultat de la reduction
;;StartDrawing(ImageOutput(nb_image))
For y=0 to Hauteur_image-1
For x=0 to largeur_image-1
TAB_ori(x,y)=point(x,y)
Next x
Next y
;;StopDrawing()
EndProcedure
Procedure mise_en_tab(nb_image,Largeur_image,Hauteur_image)
; By Dobro
; mise en tableau du resultat de la reduction
;;StartDrawing(ImageOutput(nb_image))
For y=0 to Hauteur_image-1
For x=0 to largeur_image-1
TAB(x,y)=point(x,y)
Next x
Next y
;;StopDrawing()
EndProcedure
Procedure Flou(nb_image,ImageX,ImageY, Niveau)
; Le Soldat inconnu
;;StartDrawing ( ImageOutput (nb_image)) ; on dessine sur l'image
x = 0 ; on se place en x=0 sur l'image
Repeat
y = 0 ; on se place en y=0 sur l'image
Repeat
; on récupère la couleur du point en x et y et alentour
Rouge = 0
Vert = 0
Bleu = 0
NbPixel = 0
For Px = -Niveau To Niveau
For Py = -Niveau To Niveau
If Px + x >= 0 And Px + x < ImageX
If Py + y >= 0 And Py + y < ImageY
; on fait le mélange des couleurs entre le point en x,y et ceux qui sont à sont alentour
Couleur = TAB(Px + x, Py + y)
Rouge = Rouge + Red (Couleur)
Vert = Vert + Green (Couleur)
Bleu = Bleu + Blue (Couleur)
NbPixel + 1
EndIf
EndIf
Next
Next
; On recalcule la nouvelle couleur
Couleur = RGB (Rouge / NbPixel, Vert / NbPixel, Bleu / NbPixel)
; si la taille du nouveau pixel est positive
Plot (x, y, Couleur) ; on dessine un carré qui fait le nouveau pixel de la même couleur que celle récupéré en x et y
; on se déplace sur l'image en y de la taille d'un pixel
y = y + 1
Until y >= ImageY ; si on a finit la colonne de l'image placé en x
; on se déplace sur l'image en x de la taille d'un pixel
x = x + 1
; on fait progresser la barre
;;;SetGadgetState ( #Barre , x)
Until x >= ImageX ; si on a traité toutes les lignes de l'image
;;StopDrawing ()
EndProcedure
Procedure DoOmohundro(hImg) ; Floyd
Protected nextpixel.f, ierror.f, fer.f, eer.f, teer.f, lr.f
;StartDrawing(ImageOutput(hImg))
width = ImageWidth(hImg): height = ImageHeight(hImg)
Protected error.f ;hold error for current pixel
Dim error_arr.f(width)
For y = 2 To height-3
lr = 0
For x = 2 To width-3
val = TruncF0255(RGBtoLum(Point(x,y)) + error_arr(x))
If val > 128
Plot(x,y,TAB(x,y))
error = val-255
Else
r=red(TAB(x,y))/1.5
v=green(TAB(x,y))/1.5
b=blue(TAB(x,y))/1.5
Plot(x,y,rgb(r,v,b))
error = val
EndIf
fer.f = error/4 ;a fourth of the error
eer.f = fer/2 ;an eighth
teer.f = fer + eer ;three eights
error_arr(x-1) + eer
error_arr(x) = teer + lr
lr = eer
error_arr(x+1) + teer
Next x
Next y
;StopDrawing()
EndProcedure
Procedure.i TruncF0255 (a.f)
If a > 255: a = 255: ElseIf a < 0: a = 0: EndIf
ProcedureReturn a
EndProcedure
Procedure RGBtoLum(c.l) ;Get luminosity (the min and max RGB levels / 2)
r = (c & $FF) : g = ((c & $FF00) >> 8) : b = ((c & $FF0000) >> 16)
If g < r: min = g: Else: min = r: EndIf
If b < min: min = b: EndIf
If g > r: max = g: Else: max = r: EndIf
If b > max: max = b: EndIf
If b > max: max = b: EndIf
lum = (max + min) >> 1 ;/ 2.0
ProcedureReturn lum
EndProcedure
; Epb