Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calculate dominant colour #640

Closed
refo opened this issue Dec 1, 2016 · 23 comments
Closed

Calculate dominant colour #640

refo opened this issue Dec 1, 2016 · 23 comments

Comments

@refo
Copy link

refo commented Dec 1, 2016

Is it possible to extract prominent colours or single dominant colour using sharp ?

@lovell
Copy link
Owner

lovell commented Dec 1, 2016

Hello Refik, thanks for raising this topic here. I've been toying with the idea of adding a "stats" feature to expose various calculations based on pixel values and this feature would fit in that category.

This could include any derived data where all the pixel values have to be decompressed so will be much slower than inspecting the uncompressed header data.

We can probably use the histogram binning features of libvips for dominant colours, although I think we'd need to perform quantisation of these values to make them useful.

In the meantime, for those who like to live dangerously, there's the unsupported https://github.com/lovell/attention

@rn4391
Copy link

rn4391 commented Dec 16, 2016

This can be done in conjunction with the smartcrop.js module (basically edge detection) to first identify the most important part of the image. This will eliminate cases where a single background colour dominates.

@Shuunen
Copy link

Shuunen commented Mar 7, 2017

Would be great to have this feature 👍

Is there any trick to achieve this with current sharp features ?

@ile
Copy link

ile commented May 12, 2017

Yes, and getting the number of colors would be useful too. (For example for distinguishing whether an image is a logo or a photo.)

@homerjam
Copy link

I like the sound of the stats idea. I still rely on attention for focus region and focus point too (mainly point) - it's amazing, I really haven't found a comparable alternative. It'd be great to see these things ported under utilities perhaps?

I noticed sharp now uses libvips internal cropping methods - does this mean region/point could be exposed somehow?

@lovell
Copy link
Owner

lovell commented Jun 20, 2017

@homerjam Yes, we could now use vips_smartcrop for a smart extract, where only a width and height are required as input params and the top/left coords of the crop are made available as output parameters. If you're interested, please create a new issue and we can use that to track progress.

@coffeebite
Copy link

Hey @lovell Is there an update on this? How would one go about finding, say, the top 3 colors of an image?

@medanat
Copy link

medanat commented Jan 25, 2018

The way I did it with stats:

  sharp(buffer)
    .stats()
    .then(({ channels: [rc, gc, bc] }) => {
      const r = Math.round(rc.mean),
            g = Math.round(gc.mean),
            b = Math.round(bc.mean);

    ... do things with r, g, b
  });

@prewk
Copy link
Contributor

prewk commented Jan 25, 2018

@medanat I'm not sure what kind of result you're after but mean is not a useful measure of prominence/domination. Check out http://jariz.github.io/vibrant.js/ and you'll see what I assume people in this thread are after.

I "solved" the problem by running it through https://github.com/akfish/node-vibrant but I'd love a sharp solution for it!

@medanat
Copy link

medanat commented Jan 25, 2018

That was my solution for mimicking the dominant colour functionality of https://github.com/lovell/attention.

In my tests the previous snippet yielded similar or very close results to:

attention('input.jpg')
  .swatches(1)
  .palette(function(err, palette) {
    palette.swatches.forEach(function(swatch) {
      console.dir(swatch);
    });
  });

where the swatch result would give back rgb results. I'm curious to see how else I can approach this.

@jacktuck
Copy link

Any update? <3

@jacktuck
Copy link

jacktuck commented Jul 11, 2018

A coworker gave me this idea as a workaround, i think it works, just not sure about its efficacy versus, say, using attention library.

Note: pipeline is a sharp instance.

function getDominantColor (pipeline) {
  return pipeline
    .resize(5, 5)
    .crop(sharp.strategy.attention)
    .toBuffer()
    .then(buffer => {
      return sharp(buffer)
        .stats()
        .then(stats => {
          measure()
          console.log('getDominantColor stats', stats)

          const { channels: [r, g, b] } = stats

          return [
            Math.round(r.max),
            Math.round(g.max),
            Math.round(b.max)
          ]
        })
    })
}

@MikeRock
Copy link

MikeRock commented Nov 26, 2018

