Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

5 Commits

Repository files navigation

quantize demo cover

Quantize Image Methods

Here you'll find different methods to quantize images in Python using clusterisation. Choose your favorite!

All of the functions used are not minimal examples of the different library's method they are using, the time measurements should be looked at with caution and only in the context of image quantization.

All time measurements where taken for 10 successive iterations of a method. Unless otherwise specified, the licenses of the used libraries are freely available for commercial use. The code used to reproduce all results is available in this file.

For the photos used for the tests, I tried to select sufficiently diverse ones to really show the specificity to be expected for each methods. The authors of the photos are Hanif Mahmad (for the first 5) and Simon Launay (for the rest).

buildingcity sunsetforest panoramamini old lighthouse
red chairwalking monkscoloured tiles paintingbusy Japan street
cycling womanchild portraitdark tunnelman in train window
desert roadtree flower closeupchildren playgroundgirl portrait

The images where all reduced to a maximum size of 500px (width or height) before quantization (included in time measurement), except specified ones which needed an even smaller size to get back any kind of result. And without further ado, here are the results:

Pillow

Pillow is a rework of the PIL imaging library ; it is the only one of the contestants containing a function dedicated to quantizing. The function takes a method parameter which can be any one of the following quantizing methods: median cut, maximum coverage, fast octree or the custom algorithm of libimagequant, but this one is an external, either GPL or commercial dependency, and to be able to use it you need to rebuild Pillow yourself while including it ; I didn't test it. Pillow's quantize method require the precise number of colours that you want in the resulting image. What I did to choose that number was to iterate over the quantize method, reducing the number of colours until there was none left covering less that 5% of the image. Although the quantization is applied several times, the method is stilll the quickest by far, as you'll see next. It is also the only method not using the cv2.resize to reduce the image size ; instead it uses Pillow's Image.thumbnail.

Median cut

pillow_median_cut image buildingpillow_median_cut image city sunsetpillow_median_cut image forest panoramapillow_median_cut image mini old lighthouse
pillow_median_cut colours buildingpillow_median_cut colours city sunsetpillow_median_cut colours forest panoramapillow_median_cut colours mini old lighthouse
pillow_median_cut image red chairpillow_median_cut image walking monkspillow_median_cut image coloured tiles paintingpillow_median_cut image busy Japan street
pillow_median_cut colours red chairpillow_median_cut colours walking monkspillow_median_cut colours coloured tiles paintingpillow_median_cut colours busy Japan street
pillow_median_cut image cycling womanpillow_median_cut image child portraitpillow_median_cut image dark tunnelpillow_median_cut image man in train window
pillow_median_cut colours cycling womanpillow_median_cut colours child portraitpillow_median_cut colours dark tunnelpillow_median_cut colours man in train window
pillow_median_cut image desert roadpillow_median_cut image tree flower closeuppillow_median_cut image children playgroundpillow_median_cut image girl portrait
pillow_median_cut colours desert roadpillow_median_cut colours tree flower closeuppillow_median_cut colours children playgroundpillow_median_cut colours girl portrait

pillow_median_cut time : 2.677708653 s

The results are not bad, but you lose vivid colours that are not covering a lot of the image, like the monks' clothing or the road yellow line.

Maximum coverage

pillow_maximum_coverage image buildingpillow_maximum_coverage image city sunsetpillow_maximum_coverage image forest panoramapillow_maximum_coverage image mini old lighthouse
pillow_maximum_coverage colours buildingpillow_maximum_coverage colours city sunsetpillow_maximum_coverage colours forest panoramapillow_maximum_coverage colours mini old lighthouse
pillow_maximum_coverage image red chairpillow_maximum_coverage image walking monkspillow_maximum_coverage image coloured tiles paintingpillow_maximum_coverage image busy Japan street
pillow_maximum_coverage colours red chairpillow_maximum_coverage colours walking monkspillow_maximum_coverage colours coloured tiles paintingpillow_maximum_coverage colours busy Japan street
pillow_maximum_coverage image cycling womanpillow_maximum_coverage image child portraitpillow_maximum_coverage image dark tunnelpillow_maximum_coverage image man in train window
pillow_maximum_coverage colours cycling womanpillow_maximum_coverage colours child portraitpillow_maximum_coverage colours dark tunnelpillow_maximum_coverage colours man in train window
pillow_maximum_coverage image desert roadpillow_maximum_coverage image tree flower closeuppillow_maximum_coverage image children playgroundpillow_maximum_coverage image girl portrait
pillow_maximum_coverage colours desert roadpillow_maximum_coverage colours tree flower closeuppillow_maximum_coverage colours children playgroundpillow_maximum_coverage colours girl portrait

