Skip to content

Instantly share code, notes, and snippets.

@jcupitt
Last active February 15, 2023 16:14
Show Gist options
  • Save jcupitt/ee3afcbb931b41b4d7f4 to your computer and use it in GitHub Desktop.
Save jcupitt/ee3afcbb931b41b4d7f4 to your computer and use it in GitHub Desktop.
find dominant colour in an 8-bit RGB Image with libvips python
#!/usr/bin/python
import sys
from gi.repository import Vips
N_BINS = 10
BIN_SIZE = 256 / N_BINS
im = Vips.Image.new_from_file(sys.argv[1], access = Vips.Access.SEQUENTIAL)
# turn to lab
im = im.colourspace("lab")
# turn to 8-bit unsigned so we can make a histogram
# use 0 - 255 to be -128 - +127 for a/b
# and 0 - 255 for 0 - 100 L
im += [0, 128, 128]
im *= [255.0 / 100, 1, 1]
im = im.cast("uchar")
# make a 3D histogram of the 8-bit LAB image
hist = im.hist_find_ndim(bins = N_BINS)
# find the position of the maximum
v, x, y = hist.maxpos()
# get the pixel at (x, y)
pixel = hist(x, y)
# find the index of the max value in the pixel
band = pixel.index(v)
# scale up for the number of bins
x = x * BIN_SIZE + BIN_SIZE / 2
y = y * BIN_SIZE + BIN_SIZE / 2
band = band * BIN_SIZE + BIN_SIZE / 2
# turn the index back into the LAB colour
L = x * (100.0 / 255)
a = y - 128
b = band - 128
print "dominant colour:"
print " L = ", L
print " a = ", a
print " b = ", b
#!/usr/bin/python
import sys
from gi.repository import Vips
im = Vips.Image.new_from_file(sys.argv[1], access = Vips.Access.SEQUENTIAL)
N_BINS = 10
BIN_SIZE = 256 / N_BINS
# make a 3D histogram of the RGB image ... 10 bins in each axis
hist = im.hist_find_ndim(bins = N_BINS)
# find the position of the maximum
v, x, y = hist.maxpos()
# get the pixel at (x, y)
pixel = hist(x, y)
# find the index of the max value in the pixel
band = pixel.index(v)
print "dominant colour:"
print " R = ", x * BIN_SIZE + BIN_SIZE / 2
print " G = ", y * BIN_SIZE + BIN_SIZE / 2
print " B = ", band * BIN_SIZE + BIN_SIZE / 2
@vinvinod
Copy link

Hi John,

I was wondering, is there any utility function in libvips python, for converting the resulting (L,a,b) value to corresponding (R, G, B) value?

@dkam
Copy link

dkam commented Sep 2, 2018

From https://github.com/jcupitt/libvips/issues/259#issuecomment-214658553

import gi
gi.require_version('Vips', '8.0')
from gi.repository import Vips

value = [50, 2, 2]
from_space = "lab"
to_space = "srgb"

# make a 1x1 pixel image, tag as being in the source colourspace
pixel = Vips.Image.black(1, 1) + value
pixel = pixel.copy(interpretation = from_space)

# transform to dest space
pixel = pixel.colourspace(to_space)

# pull out the pixel from coordinate (0, 0) as an array 
new_value = pixel(0, 0)

print(from_space, " ", value)
print(to_space, " ", new_value)`

@maximal
Copy link

maximal commented Feb 14, 2023

Hi, @jcupitt, I’m messed up a little here, could you please help with getting not the most used color, but N most used colors?

@jcupitt
Copy link
Author

jcupitt commented Feb 14, 2023

Hi, you can use the size flag to max to get it to find the N largest values.

@maximal
Copy link

maximal commented Feb 15, 2023

@jcupitt, I use max(), but it returns a single result... Where could I be wrong?

const BINS = 20;
const BIN_SIZE = 256 / BINS;
const PALETTE_MAX_COLORS = 10;
// load an image, get fields, process, save
$image = Vips\Image::newFromFile($argv[1] ?? '2.jpg');
echo "width×height = {$image->width}×{$image->height}\n";

// Lab?
//$image = $image->colourspace('lab');
//$image = $image->cast('uchar');

$histogram = $image->hist_find_ndim(['bins' => BINS]);
$max = $histogram->max(['x' => true, 'y' => true, 'size' => PALETTE_MAX_COLORS]);

var_dump($max);

yields

width×height = 600×450
array(3) {
  ["out"]=>
  float(11888)
  ["x"]=>
  int(3)
  ["y"]=>
  int(2)
}

@jcupitt
Copy link
Author

jcupitt commented Feb 15, 2023

You need to ask for the output array, eg.:

john@banana ~/pics $ python3
Python 3.10.7 (main, Nov 24 2022, 19:45:47) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyvips
>>> x = pyvips.Image.new_from_file("k2.jpg")
>>> x.max(size=12)
255.0
>>> x.max(size=12, out_array=True)
[255.0, {'out_array': [255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0]}]

You can get the positions of the maxima like this:

>>> x.max(size=12, out_array=True, x_array=True, y_array=True)
[255.0, {'out_array': [255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0, 255.0], 'x_array': [376, 377, 378, 378, 379, 385, 386, 387, 390, 391, 391, 392], 'y_array': [2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2032]}]

@maximal
Copy link

maximal commented Feb 15, 2023

@jcupitt, you helped a lot, I really appreciate it! Maybe we’ll continue in the discussion?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment