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

cl25kdpy  于 2021-09-13  发布在  Java
关注(0)|答案(14)|浏览(291)

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

46scxncf

46scxncf1#

好了,唯一的办法就是 <canvas/> ...
演示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。

ftf50wuq

ftf50wuq2#

我想我会发布一个我最近遇到的获得主色调的项目:
色贼
从图像中获取主色调或代表调色板的脚本。使用javascript和画布。
其他提到和建议主色的解决方案在适当的上下文(“javascript”)中从未真正回答过这个问题。希望这个项目能帮助那些想这样做的人。

olhwl3o2

olhwl3o23#

“主色调”是个棘手的问题。您要做的是比较颜色空间中每个像素和每个其他像素之间的距离(欧氏距离),然后找到颜色与每个其他颜色最接近的像素。那个像素是主色。平均颜色通常为泥浆。
我希望我有mathml在这里向你们展示欧几里德距离。谷歌。
在这里,我使用php/gd在rgb颜色空间中完成了上述执行:https://gist.github.com/cf23f8bddb307ad4abd8
然而,这在计算上非常昂贵。它会使您的系统在大图像上崩溃,如果您在客户端尝试,它肯定会使您的浏览器崩溃。我一直在努力重构我的执行,以便:-将结果存储在查找表中,以便将来在每个像素的迭代中使用。-将大型图像划分为20px 20px的网格,以实现局部优势。-使用x1y1和x1y2之间的欧几里德距离计算x1y1和x1y3之间的距离。
如果你在这方面有进展,请告诉我。我很高兴看到它。我也会这样做。
canvas无疑是在客户端实现这一点的最佳方式。svg不是,svg是基于向量的。在我停止执行之后,下一步我要做的就是在画布上运行它(可能需要一个webworker来计算每个像素的总距离)。
要考虑的另一件事是,rgb不是一个很好的颜色空间,因为rgb空间中颜色之间的欧几里德距离与视觉距离不是很接近。更好的颜色空间可能是luv,但我还没有找到一个好的库,或者任何将rgb转换为luv的算法。
另一种完全不同的方法是在彩虹中对颜色进行排序,并建立一个带有容差的直方图来解释颜色的不同深浅。我没有尝试过这个,因为在彩虹中对颜色进行排序很困难,颜色直方图也是如此。下一步我可能会试试这个。再一次,如果你在这里有任何进展,请告诉我。

b1uwtaje

b1uwtaje4#

第一:它可以在没有html5画布或svg的情况下完成。
实际上,有人刚刚使用javascript生成了客户端png文件,而不使用canvas或svg,而是使用数据uri方案。
第二:您实际上可能根本不需要画布、svg或以上任何一种。
如果您只需要在客户端处理图像,而不修改它们,则不需要所有这些。
您可以从页面上的img标记获取源地址,对其发出xhr请求(它很可能来自浏览器缓存),并将其作为来自javascript的字节流进行处理。
您需要对图像格式有很好的了解(上述生成器部分基于libpng源代码,可能提供了一个良好的起点。)

c9qzyr3d

c9qzyr3d5#

我会说通过html画布标签。
你可以在这里找到@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值,然后将红色、绿色和蓝色数组取整,最后将它们转换回十六进制代码。

g9icjywg

g9icjywg6#

不太准确但最快的方法是使用 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);
}
ngynwnxp

ngynwnxp7#

我最近遇到了一个jquery插件,它实现了我最初想要的功能https://github.com/briangonzalez/jquery.adaptive-backgrounds.js 关于从一个图像中获得一种自治的颜色。

vs91vp4v

vs91vp4v8#

javascript无法访问图像的单个像素颜色数据。至少,可能要等到html5。。。在这一点上,你可以在画布上画一个图像,然后检查画布(也许,我自己从来没有做过)。

xtfmy6hx

xtfmy6hx9#

编辑:在发布了这篇文章之后,我才意识到@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. ,这就是您需要在演示中上载图像而不是输入url的原因。
这种方法也可以用于通过增加宽度和高度来绘制图像的像素化图像。
这在chrome上有效,但在其他浏览器上可能不起作用。

pb3skfrl

pb3skfrl10#

一体化解决方案

我个人会将“颜色小偷”与该颜色名称的修改版本结合起来,以获得足够多的图像主色结果数组。
例子:
考虑下面的图像:

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

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;
7nbnzgx9

7nbnzgx911#

这是关于“颜色量化”的,它有几种方法,比如mmcq(改进的中值切割量化)或oq(八叉树量化)。不同的方法使用k-均值来获得颜色簇。
我在这里把所有的东西都放在一起了,因为我正在寻找解决问题的方法 tvOS 如果有xhtml的子集,则没有 <canvas/> 要素:
使用xmlhttprequest为rgb图像生成主色调

vzgqcmou

vzgqcmou12#

正如其他答案所指出的,通常你真正想要的是主导色,而不是一般的棕色。我写了一个脚本,得到了最常见的颜色,并张贴在这个要点

deyfvvtc

deyfvvtc13#

这是@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));
        };
    });
}
2lpgd968

2lpgd96814#

有一个在线工具pickimagecolor.com可以帮助您找到图像的平均颜色或主色。您只需从计算机上载图像,然后单击图像即可。它以十六进制、rgb和hsv表示平均颜色。它还可以找到与该颜色相匹配的色调供选择。我已经用过很多次了。

相关问题