Color Quantizing Image Editor

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Color Quantizing Image Editor

Post by wilbert »

I did an attempt of the suggestion I made.
Here's the modified NeuQuant module
http://www.purebasic.fr/english/viewtop ... 12&t=57939

When using this, your procedure could be rewritten like this

Code: Select all

Procedure ASSEMBLE_TO_PALETTE(Array palette(1))
   ; assign each pixel of an image to the defined palette
   
   Protected Xmax, yMax, i, x, y
   Protected dither = GetGadgetState(#Chk_Dither)
   
   CopyImage(ImgRef,ImgWork) ; source and output images
   
   InxBuildFromPalette(palette())
   
   StartDrawing(ImageOutput(ImgWork))
   Xmax = OutputWidth() - 1 : Ymax = OutputHeight() - 1
   
   For y = 0 To Ymax
     For x = 0 To Xmax
       If dither
         i = InxSearch(PointOrdDith(x, y)); dither
       Else
         i = InxSearch(Point(x, y)); no dither
       EndIf  
       Plot(x, y, Palette(i))
     Next x
   Next y
   
   StopDrawing()
   
   Select GetGadgetState(#Combo_Qmode)
      Case #Q_Fixed_16, #Q_fixed_24, #Q_fixed_32
      Default : SHOW_PALETTE(palette())
   EndSelect
EndProcedure
I would love to hear if it makes a significant difference in processing speed or not.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

wilbert wrote:I would love to hear if it makes a significant difference in processing speed or not.
Yes it makes a big difference. :D

Here are the results of a test I did.
test image size was 1920 x 1080.
quantization method was 'popularity'
palette size (limit) was set to 256.

my original procedure results:
.... 5384mS without dither
.... 5413mS with dither

using your procedure with modified NeuQuant module
.... 550ms without dither
.... 570ms with dither

conclusion: speed improvement is about x10.

Many thanks Wilbert, I will include this in the next version for sure.

BP
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

I have just uploaded a new version of CQ Image editor.
Notable improvements are the inclusion of Wilbert's NearestColorModule,
and an improved histogram display.

Use this latest version to find out for yourself what the color of "The Dress" really is!
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
yyh
New User
New User
Posts: 3
Joined: Wed Aug 19, 2015 9:43 am

Re: Color Quantizing Image Editor

Post by yyh »

Hi!
I need your help. I only need to show the color palette of the quantized image for my project.
I am using the popularity_palette function, which other functions should I use?

Thank you!
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

@yyh
yyh wrote:I am using the popularity_palette function, which other functions should I use?
Here are the procedures you will need.
I have modified them so they are not dependent on the larger program.
The code here is a complete working example with the exception of the include file which you can
download here: http://www.purebasic.fr/english/viewtop ... 12&t=61475

@everyone
Expect a rather large update to my image editor in the near future.

Code: Select all

EnableExplicit

XIncludeFile "NearestColorModule.pbi" ; http://www.purebasic.fr/english/viewtopic.php?f=12&t=61475

UsePNGImageDecoder()
UseJPEGImageDecoder()

Declare POPULARITY_PALETTE(ImgRef.i,limit.i)
Declare SCAN_FOR_PALETTE(image.i)
Declare SHOW_PALETTE(Array palette.l(1))
Declare COUNT_COLORS(image.i)
Declare ASSEMBLE_TO_PALETTE(Array palette.l(1), ImgRef.i, dither.i)

#WinMain    = 0
#WinPalette = 1

Structure PopType ; used in POPULARITY_PALETTE()
   clr.l ; color
   pop.i ; popularity
EndStructure 

Global Dim WK_Pal.l(0) ;POPULARITY_PALETTE() fills this array

Define.i w,h,Qimage
Define.i limit = 32 ; the maximum number of colors in the result
Define.i dither = #True

Define.i ImgRef = LoadImage(#PB_Any,"TestImage.png") ;<-- edit path and filename as required

If ImgRef : w = ImageWidth(ImgRef) : h = ImageHeight(ImgRef)
   If OpenWindow(#WinMain,0,0,w,h,"Example",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
      
      If POPULARITY_PALETTE(ImgRef, limit) ;generate the palette
         Qimage = ASSEMBLE_TO_PALETTE(WK_Pal(), ImgRef, dither) ;build the final image
         If IsImage(Qimage)
            ImageGadget(#PB_Any,0,0,w,h,ImageID(Qimage)) ;display the result
            SCAN_FOR_PALETTE(Qimage) ; show the palette in a separate window
         EndIf
      EndIf
      
      Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
   EndIf
EndIf

End
; ****************************************************************************************

Procedure POPULARITY_PALETTE(ImgRef.i,limit.i)
   ; Create a color palette with a modified popularity approach.
   ; ImgRef = the source image.
   ; Limit = the maximum number of colors in result.
   ; Limit count can be specified from 2 to 256. (256 is arbitrary limit)
   ; Required support procedures are:
   ;   'COUNT_COLORS', and 'ASSEMBLE_TO_PALETTE'.
   ; Finished palette is placed in the global array WK_Pal().
   ; This algorithm was created by BasicallyPure.
   
   Static.i kb = $FF0000, kg = $00FF00, kr = $0000FF
   Protected.i ImgWork, count, Xmin,Ymin,Xmax, Ymax, i, x, y, lum, d, br, da, mb, md
   
   Macro BMSK(mask) ; simple bitmask color reduction method
      StartDrawing(ImageOutput(ImgWork))
         For y = Ymin To yMax
            For x = Xmin To xMax
               Plot(x, y, Point(x,y) & mask | $0F0F0F)
            Next x
         Next y
      StopDrawing()
   EndMacro
   
   If IsImage(ImgRef)
      ImgWork = CopyImage(ImgRef,#PB_Any)
      
      If ImgWork
         count = COUNT_COLORS(ImgWork)
         If count <= limit : limit = count : EndIf
      Else
         ProcedureReturn 0
      EndIf
   Else
      ProcedureReturn 0
   EndIf
   
   ; 1) preprocess colors using BitMask method, color count will
   ;    be greatly reduced.
      Xmax = ImageWidth(ImgWork) - 1
      Ymax = ImageHeight(ImgWork) - 1
      
      If limit < 48
         BMSK($C0C0C0) ; _64 possible
         count = COUNT_COLORS(ImgWork)
      Else
         count = 0
      EndIf
      
      If count < (limit + 10) ; need more colors to work with
         If IsImage(ImgWork) : FreeImage(ImgWork) : EndIf
         ImgWork = CopyImage(ImgRef,#PB_Any)
         BMSK($E0E0E0) ; _512 possible
         count = COUNT_COLORS(ImgWork)
         If count < (limit + 10) ; need still more colors
            If IsImage(ImgWork) : FreeImage(ImgWork) : EndIf
            ImgWork = CopyImage(ImgRef,#PB_Any)
            BMSK($F0F0F0) ; _4096 possible
         EndIf
      EndIf
   
   ; 2) gather popularity data
      NewMap Pmap.i()
      StartDrawing(ImageOutput(ImgWork))
         For y = Ymin To Ymax
            For x = Xmin To Xmax
               Pmap(Str(Point(x,y))) + 1
            Next 
         Next
      StopDrawing()
      
      If MapSize(Pmap()) < limit : limit = MapSize(Pmap()) : EndIf
      
   ; 3) subdivide colors into 4 brightness lists
      NewList bright.PopType()
      NewList MedBri.PopType()
      NewList MedDrk.PopType()
      NewList dark.PopType()
      
      ForEach Pmap()
         d = Val(MapKey(Pmap()))
         lum = (d & kr)<<1 + (d & kg) >> 6 + (d & kb) >> 16
         If lum > 1338
            AddElement(bright()) : bright()\clr = d : bright()\pop = Pmap()
         ElseIf lum > 892
            AddElement(MedBri()) : MedBri()\clr = d : MedBri()\pop = Pmap()
         ElseIf lum > 446
            AddElement(MedDrk()) : MedDrk()\clr = d : MedDrk()\pop = Pmap()
         Else
            AddElement(dark())   : dark()\clr   = d : dark()\pop   = Pmap()
         EndIf
      Next
      
   ; 4) sort each brightness lists by popularity
      SortStructuredList(bright(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
      SortStructuredList(MedBri(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
      SortStructuredList(MedDrk(),#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
      SortStructuredList(dark()  ,#PB_Sort_Descending,OffsetOf(PopType\pop),#PB_Integer)
      
   ; 5) create the final palette
      FirstElement(bright()) : br = ListSize(bright())
      FirstElement(MedBri()) : mb = ListSize(MedBri())
      FirstElement(MedDrk()) : md = ListSize(MedDrk())
      FirstElement(dark())   : da = ListSize(dark())
      
      limit - 1
      ReDim WK_Pal(limit)
      
      i = 0 : d = %00
      Repeat ; pick from each list in turn the most popular color
         If d = %00 And br > 0
            WK_Pal(i) = bright()\clr
            NextElement(bright())
            i + 1 : br - 1
         ElseIf d = %01 And da > 0
            WK_Pal(i) = dark()\clr
            NextElement(dark())
            i + 1 : da - 1
         ElseIf d = %10 And mb > 0
            WK_Pal(i) = MedBri()\clr
            NextElement(MedBri())
            i + 1 : mb - 1
         ElseIf d = %11 And md > 0
            WK_Pal(i) = MedDrk()\clr
            NextElement(MedDrk())
            i + 1 : md - 1
         EndIf
            
         d = (d + %01) & %11
      Until i > limit
      
      If IsImage(ImgWork) : FreeImage(ImgWork) : EndIf
      
      ProcedureReturn 1
EndProcedure

Procedure COUNT_COLORS(image.i)
   ; returns the number of unique colors in an image (24 bit)
   Protected.i x, y, max_x, max_y, c, count, m
   Dim m.a($1FFFFF)
   StartDrawing(ImageOutput(image))
      max_x = ImageWidth(image) - 1
      max_y = ImageHeight(image) - 1
      For y = 0 To max_y
         For x = 0 To max_x
            c = Point(x, y) & $FFFFFF
            If m(c >> 3) & 1 << (c & 7) = 0
               m(c >> 3) | 1 << (c & 7)
               count + 1
            EndIf
         Next
      Next
   StopDrawing()
   ProcedureReturn count
EndProcedure

Procedure ASSEMBLE_TO_PALETTE(Array palette.l(1), ImgRef.i, dither.i)
   ; assign each pixel of an image to the defined palette using NearestColor module
   ; ImgRef  = the source image
   ; dither: 0 = no dither, 1 = dither
   ; A new image is created, the return value is the new image number.
   
   NearestColor::CatchPalette(@palette(), ArraySize(palette())+1)
   
   ProcedureReturn NearestColor::DitheredImage(ImgRef, dither*128)
   
EndProcedure

Procedure SCAN_FOR_PALETTE(image.i)
   ; obtain the palette of all colors used in an image
   ; stops if number of colors exceeds 288
   Static NewMap Pmap.i(1024)
   Static Dim Palette.l(0)
   Protected c,i,x,y,Xmax,Ymax
   
   Xmax = ImageWidth(image)-1
   Ymax = ImageHeight(image)-1
   
   StartDrawing(ImageOutput(image))
      For y = 0 To Ymax
         For x = 0 To Xmax
            c = Point(x,y)
               If MapSize(Pmap()) > 288
                  Break 2
               EndIf
            Pmap(Str(c)) = c
         Next
      Next
   StopDrawing()

   ReDim palette(MapSize(Pmap())-1)
   
   i = 0
   ForEach Pmap()
      palette(i) = Pmap()
      i + 1
   Next
   
   ClearMap(Pmap())
   
   SHOW_PALETTE(Palette())
EndProcedure

Procedure SHOW_PALETTE(Array palette.l(1))
   ; draw the palette window
   
   Static flags = #PB_Window_SystemMenu | #PB_Window_Tool | #PB_Window_ScreenCentered
   Static imgGad, imgPalette
   Protected c, i, x, y, columns, Xmax, Ymax, blockSize
   Protected Imax = ArraySize(palette())
   
   If IsWindow(#WinPalette) = 0
      OpenWindow(#WinPalette,0,0,110,220,"",flags,WindowID(#WinMain))
      imgGad = ImageGadget(#PB_Any,0,0,1,1,0)
   EndIf
   
   If Imax > 287 ; (12_columns * 24_rows) - 1
      Imax = 287
      SetWindowTitle(#WinPalette,"over!")
   Else
      SetWindowTitle(#WinPalette,Str(Imax+1)+" Palette")
   EndIf
   
   columns = Round(Sqr((Imax+1)/2),#PB_Round_Up)
   blockSize = 112 / columns
   Xmax = columns * blockSize
   Ymax = Xmax * 2
   
   If IsImage(imgPalette) = 0
      imgPalette = CreateImage(#PB_Any,Xmax,Ymax,24,0)
   Else
      ResizeImage(imgPalette,Xmax,Ymax)
   EndIf
   
   StartDrawing(ImageOutput(imgPalette))
      Xmax - 1 : Ymax - 1
      For y = 0 To Ymax
         For x = 0 To Xmax
            c = (x ! y)
            c = (c << 16) | (c << 8) | c
            Plot(x, y, c | $C0C0C0)
         Next
      Next
      X = 0 : Y = 0
      For I = 0 To Imax
         Box(X, Y, blockSize, blockSize, palette(I))
         X + blockSize
         If X > Xmax : X = 0 : Y + blockSize : EndIf
      Next
   StopDrawing()

   SetGadgetState(imgGad,ImageID(imgPalette))
   
EndProcedure
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
yyh
New User
New User
Posts: 3
Joined: Wed Aug 19, 2015 9:43 am

Re: Color Quantizing Image Editor

Post by yyh »

Thank you so much!
Much appreciated! Now I only need to worry on comparing images.
Thanks again!
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Color Quantizing Image Editor

Post by davido »

@yyh,
In your quest to compare pictures did you chance upon this little gem by wilbert:
http://www.purebasic.fr/english/viewtop ... 31#p433184
DE AA EB
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

Version 2.0 is now available for download.
See the first posting in this topic.

Version 2.0 has many changes and improvements.
The GUI has been completely redesigned.
Histogram tool is improved.
Brightness/Contrast have been greatly improved.
New effects have been added.
Edits can be performed on a selected area or the entire image.
Now supports unlimited undo/redo actions.

Bug reports are welcome.

I have not given the source code for this version, only the compiled .exe files.
I request the assistance from any Mac user who would be willing to compile a Mac version of this.
Please use the latest version of PureBasic. x86, x64 or both.
I can PM you a link where you can download all of the required files you would need to do a compile.

thanks.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
applePi
Addict
Addict
Posts: 1404
Joined: Sun Jun 25, 2006 7:28 pm

Re: Color Quantizing Image Editor

Post by applePi »

Thank you BasicallyPure, very useful utility, i always test a utility like this one with the shroud of tourin picture, and applying various digital filters to it
https://commons.wikimedia.org/wiki/File ... in_001.jpg
https://en.wikipedia.org/wiki/Shroud_of ... _94_KB.jpg
i just find the picture interesting as a pure scientific topic even if it is fake (or may be not)
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4636
Joined: Sun Apr 12, 2009 6:27 am

Re: Color Quantizing Image Editor

Post by RASHAD »

OK BP
You did a very good job except for one thing
Both versions for windows to download are x86 :P

I wish I have the source :D
Egypt my love
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

RASHAD wrote:Both versions for windows to download are x86
Fixed. Thank you for pointing that out.

For your reward check the PM I sent you. :)
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: Color Quantizing Image Editor

Post by davido »

@BasicallyPure,

I would be pleased to check it on my MacBook if you could provide the code to compile.
DE AA EB
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

davido wrote:I would be pleased to check it on my MacBook if you could provide the code to compile.
Hi davido, I just sent you a PM that links to the code.
Thank you.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 536
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: Color Quantizing Image Editor

Post by BasicallyPure »

Thanks to davido a download for Mac users is now available.
(see the first post in this topic) http://www.purebasic.fr/english/viewtop ... 25#p445225

Mac users please report any problems, thanks.

All users,
Check out the new Brightness/Contrast adjustment tool.
Several options are available for brightness adjust.
The default settings are usually the best.
Use of the Histogram tool is advised when making brightness/contrast adjustments.
Remember when color values are pushed beyond the upper/lower limits information is lost.
The appearance of many images can be improved by increasing contrast.
It may be necessary however to first reduce brightness so when the contrast is applied,
the color values are not pushed up to the upper limit. (this would be the right side of the histogram)
Sometimes it is best to use the selector tool to display the histogram only in a specific region of the image.

This latest version has some new quantization options.
TruColor_256 fixed palette, an 8-bit palette where 3 bits are for red and green, and 2 bits for blue.
Human_256 fixed palette, a hand-picked 8-bit palette chosen by me for good results with human skin tones.

I have a few things in mind for future versions of CQ_ImageEditor.
1. add zoom feature.
2. add full screen viewing option.
3. add ability to adjust image using HSV color space method.
4. add colorize option to apply a tint to grayscale images (or color images for that matter).
5. add a custom palette maker tool.
6. add drawing options such as lines and polygons.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
mpz
Enthusiast
Enthusiast
Posts: 494
Joined: Sat Oct 11, 2008 9:07 pm
Location: Germany, Berlin > member German forum

Re: Color Quantizing Image Editor

Post by mpz »

Hi BasicallyPure,

nice project and thanks for the program. I am searching for a programm to create a HDR image from (for example) three images. Do you thing this can be a good idea for a future of you program to make a Tone-Mapping function and then a function to addition for HDR?

http://www.wikihow.com/Create-High-Dyna ... hotographs

Greetings Michael
Working on - MP3D Library - PB 5.73 version ready for download
Post Reply