pillow_maximum_coverage time : 1.9332736510000004 s

This method gives really interesting results. Objects are identifiable but some colours are really transformed and the number of colour might be to small for some. The advantage is that you get no colour resembling each other in the colours extraction

Fast octree

pillow_fast_octree image buildingpillow_fast_octree image city sunsetpillow_fast_octree image forest panoramapillow_fast_octree image mini old lighthouse
pillow_fast_octree colours buildingpillow_fast_octree colours city sunsetpillow_fast_octree colours forest panoramapillow_fast_octree colours mini old lighthouse
pillow_fast_octree image red chairpillow_fast_octree image walking monkspillow_fast_octree image coloured tiles paintingpillow_fast_octree image busy Japan street
pillow_fast_octree colours red chairpillow_fast_octree colours walking monkspillow_fast_octree colours coloured tiles paintingpillow_fast_octree colours busy Japan street
pillow_fast_octree image cycling womanpillow_fast_octree image child portraitpillow_fast_octree image dark tunnelpillow_fast_octree image man in train window
pillow_fast_octree colours cycling womanpillow_fast_octree colours child portraitpillow_fast_octree colours dark tunnelpillow_fast_octree colours man in train window
pillow_fast_octree image desert roadpillow_fast_octree image tree flower closeuppillow_fast_octree image children playgroundpillow_fast_octree image girl portrait
pillow_fast_octree colours desert roadpillow_fast_octree colours tree flower closeuppillow_fast_octree colours children playgroundpillow_fast_octree colours girl portrait

pillow_fast_octree time : 0.47265442100000143 s

This method is by far the fastest one, but the results are really noisy and you lose a lot of vivid colours, even more than with Median cut.

OpenCV

OpenCV is a famous library containing utility functions for computer vision. For these next set of functions, we are using the cv2.kmeans because the quantization problem can be in fact redefined as a clusterisation problem. Three colour space will be tested, to see if we get better results in colour space where classic distance computation should have a better meaning. Like the Pillow methods, the function is iterated until we get only colours that are sufficiently present in the image. The threshold is 1% instead of 5% here, because we didn't get enough colours otherwise.

RGB

opencv_rgb image buildingopencv_rgb image city sunsetopencv_rgb image forest panoramaopencv_rgb image mini old lighthouse
opencv_rgb colours buildingopencv_rgb colours city sunsetopencv_rgb colours forest panoramaopencv_rgb colours mini old lighthouse
opencv_rgb image red chairopencv_rgb image walking monksopencv_rgb image coloured tiles paintingopencv_rgb image busy Japan street
opencv_rgb colours red chairopencv_rgb colours walking monksopencv_rgb colours coloured tiles paintingopencv_rgb colours busy Japan street
opencv_rgb image cycling womanopencv_rgb image child portraitopencv_rgb image dark tunnelopencv_rgb image man in train window
opencv_rgb colours cycling womanopencv_rgb colours child portraitopencv_rgb colours dark tunnelopencv_rgb colours man in train window
opencv_rgb image desert roadopencv_rgb image tree flower closeupopencv_rgb image children playgroundopencv_rgb image girl portrait
opencv_rgb colours desert roadopencv_rgb colours tree flower closeupopencv_rgb colours children playgroundopencv_rgb colours girl portrait

opencv_rgb time : 32.54811209199988 s

Results are really good, the kmeans does really well at extracting the most important colours. The default is that the time taken took a big leap for the worst compared to Pillow's methods.

HSV

opencv_hsv image buildingopencv_hsv image city sunsetopencv_hsv image forest panoramaopencv_hsv image mini old lighthouse
opencv_hsv colours buildingopencv_hsv colours city sunsetopencv_hsv colours forest panoramaopencv_hsv colours mini old lighthouse
opencv_hsv image red chairopencv_hsv image walking monksopencv_hsv image coloured tiles paintingopencv_hsv image busy Japan street
opencv_hsv colours red chairopencv_hsv colours walking monksopencv_hsv colours coloured tiles paintingopencv_hsv colours busy Japan street
opencv_hsv image cycling womanopencv_hsv image child portraitopencv_hsv image dark tunnelopencv_hsv image man in train window
opencv_hsv colours cycling womanopencv_hsv colours child portraitopencv_hsv colours dark tunnelopencv_hsv colours man in train window
opencv_hsv image desert roadopencv_hsv image tree flower closeupopencv_hsv image children playgroundopencv_hsv image girl portrait
opencv_hsv colours desert roadopencv_hsv colours tree flower closeupopencv_hsv colours children playgroundopencv_hsv colours girl portrait

