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

时间:2010-03-29 21:42:51

标签: javascript image-manipulation

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

14 个答案:

答案 0 :(得分:126)

AFAIK,唯一的方法是使用<canvas/> ...

DEMO V2 http://jsfiddle.net/xLF38/818/

请注意,这仅适用于同一域和支持HTML5画布的浏览器中的图像:

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,请查看excanvas

答案 1 :(得分:76)

想想我发布了一个我最近遇到的主要颜色的项目:

<强> Color Thief

  

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

提及和暗示主色的其他解决方案永远不会在适当的上下文中回答问题(“在javascript中”)。希望这个项目能够帮助那些想要做到这一点的人。

答案 2 :(得分:47)

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

我希望我在这里有MathML向你展示欧几里德距离。谷歌吧。

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

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

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

Canvas绝对是在客户端中执行此操作的最佳方式。 SVG不是,SVG是基于矢量的。在我执行下来之后,接下来要做的就是让它在画布中运行(可能每个像素的整体距离计算都有一个webworker)。

要考虑的另一件事是RGB不是一个很好的色彩空间,因为RGB空间中的颜色之间的欧氏距离不是非常接近视距。这样做的更好的颜色空间可能是LUV,但我没有找到一个好的库,或者没有找到将RGB转换为LUV的任何算法。

完全不同的方法是在彩虹中对颜色进行排序,并构建一个具有容差的直方图,以适应不同颜色的阴影。我没试过这个,因为在彩虹中排序颜色很难,颜色直方图也是如此。我可能会尝试下一步。再次,如果你在这里取得任何进展,请告诉我。

答案 3 :(得分:15)

首先:它可以在没有HTML5 Canvas或SVG的情况下完成 实际上,有人使用generate client-side PNG files using JavaScript设法{@ 3}},没有画布或SVG。

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

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

答案 4 :(得分:9)

我想通过HTML canvas标签说。

你可以在@Georg上找到here一篇关于Opera dev的小代码的帖子:

// 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值,然后向上舍入红色,绿色和蓝色数组,最后将它们转换回HEX代码。

答案 5 :(得分:5)

我最近遇到了一个jQuery插件,该插件执行了我原本想要的https://github.com/briangonzalez/jquery.adaptive-backgrounds.js关于从图像获取支配颜色的问题。

答案 6 :(得分:4)

Javascript无权访问图片的各个像素颜色数据。至少,也许不是直到html5 ......在这一点上,你可以将图像绘制到画布,然后检查画布(也许,我自己从未做过)。

答案 7 :(得分:4)

编辑:只有在发布此内容后,我才意识到@350D 的答案完全相同。

令人惊讶的是,这只需 4 行代码即可完成:

const canvas = document.getElementById("canvas"),
  preview = document.getElementById("preview"),
  ctx = canvas.getContext("2d");

canvas.width = 1;
canvas.height = 1;

preview.width = 400;
preview.height = 400;

function getDominantColor(imageObject) {
  //draw the image to one pixel and let the browser find the dominant color
  ctx.drawImage(imageObject, 0, 0, 1, 1);

  //get pixel color
  const i = ctx.getImageData(0, 0, 1, 1).data;

  console.log(`rgba(${i[0]},${i[1]},${i[2]},${i[3]})`);

  console.log("#" + ((1 << 24) + (i[0] << 16) + (i[1] << 8) + i[2]).toString(16).slice(1));
}



// vvv all of this is to just get the uploaded image vvv
const input = document.getElementById("input");
input.type = "file";
input.accept = "image/*";

input.onchange = event => {
  const file = event.target.files[0];
  const reader = new FileReader();

  reader.onload = readerEvent => {
    const image = new Image();
    image.onload = function() {
      //shows preview of uploaded image
      preview.getContext("2d").drawImage(
        image,
        0,
        0,
        preview.width,
        preview.height,
      );
      getDominantColor(image);
    };
    image.src = readerEvent.target.result;
  };
  reader.readAsDataURL(file, "UTF-8");
};
canvas {
  width: 200px;
  height: 200px;
  outline: 1px solid #000000;
}
<canvas id="preview"></canvas>
<canvas id="canvas"></canvas>
<input id="input" type="file" />

工作原理:

创建画布上下文

const context = document.createElement("canvas").getContext("2d");

这会将图像仅绘制到一个画布像素,使浏览器为您找到主色。

context.drawImage(imageObject, 0, 0, 1, 1);

之后,只需获取像素的图像数据:

const i = context.getImageData(0, 0, 1, 1).data;

最后,转换为 rgbaHEX

const rgba = `rgba(${i[0]},${i[1]},${i[2]},${i[3]})`;

const HEX = "#" + ((1 << 24) + (i[0] << 16) + (i[1] << 8) + i[2]).toString(16).slice(1);

但是这种方法有一个问题,那就是getImageData有时会抛出错误Unable to get image data from canvas because the canvas has been tainted by cross-origin data.,这就是你需要在演示中上传图片而不是输入网址的原因,例如.

此方法也可用于通过增加宽度和高度来绘制图像来像素化图像。

这适用于 chrome,但可能不适用于其他浏览器。

答案 8 :(得分:1)

这是关于&#34;颜色量化&#34;有几种方法,如MMCQ(修正中值切割量化)或OQ(八叉树量化)。不同的方法使用K-Means来获得颜色簇。

我已经把所有人放在一起,因为我找到了tvOS的解决方案,其中有一部分XHTML,没有<canvas/>元素:

Generate the Dominant Colors for an RGB image with XMLHttpRequest

答案 9 :(得分:1)

多合一解决方案

我会亲自将Color Thief与此modified version of Name that Color结合使用,以获得图像的主要颜色结果数据。

示例:

考虑以下图片:

enter image description here

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

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;

答案 10 :(得分:1)

这是@350D 的答案,但异步(因为某些图像可能需要时间加载)和打字稿

async function get_average_rgb(src: string): Promise<Uint8ClampedArray> {
/* https://stackoverflow.com/questions/2541481/get-average-color-of-image-via-javascript */
    return new Promise(resolve => {
        let context = document.createElement('canvas').getContext('2d');
        context!.imageSmoothingEnabled = true;

        let img = new Image;
        img.src = src;
        img.crossOrigin = "";

        img.onload = () => {
            context!.drawImage(img, 0, 0, 1, 1);
            resolve(context!.getImageData(0, 0, 1, 1).data.slice(0,3));
        };
    });
}

答案 11 :(得分:0)

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

答案 12 :(得分:0)

使用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);
}

答案 13 :(得分:0)

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