Hi. I've been having to the same problem and if you want to get the dominant color returned as an image a simple solution is just setting max blur on it.

const buffer = await new Promise((resolve, reject) => {
      sharp(path.resolve(__dirname,'image.jpg'))
      .resize(3, 3, {
        fit: 'cover',
        position: sharp.strategy.entropy,
      })
      .blur(1000)
      .toBuffer((err, buffer, info) => {
        if (err) reject(err)
        resolve(buffer.toString('base64'))
      })
    })

@prewk
Copy link
Contributor

prewk commented Nov 27, 2018

That doesn't sound very dominant? Sounds more like an avarage value. Check out https://jariz.github.io/vibrant.js/ for a better grasp of what's "dominant" (it's got a very subjective definition.)

@kapouer
Copy link
Contributor

kapouer commented Dec 23, 2019

This vips issue might be helpful: libvips/libvips#259

@polarathene
Copy link

color-thief functionality looks to be what I'd be interested in if possible to support.Uses quantization producing a list(~20 elements) of colours ordered by count, with dominant being the most used colour in the image.

In my use-case it's for a placeholder background colour that the source image would have a fade-in transition effect on. Looking at vibrant.js it doesn't seem to an appropriate/reliable type for getting that colour result.

@lovell
Copy link
Owner

lovell commented Jul 15, 2020

Commit c42de19 adds the following API, which uses the 3D histogram technique with 4096 (16^3) RGB bins.

const { dominant } = await sharp(input).stats();
const { r, g, b } = dominant;

This will be in v0.26.0.

@lovell lovell added this to the v0.26.0 milestone Jul 15, 2020
@lovell lovell changed the title Extracting prominent colours Calculate dominant colour Jul 15, 2020
@fgilio
Copy link

fgilio commented Jul 16, 2020

Thanks!

@lovell
Copy link
Owner

lovell commented Aug 25, 2020

v0.26.0 now available.

@lovell lovell closed this as completed Aug 25, 2020
@PlopTheReal
Copy link

Is there is a way to get a palette as I might need to ignore some dominant color (mostly white or black)?

hail2u added a commit to hail2u/hail2u.net that referenced this issue Oct 10, 2020
@nunowar
Copy link

nunowar commented Feb 21, 2022

Is possible to extract the top 5 dominant colors from the image with Sharp? Using the stats.dominant I just get one color. I need to get the Palette as https://jariz.github.io/vibrant.js/ does. This is possible with Sharp?

@prewk
Copy link
Contributor

prewk commented Feb 22, 2022

Is possible to extract the top 5 dominant colors from the image with Sharp? Using the stats.dominant I just get one color. I need to get the Palette as https://jariz.github.io/vibrant.js/ does. This is possible with Sharp?

If you read the thread you'll find the answers

@Aminelahlou
Copy link

Hello everyone, I am using sharp for printing textiles and am also intersted in making palette. I have played with some algorithms for making palettes. One of the algorithms I found very interesting is the octree color quantization: https://observablehq.com/@tmcw/octree-color-quantization.
I think it would perfectly fit the problem detailed by the author (except I managed to make only 4 dominant color. the algorithm is a bit complex to change for me to make it supports 20 differents colors.

With this algorithm, do you think it could be possible to modulate with brigthness, saturation, and hue each pixel with different parameters depending on the distance of pixel that need to be changed with the color palette?

Exemple:
I have image I with 700 000 pixels. I found 3 main colors [{r:255,g:100,b:50},{r:0,g:30,b:50},{r:70,g:90,b:150}].

I then calculate the squared distance of the original pixel {{r:30,g:55,b:90}with each palette color: distances=[(255-30)^2+(100-55)^2+(50-90)^2,(0-30)^2+(30-55)^2+(50-90)^2,(70-30)^2+(90-55)^2+(150-90)^2]which equals to [54250,3125,6425] which means the second color from the palette is near the original pixel:

I then apply a modulate function depending on specific parameters [{hue: 50}, {hue:90,saturation:1.05},{hue:40}] since the second pixel from the palette is nearest I apply those parameters {hue:90,saturation:1.05} onto the pixel.

Does sharp supports this types of operations?

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

No branches or pull requests