opencv_hsv time : 50.326093940999954 s

Since HSV is not really supposed to be a good fit for colour distancing, I expected much worse results. Some colours have a wrong hue, but overall it's not that bad. RGB is still more faithful and take less time, so you shouldn't use HSV.

LAB

opencv_lab image buildingopencv_lab image city sunsetopencv_lab image forest panoramaopencv_lab image mini old lighthouse
opencv_lab colours buildingopencv_lab colours city sunsetopencv_lab colours forest panoramaopencv_lab colours mini old lighthouse
opencv_lab image red chairopencv_lab image walking monksopencv_lab image coloured tiles paintingopencv_lab image busy Japan street
opencv_lab colours red chairopencv_lab colours walking monksopencv_lab colours coloured tiles paintingopencv_lab colours busy Japan street
opencv_lab image cycling womanopencv_lab image child portraitopencv_lab image dark tunnelopencv_lab image man in train window
opencv_lab colours cycling womanopencv_lab colours child portraitopencv_lab colours dark tunnelopencv_lab colours man in train window
opencv_lab image desert roadopencv_lab image tree flower closeupopencv_lab image children playgroundopencv_lab image girl portrait
opencv_lab colours desert roadopencv_lab colours tree flower closeupopencv_lab colours children playgroundopencv_lab colours girl portrait

opencv_lab time : 34.00670318699986 s

Lab should theoretically give better result than RGB, being a colour space created to give better distance measurement. But the RGB already gives good results, and I don't see any visible improvement by using Lab. Time taken also is similar, so you can probably use either one. You should theoretically get better results by using Delta E 2000 instead of euclidian distance, but openCV doesn't give us the possibility to change the distance function. Based upon these results, all following methods will use RGB colour space.

SciPy

SciPy is a library for scientific computing. Like OpenCV, it contains a function for KMeans, and that's what I used. Unlike OpenCV, the function can be divided in two parts: the first find the centroids (fit), the second create the pixel clusters using those (predict). The threshold of the minimum colour presence percentage was set at 2% which produced not too much nor too little colours.

Without optimisation trick

scipy image buildingscipy image city sunsetscipy image forest panoramascipy image mini old lighthouse
scipy colours buildingscipy colours city sunsetscipy colours forest panoramascipy colours mini old lighthouse
scipy image red chairscipy image walking monksscipy image coloured tiles paintingscipy image busy Japan street
scipy colours red chairscipy colours walking monksscipy colours coloured tiles paintingscipy colours busy Japan street
scipy image cycling womanscipy image child portraitscipy image dark tunnelscipy image man in train window
scipy colours cycling womanscipy colours child portraitscipy colours dark tunnelscipy colours man in train window
scipy image desert roadscipy image tree flower closeupscipy image children playgroundscipy image girl portrait
scipy colours desert roadscipy colours tree flower closeupscipy colours children playgroundscipy colours girl portrait

scipy time : 524.6095604020002 s

We can see that the result is similar to those of the Median cut of Pillow: you lose some vivid minority colours. But the main problem is the time ; using a threshold of 2% instead of 1%, I expected the function to take more time than the OpenCV equivalent. But this is just ridiculous and unusable. Fortunately, what you'll see next is a trick to get similar results in much less time.

With optimisation trick

Since the quantization is done in two steps, we can compute the centroids on a randomized shuffle subset of the original image, and then apply the model to the whole image.

scipy2 image buildingscipy2 image city sunsetscipy2 image forest panoramascipy2 image mini old lighthouse
scipy2 colours buildingscipy2 colours city sunsetscipy2 colours forest panoramascipy2 colours mini old lighthouse
scipy2 image red chairscipy2 image walking monksscipy2 image coloured tiles paintingscipy2 image busy Japan street
scipy2 colours red chairscipy2 colours walking monksscipy2 colours coloured tiles paintingscipy2 colours busy Japan street
scipy2 image cycling womanscipy2 image child portraitscipy2 image dark tunnelscipy2 image man in train window
scipy2 colours cycling womanscipy2 colours child portraitscipy2 colours dark tunnelscipy2 colours man in train window
scipy2 image desert roadscipy2 image tree flower closeupscipy2 image children playgroundscipy2 image girl portrait
scipy2 colours desert roadscipy2 colours tree flower closeupscipy2 colours children playgroundscipy2 colours girl portrait

scipy2 time : 6.44186058299988 s

As you can see, the results are pretty much the same as those without optimisation, for a much more reasonable computation time, even much better than OpenCV.

