通过Javascript获取图像的平均颜色


118

不确定是否可行,但希望编写一个脚本来返回图像的平均值hexrgb值。我知道可以在AS中完成,但希望在JavaScript中完成。


19
我的朋友Boaz以前一直走这条路(希望他会鸣叫),但平均而言,它通常会给您留下类似呕吐物的颜色。您真正想要的是“显色”。有几种方法可以做到这一点(色调直方图/动态存储等),但我认为这可能更符合您的预期结果。
保罗·爱尔兰

Answers:


149

AFAIK,做到这一点的唯一方法是<canvas/>...

演示版V2http//jsfiddle.net/xLF38/818/

请注意,这仅适用于相同域上的图像以及支持HTML5 canvas的浏览器:

function getAverageRGB(imgEl) {

    var blockSize = 5, // only visit every 5 pixels
        defaultRGB = {r:0,g:0,b:0}, // for non-supporting envs
        canvas = document.createElement('canvas'),
        context = canvas.getContext && canvas.getContext('2d'),
        data, width, height,
        i = -4,
        length,
        rgb = {r:0,g:0,b:0},
        count = 0;

    if (!context) {
        return defaultRGB;
    }

    height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height;
    width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width;

    context.drawImage(imgEl, 0, 0);

    try {
        data = context.getImageData(0, 0, width, height);
    } catch(e) {
        /* security error, img on diff domain */
        return defaultRGB;
    }

    length = data.data.length;

    while ( (i += blockSize * 4) < length ) {
        ++count;
        rgb.r += data.data[i];
        rgb.g += data.data[i+1];
        rgb.b += data.data[i+2];
    }

    // ~~ used to floor values
    rgb.r = ~~(rgb.r/count);
    rgb.g = ~~(rgb.g/count);
    rgb.b = ~~(rgb.b/count);

    return rgb;

}

对于IE,请查看exanvas


1
有什么方法可以获取位于不同域上的图像的颜色直方图?
Dan Loewenherz 2010年

1
丹:如果您尝试从其他域访问图像上的图像数据,我认为您会遇到跨域限制。真是可惜

8
我认为蓝色和绿色的值存在令人担忧的位置,您写道'rgb('+rgb.r+','+rgb.b+','+rgb.g+')',应该是'rgb('+rgb.r+','+rgb.g+','+rgb.b+')'。当主要颜色是蓝色但结果是绿色时,这很奇怪。
Ariona Rian

7
找到了解决跨域限制的好方法:只需img.crossOrigin = '';在设置src属性之前添加即可。发现于:coderwall.com/p/pa-2uw
mhu 2014年

无需算数,因为count === length / 4 / blockSize;)
yckart '16

84

想想我会发布一个我最近遇到的项目来获得主导色彩:

色贼

用于从图像中获取主色或代表性调色板的脚本。使用javascript和canvas。

提及和暗示主导色的其他解决方案在适当的上下文中(“使用javascript”)从未真正回答过该问题。希望这个项目将对那些想要做到的人有所帮助。


55

“主色”是棘手的。您想要做的是比较颜色空间中每个像素与每个其他像素之间的距离(欧氏距离),然后找到其颜色最接近于其他所有颜色的像素。该像素是主要颜色。平均颜色通常是泥。

我希望我在这里有MathML向您展示欧几里得距离。谷歌一下。

我在这里使用PHP / GD在RGB颜色空间中完成了上述执行: https //gist.github.com/cf23f8bddb307ad4abd8

然而,这在计算上非常昂贵。如果您在客户端中尝试使用大图像,它将使您的系统崩溃,并且肯定会使您的浏览器崩溃。我一直在努力将执行重构为:-将结果存储在查找表中,以备将来在每个像素的迭代中使用。-将大型图片分成20px 20px的网格以实现局部优势。-使用x1y1和x1y2之间的欧式距离来计算x1y1和x1y3之间的距离。

如果您在这方面取得了进展,请告诉我。我很高兴看到它。我也会这样做。

画布绝对是在客户端中执行此操作的最佳方法。SVG不是,SVG是基于向量的。完成执行后,我要做的下一件事是在画布上运行该文件(也许需要一个网络工作人员来计算每个像素的整体距离)。

还要考虑的另一件事是,RGB并不是一个很好的颜色空间,因为RGB空间中颜色之间的欧式距离与视觉距离不是很接近。更好的色彩空间可能是LUV,但我还没有找到一个好的库,也没有找到将RGB转换为LUV的算法。

完全不同的方法是将您的颜色分类为彩虹,并构建具有容忍度的直方图以说明颜色的各种阴影。我没有尝试过,因为很难对彩虹中的颜色进行排序,颜色直方图也是如此。我下一步可以尝试。同样,如果您在这里取得任何进展,请告诉我。


6
这是我希望我能做的不只是+1的答案之一:-)
David Johnstone 2010年

3
即使没有提供清晰的解决方案,它也是一个很好的答案。这将帮助我决定下一步...
很棒的2010年