Scikit-learn

Scikit-learn is specialised in machine learning tools (classification, regression and clustering). We will use KMeans with same parameters and process as those used with SciPy, and we will try some other clusterisation functions available.

KMeans without optimisation trick

sklearn_kmean image buildingsklearn_kmean image city sunsetsklearn_kmean image forest panoramasklearn_kmean image mini old lighthouse
sklearn_kmean colours buildingsklearn_kmean colours city sunsetsklearn_kmean colours forest panoramasklearn_kmean colours mini old lighthouse
sklearn_kmean image red chairsklearn_kmean image walking monkssklearn_kmean image coloured tiles paintingsklearn_kmean image busy Japan street
sklearn_kmean colours red chairsklearn_kmean colours walking monkssklearn_kmean colours coloured tiles paintingsklearn_kmean colours busy Japan street
sklearn_kmean image cycling womansklearn_kmean image child portraitsklearn_kmean image dark tunnelsklearn_kmean image man in train window
sklearn_kmean colours cycling womansklearn_kmean colours child portraitsklearn_kmean colours dark tunnelsklearn_kmean colours man in train window
sklearn_kmean image desert roadsklearn_kmean image tree flower closeupsklearn_kmean image children playgroundsklearn_kmean image girl portrait
sklearn_kmean colours desert roadsklearn_kmean colours tree flower closeupsklearn_kmean colours children playgroundsklearn_kmean colours girl portrait

sklearn_kmeans time : 292.4821637319999 s

The time is once again ridiculously long, but since we can use the same trick as with Scipy, let's see the results with it.

KMeans with optimisation trick

sklearn_kmean2 image buildingsklearn_kmean2 image city sunsetsklearn_kmean2 image forest panoramasklearn_kmean2 image mini old lighthouse
sklearn_kmean2 colours buildingsklearn_kmean2 colours city sunsetsklearn_kmean2 colours forest panoramasklearn_kmean2 colours mini old lighthouse
sklearn_kmean2 image red chairsklearn_kmean2 image walking monkssklearn_kmean2 image coloured tiles paintingsklearn_kmean2 image busy Japan street
sklearn_kmean2 colours red chairsklearn_kmean2 colours walking monkssklearn_kmean2 colours coloured tiles paintingsklearn_kmean2 colours busy Japan street
sklearn_kmean2 image cycling womansklearn_kmean2 image child portraitsklearn_kmean2 image dark tunnelsklearn_kmean2 image man in train window
sklearn_kmean2 colours cycling womansklearn_kmean2 colours child portraitsklearn_kmean2 colours dark tunnelsklearn_kmean2 colours man in train window
sklearn_kmean2 image desert roadsklearn_kmean2 image tree flower closeupsklearn_kmean2 image children playgroundsklearn_kmean2 image girl portrait
sklearn_kmean2 colours desert roadsklearn_kmean2 colours tree flower closeupsklearn_kmean2 colours children playgroundsklearn_kmean2 colours girl portrait

sklearn_kmean2 time : 7.649859192999884 s

Like SciPy, we managed to have similar result for a much better time. Unfortunately, there is one image, the road one, where the result of the quantization is actually visibly worse by using the optimisation, but such case should be rare.

MiniBatchKMeans

MiniBatchKMeans is a variant of KMeans meant to take less computation time for a similar result. Since we have already seen that quantizing on a random sample works relatively well, this function directly use the optimised version.

sklearn_mini_batch_kmeans image buildingsklearn_mini_batch_kmeans image city sunsetsklearn_mini_batch_kmeans image forest panoramasklearn_mini_batch_kmeans image mini old lighthouse
sklearn_mini_batch_kmeans colours buildingsklearn_mini_batch_kmeans colours city sunsetsklearn_mini_batch_kmeans colours forest panoramasklearn_mini_batch_kmeans colours mini old lighthouse
sklearn_mini_batch_kmeans image red chairsklearn_mini_batch_kmeans image walking monkssklearn_mini_batch_kmeans image coloured tiles paintingsklearn_mini_batch_kmeans image busy Japan street
sklearn_mini_batch_kmeans colours red chairsklearn_mini_batch_kmeans colours walking monkssklearn_mini_batch_kmeans colours coloured tiles paintingsklearn_mini_batch_kmeans colours busy Japan street
sklearn_mini_batch_kmeans image cycling womansklearn_mini_batch_kmeans image child portraitsklearn_mini_batch_kmeans image dark tunnelsklearn_mini_batch_kmeans image man in train window
sklearn_mini_batch_kmeans colours cycling womansklearn_mini_batch_kmeans colours child portraitsklearn_mini_batch_kmeans colours dark tunnelsklearn_mini_batch_kmeans colours man in train window
sklearn_mini_batch_kmeans image desert roadsklearn_mini_batch_kmeans image tree flower closeupsklearn_mini_batch_kmeans image children playgroundsklearn_mini_batch_kmeans image girl portrait
sklearn_mini_batch_kmeans colours desert roadsklearn_mini_batch_kmeans colours tree flower closeupsklearn_mini_batch_kmeans colours children playgroundsklearn_mini_batch_kmeans colours girl portrait

sklearn_mini_batch_kmeans time : 74.64295504700002 s

Contrarily to what is promised, we actually have worse time performance by using MiniBatchKMeans rather than KMeans, even though the rest of the implementation of the function is exactly the same. Aside from that, the images result are similar, and even better for a few, to those of KMeans.

MeanShift

MeanShift tries to discover "blobs" of data, and in a second step regroups together blobs that are near-duplicates. Because of its implementation, there is no need to give this method the number of colours we want, it will chose it. Since this method can be done in two steps (fit then predict), we will once again use a random sample for the fitting to reduce computation time.

sklearn_mean_shift image buildingsklearn_mean_shift image city sunsetsklearn_mean_shift image forest panoramasklearn_mean_shift image mini old lighthouse
sklearn_mean_shift colours buildingsklearn_mean_shift colours city sunsetsklearn_mean_shift colours forest panoramasklearn_mean_shift colours mini old lighthouse
sklearn_mean_shift image red chairsklearn_mean_shift image walking monkssklearn_mean_shift image coloured tiles paintingsklearn_mean_shift image busy Japan street
sklearn_mean_shift colours red chairsklearn_mean_shift colours walking monkssklearn_mean_shift colours coloured tiles paintingsklearn_mean_shift colours busy Japan street
sklearn_mean_shift image cycling womansklearn_mean_shift image child portraitsklearn_mean_shift image dark tunnelsklearn_mean_shift image man in train window
sklearn_mean_shift colours cycling womansklearn_mean_shift colours child portraitsklearn_mean_shift colours dark tunnelsklearn_mean_shift colours man in train window
sklearn_mean_shift image desert roadsklearn_mean_shift image tree flower closeupsklearn_mean_shift image children playgroundsklearn_mean_shift image girl portrait
sklearn_mean_shift colours desert roadsklearn_mean_shift colours tree flower closeupsklearn_mean_shift colours children playgroundsklearn_mean_shift colours girl portrait

sklearn_mean_shift time : 52.089231103 s

The resulting images are quite good, even if a lot of them are a bit to minimalistic in colours (except for the tunnel one which is surprisingly and uselessly detailed). Time taken is quite long however.

Pyclustering

Pyclustering, like its name implies, is specialised in clusterisation, but it uses a GPL license. With it we are able to test several different cluster functions aside from K-Means. CCORE included library is used to get better performances.

BSAS

BSAS use a maximum allowable number of clusters and a threshold of dissimilarity as parameters, as well as the distance function to be used. The number of clusters is then decided by the algorithm, and we don't need to loop and check pixel percentage coverage ourselves.

pycluster_bsas image buildingpycluster_bsas image city sunsetpycluster_bsas image forest panoramapycluster_bsas image mini old lighthouse
pycluster_bsas colours buildingpycluster_bsas colours city sunsetpycluster_bsas colours forest panoramapycluster_bsas colours mini old lighthouse
pycluster_bsas image red chairpycluster_bsas image walking monkspycluster_bsas image coloured tiles paintingpycluster_bsas image busy Japan street
pycluster_bsas colours red chairpycluster_bsas colours walking monkspycluster_bsas colours coloured tiles paintingpycluster_bsas colours busy Japan street
pycluster_bsas image cycling womanpycluster_bsas image child portraitpycluster_bsas image dark tunnelpycluster_bsas image man in train window
pycluster_bsas colours cycling womanpycluster_bsas colours child portraitpycluster_bsas colours dark tunnelpycluster_bsas colours man in train window
pycluster_bsas image desert roadpycluster_bsas image tree flower closeuppycluster_bsas image children playgroundpycluster_bsas image girl portrait
pycluster_bsas colours desert roadpycluster_bsas colours tree flower closeuppycluster_bsas colours children playgroundpycluster_bsas colours girl portrait

pycluster_bsas time : 33.03888442999994 s