这是一个很好的答案。我写了一些节点模块来执行欧式距离计算,而不是在LUV中,而是在L a b *颜色空间中。见github.com/zeke/color-namergithub.com/zeke/euclidean-distance
Zeke

1
@user:我只是想知道:大图像上1%的样本不足以加快计算速度吗?
jackthehipster 2014年

听起来像几何中值。也许这可以帮助? stackoverflow.com/questions/12934213/… ,如果要查找的点必须存在于原始集中: en.m.wikipedia.org/wiki/Medoid#Algorithms_to_compute_medoids
劳伦斯·

16

首先:无需HTML5 Canvas或SVG即可完成。
实际上,有人只是使用数据URI方案使用JavaScript生成了客户端PNG文件而没有 canvas或SVG 。

第二:您实际上可能根本不需要Canvas,SVG或以上任何一种。
如果只需要在客户端上处理图像,而无需修改它们,则不需要所有这些。

您可以从页面上的img标签获取源地址,对其进行XHR请求-它很可能来自浏览器缓存-并将其作为来自Javascript的字节流进行处理。
您将需要对图像格式有很好的了解。(以上生成器部分基于libpng来源,可能会提供一个很好的起点。)


+1用于字节流解决方案。如果唯一的选择是在客户端应用程序上进行安装,这是一个很好的选择。
loretoparisi 2015年

11

我会说通过HTML canvas标签。

您可以在这里找到@Georg的帖子,其中谈到了Opera开发人员的一小段代码:

// Get the CanvasPixelArray from the given coordinates and dimensions.
var imgd = context.getImageData(x, y, width, height);
var pix = imgd.data;

// Loop over each pixel and invert the color.
for (var i = 0, n = pix.length; i < n; i += 4) {
    pix[i  ] = 255 - pix[i  ]; // red
    pix[i+1] = 255 - pix[i+1]; // green
    pix[i+2] = 255 - pix[i+2]; // blue
    // i+3 is alpha (the fourth element)
}

// Draw the ImageData at the given (x,y) coordinates.
context.putImageData(imgd, x, y);

通过使用每个像素的R,G和B值来反转图像。您可以轻松存储RGB值,然后将红色,绿色和蓝色数组四舍五入,最后将它们转换回十六进制代码。


有没有机会用IE替代画布解决方案?
bgreater

@ Ben4Himv:有IE9平台预览版;-)但是严重的是,恐怕没有。
安迪E


4

Javascript无法访问图像的单个像素颜色数据。至少直到html5为止……这时,您可以将图像绘制到画布上,然后检查画布(也许,我自己从未做过)。


2

多合一解决方案

我将结合使用Color ThiefName of Color的修改版本,以获得足够多的图像主色结果数组。

例:

考虑下图:

在此处输入图片说明

您可以使用以下代码提取与主色有关的图像数据:

let color_thief = new ColorThief();
let sample_image = new Image();

sample_image.onload = () => {
  let result = ntc.name('#' + color_thief.getColor(sample_image).map(x => {
    const hex = x.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
    
  }).join(''));
  
  console.log(result[0]); // #f0c420     : Dominant HEX/RGB value of closest match
  console.log(result[1]); // Moon Yellow : Dominant specific color name of closest match
  console.log(result[2]); // #ffff00     : Dominant HEX/RGB value of shade of closest match
  console.log(result[3]); // Yellow      : Dominant color name of shade of closest match
  console.log(result[4]); // false       : True if exact color match
};

sample_image.crossOrigin = 'anonymous';
sample_image.src = document.getElementById('sample-image').src;

1

这是关于“色彩量化”的,它具有几种方法,例如MMCQ(改进的中位数切割量化)或OQ(八叉树量化)。不同的方法使用K均值来获取颜色的群集。

我已经将所有内容放到这里,因为我找到了一个解决方案,该解决方案针对tvOSXHTML的子集,其中没有任何<canvas/>元素:

使用XMLHttpRequest生成RGB图像的主色


1

在获得datauri支持的情况下获取图像平均颜色的准确度较低但最快的方法:

function get_average_rgb(img) {
    var context = document.createElement('canvas').getContext('2d');
    if (typeof img == 'string') {
        var src = img;
        img = new Image;
        img.setAttribute('crossOrigin', ''); 
        img.src = src;
    }
    context.imageSmoothingEnabled = true;
    context.drawImage(img, 0, 0, 1, 1);
    return context.getImageData(1, 1, 1, 1).data.slice(0,3);
}

1

正如在其他答案中指出的那样,通常您真正想要的是主色调,而不是通常的棕色。我编写了一个脚本,该脚本获得了最常见的颜色并将其发布在此要点上


-2

有一个在线工具pickimagecolor.com可以帮助您找到图像的平均或主色调。您只需从计算机上载图像,然后单击该图像即可。它以HEX,RGB和HSV给出平均颜色。它还会找到与该颜色匹配的颜色阴影。我已经使用了多次。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.