The results are interesting, but clearly not adapted for colour quantization. Some images are deformed, hardly recognisable, and some colours are not even the hue they should be. Time taken is equivalent to OpenCV.

MBSAS

MBSAS is similar to BSAS but for some implementation details I won't get into here.

pycluster_mbsas image buildingpycluster_mbsas image city sunsetpycluster_mbsas image forest panoramapycluster_mbsas image mini old lighthouse
pycluster_mbsas colours buildingpycluster_mbsas colours city sunsetpycluster_mbsas colours forest panoramapycluster_mbsas colours mini old lighthouse
pycluster_mbsas image red chairpycluster_mbsas image walking monkspycluster_mbsas image coloured tiles paintingpycluster_mbsas image busy Japan street
pycluster_mbsas colours red chairpycluster_mbsas colours walking monkspycluster_mbsas colours coloured tiles paintingpycluster_mbsas colours busy Japan street
pycluster_mbsas image cycling womanpycluster_mbsas image child portraitpycluster_mbsas image dark tunnelpycluster_mbsas image man in train window
pycluster_mbsas colours cycling womanpycluster_mbsas colours child portraitpycluster_mbsas colours dark tunnelpycluster_mbsas colours man in train window
pycluster_mbsas image desert roadpycluster_mbsas image tree flower closeuppycluster_mbsas image children playgroundpycluster_mbsas image girl portrait
pycluster_mbsas colours desert roadpycluster_mbsas colours tree flower closeuppycluster_mbsas colours children playgroundpycluster_mbsas colours girl portrait

pycluster_mbsas time : 33.16758375900008 s

As expected, image and time results are comparable to those of BSAS.

DBSCAN

pycluster_dbscan image building
pycluster_dbscan colours building

pycluster_dbscan time : 102.73210900899994 s

With DBSCAN, not all pixel are guaranteed to be in a cluster, some samples are considered noisy and not included in a cluster. In an image, you can't just decide a pixel is an outsider and not assign it to a cluster since it needs to have a colour. Therefore you need a lot of clusters to be certain there will be no outsider. Hence why I didn't test this algorithm on all the photos: as well as taking a lot of time, it is clearly not suitable for the quantization problem.

OPTICS

pycluster_optics image building
pycluster_optics colours building

pycluster_optics time : 122.5359561910002 s

OPTICS give similar results as those of DBSCAN which is expected since OPTICS is actually a lower memory consumption alternative to DBSCAN. That's why we also didn't test sklearn.cluster.DBSCAN and sklearn.cluster.OPTICS: they would also gives outsiders.

SyncNet

SyncNEt construct an oscillatory network to do clusterisation. Because of that, it was impossible to use without greatly reducing the size of the image first: 83px maximum (function crashed on my machine on the original 500px maximum size).

pycluster_syncnet image buildingpycluster_syncnet image city sunsetpycluster_syncnet image forest panoramapycluster_syncnet image mini old lighthouse
pycluster_syncnet colours buildingpycluster_syncnet colours city sunsetpycluster_syncnet colours forest panoramapycluster_syncnet colours mini old lighthouse
pycluster_syncnet image red chairpycluster_syncnet image walking monkspycluster_syncnet image coloured tiles paintingpycluster_syncnet image busy Japan street
pycluster_syncnet colours red chairpycluster_syncnet colours walking monkspycluster_syncnet colours coloured tiles paintingpycluster_syncnet colours busy Japan street
pycluster_syncnet image cycling womanpycluster_syncnet image child portraitpycluster_syncnet image dark tunnelpycluster_syncnet image man in train window
pycluster_syncnet colours cycling womanpycluster_syncnet colours child portraitpycluster_syncnet colours dark tunnelpycluster_syncnet colours man in train window
pycluster_syncnet image desert roadpycluster_syncnet image tree flower closeuppycluster_syncnet image children playgroundpycluster_syncnet image girl portrait
pycluster_syncnet colours desert roadpycluster_syncnet colours tree flower closeuppycluster_syncnet colours children playgroundpycluster_syncnet colours girl portrait

pycluster_syncnet time : 334.00758747099985 s

The results are pretty poor, the number of selected colours fluctuates a lot between images and the time taken is much too high event though the image is 6 times smaller than those of the other methods. This algorithm can't be used for quantization.

SYNC-SOM

SYNC-SOM works similarly to SyncNet, with the same problem of needing small images to even function.

pycluster_syncsom image buildingpycluster_syncsom image city sunsetpycluster_syncsom image forest panoramapycluster_syncsom image mini old lighthouse
pycluster_syncsom colours buildingpycluster_syncsom colours city sunsetpycluster_syncsom colours forest panoramapycluster_syncsom colours mini old lighthouse
pycluster_syncsom image red chairpycluster_syncsom image walking monkspycluster_syncsom image coloured tiles paintingpycluster_syncsom image busy Japan street
pycluster_syncsom colours red chairpycluster_syncsom colours walking monkspycluster_syncsom colours coloured tiles paintingpycluster_syncsom colours busy Japan street
pycluster_syncsom image cycling womanpycluster_syncsom image child portraitpycluster_syncsom image dark tunnelpycluster_syncsom image man in train window
pycluster_syncsom colours cycling womanpycluster_syncsom colours child portraitpycluster_syncsom colours dark tunnelpycluster_syncsom colours man in train window
pycluster_syncsom image desert roadpycluster_syncsom image tree flower closeuppycluster_syncsom image children playgroundpycluster_syncsom image girl portrait
pycluster_syncsom colours desert roadpycluster_syncsom colours tree flower closeuppycluster_syncsom colours children playgroundpycluster_syncsom colours girl portrait

pycluster_syncsom time : 1103.5881241590002 s

The results are comparable to those of SyncNet while taking an even greater time to be computed. This algorithm can't be used for quantization.

TTSAS

TTSAS is an alternative to BSAS and MBSAS that uses two thresholds instead of one.

pycluster_ttsas image buildingpycluster_ttsas image city sunsetpycluster_ttsas image forest panoramapycluster_ttsas image mini old lighthouse
pycluster_ttsas colours buildingpycluster_ttsas colours city sunsetpycluster_ttsas colours forest panoramapycluster_ttsas colours mini old lighthouse
pycluster_ttsas image red chairpycluster_ttsas image walking monkspycluster_ttsas image coloured tiles paintingpycluster_ttsas image busy Japan street
pycluster_ttsas colours red chairpycluster_ttsas colours walking monkspycluster_ttsas colours coloured tiles paintingpycluster_ttsas colours busy Japan street
pycluster_ttsas image cycling womanpycluster_ttsas image child portraitpycluster_ttsas image dark tunnelpycluster_ttsas image man in train window
pycluster_ttsas colours cycling womanpycluster_ttsas colours child portraitpycluster_ttsas colours dark tunnelpycluster_ttsas colours man in train window
pycluster_ttsas image desert roadpycluster_ttsas image tree flower closeuppycluster_ttsas image children playgroundpycluster_ttsas image girl portrait
pycluster_ttsas colours desert roadpycluster_ttsas colours tree flower closeuppycluster_ttsas colours children playgroundpycluster_ttsas colours girl portrait

pycluster_ttsas time : 32.75301202599985 s

One again, the result is similar to BSAS, and not adapted to image quantization.

X-Means

X-Means works by giving it the minimum and maximum number of clusters you want and then dynamically grow from the minimum number until the result is satisfying. I put the minimum at 2 clusters because that's the limit for an image not to be blank. You also need to give the method the cluster centers starting position, but pyclustering possess a function, kmeans_plusplus_initializer, to initialise the given number of centers at good starting positions for this family of algorithms.

pycluster_xmeans image buildingpycluster_xmeans image city sunsetpycluster_xmeans image forest panoramapycluster_xmeans image mini old lighthouse
pycluster_xmeans colours buildingpycluster_xmeans colours city sunsetpycluster_xmeans colours forest panoramapycluster_xmeans colours mini old lighthouse
pycluster_xmeans image red chairpycluster_xmeans image walking monkspycluster_xmeans image coloured tiles paintingpycluster_xmeans image busy Japan street
pycluster_xmeans colours red chairpycluster_xmeans colours walking monkspycluster_xmeans colours coloured tiles paintingpycluster_xmeans colours busy Japan street
pycluster_xmeans image cycling womanpycluster_xmeans image child portraitpycluster_xmeans image dark tunnelpycluster_xmeans image man in train window
pycluster_xmeans colours cycling womanpycluster_xmeans colours child portraitpycluster_xmeans colours dark tunnelpycluster_xmeans colours man in train window
pycluster_xmeans image desert roadpycluster_xmeans image tree flower closeuppycluster_xmeans image children playgroundpycluster_xmeans image girl portrait
pycluster_xmeans colours desert roadpycluster_xmeans colours tree flower closeuppycluster_xmeans colours children playgroundpycluster_xmeans colours girl portrait

pycluster_xmeans time : 61.51795905300014 s

Results are quite good, but the algorithm has a tendency to use the maximum number of colours, even on images where you would expect a very low number of colours to be needed, like the tunnel photo. Time taken is twice that of OpenCV's KMeans.

K-Means

Like X-Means, you need to give it the starting centers, for which you can use kmeans_plusplus_initializer. Since pyclustering is slower than openCV and has more utility functions for clusterisation, instead of iterating to find a good number of clusters for each image, we use the Elbow Method to choose for us the optimal number of clusters for each image.

pycluster_kmeans image buildingpycluster_kmeans image city sunsetpycluster_kmeans image forest panoramapycluster_kmeans image mini old lighthouse
pycluster_kmeans colours buildingpycluster_kmeans colours city sunsetpycluster_kmeans colours forest panoramapycluster_kmeans colours mini old lighthouse
pycluster_kmeans image red chairpycluster_kmeans image walking monkspycluster_kmeans image coloured tiles paintingpycluster_kmeans image busy Japan street
pycluster_kmeans colours red chairpycluster_kmeans colours walking monkspycluster_kmeans colours coloured tiles paintingpycluster_kmeans colours busy Japan street
pycluster_kmeans image cycling womanpycluster_kmeans image child portraitpycluster_kmeans image dark tunnelpycluster_kmeans image man in train window
pycluster_kmeans colours cycling womanpycluster_kmeans colours child portraitpycluster_kmeans colours dark tunnelpycluster_kmeans colours man in train window
pycluster_kmeans image desert roadpycluster_kmeans image tree flower closeuppycluster_kmeans image children playgroundpycluster_kmeans image girl portrait
pycluster_kmeans colours desert roadpycluster_kmeans colours tree flower closeuppycluster_kmeans colours children playgroundpycluster_kmeans colours girl portrait

pycluster_kmeans time : 150.11819599299997 s

I think the images results are clearly the best ones we got yet, the Elbow Method is clearly efficient at choosing the appropriate number of colours. The only image where it seems to be missing some colours is the chair one. But the time taken is unfortunately quite high.

K-Medians

K-Medians is similar to K-Means except that it compute medians instead of centroid, making it less vulnerable to outliers.

pycluster_kmedians image buildingpycluster_kmedians image city sunsetpycluster_kmedians image forest panoramapycluster_kmedians image mini old lighthouse
pycluster_kmedians colours buildingpycluster_kmedians colours city sunsetpycluster_kmedians colours forest panoramapycluster_kmedians colours mini old lighthouse
pycluster_kmedians image red chairpycluster_kmedians image walking monkspycluster_kmedians image coloured tiles paintingpycluster_kmedians image busy Japan street
pycluster_kmedians colours red chairpycluster_kmedians colours walking monkspycluster_kmedians colours coloured tiles paintingpycluster_kmedians colours busy Japan street
pycluster_kmedians image cycling womanpycluster_kmedians image child portraitpycluster_kmedians image dark tunnelpycluster_kmedians image man in train window
pycluster_kmedians colours cycling womanpycluster_kmedians colours child portraitpycluster_kmedians colours dark tunnelpycluster_kmedians colours man in train window
pycluster_kmedians image desert roadpycluster_kmedians image tree flower closeuppycluster_kmedians image children playgroundpycluster_kmedians image girl portrait
pycluster_kmedians colours desert roadpycluster_kmedians colours tree flower closeuppycluster_kmedians colours children playgroundpycluster_kmedians colours girl portrait

pycluster_kmedians time : 188.61640517599972 s

The results are both slower and less faithful than K-Means. It might be because when we look at an image, what our brain is doing is actually a mean of each colour area, hence why the colour seems so different when we take the median instead.

Conclusion

benchmarks

Pillow's methods are, by a large margin, the fastest ones. Their image results aren't bad, but they are not the best either. That prize goes to pyclustering's K-Means. But that function is very slow and, although it can probably be speeded up, the pyclustering library is under GPL, which might make it incompatible for some uses. You'll need to take a decision depending on what requirements you have. I hope these samples will help you to reach a decision if you one day need to do image quantization.

Notes:

  • The code used assumes that you are only using RGB images. It will need tweaking to support more image formats, but it won't be hard to do once you've chosen your method.
  • I am not a professional benchmarker, but these time results are so far from each other that my naive implementation is probably enough to get an accurate idea of the time difference magnitude.
  • The constants used as parameters for the different functions might not be the best ones, theses results were just produced to give me an idea of what to expect.
  • This list is of course not exhaustive, don't hesitate to suggest other options to me!

About

Results of colors selected and time taken with several method to quantize images in Python. Choose your favorite!

Resources

Stars

26 stars

Watchers

2 watching

Forks

Releases

Packages

Used by

Contributors

Languages