JavaScript - 获取图像特定区域的平均颜色

2023-12-12

我需要使用以下命令从图像的矩形区域获取平均颜色JavaScript.

我尝试使用tracking.js但它不允许指定区域而不是单个像素。


如果您需要获取单个像素的平均颜色,而不是矩形区域的颜色,请看一下另一个问题:

???? 鼠标悬停时从画布获取像素颜色

正如你所说,你需要获取图像中矩形区域的颜色,我假设您的意思是您需要获取给定区域的平均颜色,而不是单个像素的颜色。

无论如何,两者都是以非常相似的方式完成的:

???? 从图像或画布中获取单个像素的颜色/值

要获取单个像素的颜色,您首先需要将该图像绘制到画布上:

const image = document.getElementById('image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = image.width;
const height = image.height;

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

context.drawImage(image, 0, 0, width, height);

然后获取单个像素的值,如下所示:

const data = context.getImageData(X, Y, 1, 1).data;

// RED   = data[0]
// GREEN = data[1]
// BLUE  = data[2]
// ALPHA = data[3]

✂️ 获取图像或画布上某个区域的平均颜色/值

你需要使用同样的CanvasRenderingContext2D.getImageData()获取更宽(多像素)区域的值,您可以通过更改其第三个和第四个参数来实现。该函数的签名是:

ImageData ctx.getImageData(sx, sy, sw, sh);
  • sx:从中提取 ImageData 的矩形左上角的 x 坐标。
  • sy:从中提取 ImageData 的矩形左上角的 y 坐标。
  • sw:将从中提取图像数据的矩形的宽度。
  • sh:将从中提取 ImageData 的矩形的高度。

你可以看到它返回一个ImageData目的,不管那是什么。这里重要的部分是该对象有一个.data属性包含我们所有的像素值。

但请注意.data属性是一维的Uint8ClampedArray,这意味着所有像素的组件都已被展平,因此您将得到如下所示的结果:

假设您有一个如下所示的 2x2 图像:

 RED PIXEL |       GREEN PIXEL
BLUE PIXEL | TRANSPARENT PIXEL

然后,你会像这样得到它们:

[ 255, 0, 0, 255,    0, 255, 0, 255,    0, 0, 255, 255,    0, 0, 0, 0          ]
|   RED PIXEL   |    GREEN PIXEL   |     BLUE PIXEL   |    TRANSPAERENT  PIXEL |
|   1ST PIXEL   |      2ND PIXEL   |      3RD PIXEL   |             4TH  PIXEL | 

✨ 让我们看看它的实际效果

const avgSolidColor = document.getElementById('avgSolidColor');
const avgAlphaColor = document.getElementById('avgAlphaColor');
const avgSolidWeighted = document.getElementById('avgSolidWeighted');

const avgSolidColorCode = document.getElementById('avgSolidColorCode');
const avgAlphaColorCode = document.getElementById('avgAlphaColorCode');
const avgSolidWeightedCOde = document.getElementById('avgSolidWeightedCode');

const brush = document.getElementById('brush');
const image = document.getElementById('image');
const canvas = document.createElement('canvas');
const context = canvas.getContext('2d');
const width = image.width;
const height = image.height;

const BRUSH_SIZE = brush.offsetWidth;
const BRUSH_CENTER = BRUSH_SIZE / 2;
const MIN_X = image.offsetLeft + 4;
const MAX_X = MIN_X + width - BRUSH_SIZE;
const MIN_Y = image.offsetTop + 4;
const MAX_Y = MIN_Y + height - BRUSH_SIZE;

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

context.drawImage(image, 0, 0, width, height);

function sampleColor(clientX, clientY) {
  const brushX = Math.max(Math.min(clientX - BRUSH_CENTER, MAX_X), MIN_X);
  const brushY = Math.max(Math.min(clientY - BRUSH_CENTER, MAX_Y), MIN_Y);

  const imageX = brushX - MIN_X;
  const imageY = brushY - MIN_Y;
 
  let R = 0;
  let G = 0;
  let B = 0;
  let A = 0;
  let wR = 0;
  let wG = 0;
  let wB = 0;
  let wTotal = 0;

  const data = context.getImageData(imageX, imageY, BRUSH_SIZE, BRUSH_SIZE).data;
  
  const components = data.length;
  
  for (let i = 0; i < components; i += 4) {
    // A single pixel (R, G, B, A) will take 4 positions in the array:
    const r = data[i];
    const g = data[i + 1];
    const b = data[i + 2];
    const a = data[i + 3];
    
    // Update components for solid color and alpha averages:
    R += r;
    G += g;
    B += b;
    A += a;
    
    // Update components for alpha-weighted average:
    const w = a / 255;
    wR += r * w;
    wG += g * w;
    wB += b * w;
    wTotal += w;
  }
  
  const pixelsPerChannel = components / 4;
  
 // The | operator is used here to perform an integer division:

  R = R / pixelsPerChannel | 0;
  G = G / pixelsPerChannel | 0;
  B = B / pixelsPerChannel | 0;
  wR = wR / wTotal | 0;
  wG = wG / wTotal | 0;
  wB = wB / wTotal | 0;

  // The alpha channel need to be in the [0, 1] range:

  A = A / pixelsPerChannel / 255;
  
  // Update UI:
  
  requestAnimationFrame(() => {
    brush.style.transform = `translate(${ brushX }px, ${ brushY }px)`;

    avgSolidColorCode.innerText = avgSolidColor.style.background
      = `rgb(${ R }, ${ G }, ${ B })`;

    avgAlphaColorCode.innerText = avgAlphaColor.style.background
      = `rgba(${ R }, ${ G }, ${ B }, ${ A.toFixed(2) })`;

    avgSolidWeightedCode.innerText = avgSolidWeighted.style.background
      = `rgb(${ wR }, ${ wG }, ${ wB })`;
  });
}

document.onmousemove = (e) => sampleColor(e.clientX, e.clientY);
  
sampleColor(MIN_X, MIN_Y);
body {
  margin: 0;
  height: 100vh;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  cursor: crosshair;
  font-family: monospace;
}

#image {
  border: 4px solid white;
  border-radius: 2px;
  box-shadow: 0 0 32px 0 rgba(0, 0, 0, .25);
  width: 150px;
  box-sizing: border-box;
}

#brush {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  width: 50px;
  height: 50px;
  background: magenta;
  mix-blend-mode: exclusion;
}

#samples {
  position: relative;
  list-style: none;
  padding: 0;
  width: 250px;
}

#samples::before {
  content: '';
  position: absolute;
  top: 0;
  left: 27px;
  width: 2px;
  height: 100%;
  background: black;
  border-radius: 1px;
}

#samples > li {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-left: 56px;
}

#samples > li + li {
  margin-top: 8px;
}

.sample {
  position: absolute;
  top: 50%;
  left: 16px;
  transform: translate(0, -50%);
  display: block;
  width: 24px;
  height: 24px;
  border-radius: 100%;
  box-shadow: 0 0 16px 4px rgba(0, 0, 0, .25);  
  margin-right: 8px;
}

.sampleLabel {
  font-weight: bold;
  margin-bottom: 8px;
}

.sampleCode {
  
}
<img id="image" src="data:image/gif;base64,R0lGODlhSwBLAPEAACMfIO0cJAAAAAAAACH/C0ltYWdlTWFnaWNrDWdhbW1hPTAuNDU0NTUAIf4jUmVzaXplZCBvbiBodHRwczovL2V6Z2lmLmNvbS9yZXNpemUAIfkEBQAAAgAsAAAAAEsASwAAAv+Uj6mb4A+QY7TaKxvch+MPKpC0eeUUptdomOzJqnLUvnFcl7J6Pzn9I+l2IdfII8DZiCnYsYdK4qRTptAZwQKRVK71CusOgx2nFRrlhMu+33o2NEalC6S9zQvfi3Mlnm9WxeQ396F2+HcQsMjYGEBRVbhy5yOp6OgIeVIHpEnZyYCZ6cklKBJX+Kgg2riqKoayOWl2+VrLmtDqBptIOjZ6K4qAeSrL8PcmHExsgMs2dpyIxPpKvdhM/YxaTMW2PGr9GP76BN3VHTMurh7eoU14jsc+P845Vn6OTb/P/I68iYOfwGv+JOmRNHBfsV5ujA1LqM4eKDoNvXyDqItTxYX/DC9irKBlIhkKGPtFw1JDiMeS7CqWqySPZcKGHH/JHGgIpb6bCl1O0LmT57yCOqoI5UcU0YKjPXmFjMm0ZQ4NIVdGBdZRi9WrjLxJNMY1Yr4dYeuNxWApl1ALHb+KDHrTV1owlriedJgSr4Cybu/9dFiWYAagsqAGVkkzaZTAuqD9ywKWMUG9dCO3u2zWpVzIhpW122utZlrHnTN+Bq2Mqrlnqh8CQ+0Mrq3Kc++q7eo6dlB3rLuh3abPVbbbI2mxBdhWdsZhid8cr0oy9F08q0k5FXSadiyL1mF5z51a8VsQOp3/LlodkBfzmzWf2bOrtfzr48k/1hupDaLa9rUbO+zlwndfaOCURAXRNaCBqBT2BncJakWfTzSYkmCEFr60RX0V8sKaHOltCBJ1tAAFYhHaVVbig3jxp0IBADs=" >

<div id="brush"></div>

<ul id="samples">
  <li>
    <span class="sample" id="avgSolidColor"></span>
    <div class="sampleLabel">avgSolidColor</div>
    <div class="sampleCode" id="avgSolidColorCode">rgb(-, -, -)</div>
  </li>
  <li>
    <span class="sample" id="avgAlphaColor"></span>
    <div class="sampleLabel">avgAlphaColor</div>
    <div class="sampleCode" id="avgAlphaColorCode">rgba(-, -, -, -)</div>
  </li>
  <li>
    <span class="sample" id="avgSolidWeighted"></span>
    <div class="sampleLabel">avgSolidWeighted</div>
    <div class="sampleCode" id="avgSolidWeightedCode">rgba(-, -, -, -)</div>
  </li>
</ul>

⚠️ 注意我使用小数据 URI 来避免Cross-Origin如果我尝试使用更长的数据 URI,包含外部图像或大于允许的答案,则会出现问题。

????️这些普通的颜色看起来很奇怪,不是吗? (@SMT 的评论)

如果你把画笔移到左上角,你会看到avgSolidColor几乎是黑色的。这是因为该区域中的大多数像素是完全透明的,因此它们的值恰好或非常接近0, 0, 0, 255。这意味着对于我们处理的每一个,R, G and B不改变或改变很少,同时pixelsPerChannel仍然考虑到它们,所以我们最终除以一个小数字(因为我们添加0对于大多数人来说)是一个很大的值(画笔中的像素总数),这给了我们一个非常接近的值0(黑色的)。

例如,如果我们有两个像素,0, 0, 0, 255 and 255, 0, 0, 0,通过观察它们,我们可以期望平均值R渠道成为255(因为其中之一是完全透明的)。然而,这将是(0 + 255) / 2 | 1 = 127。但别担心,我们接下来会看看如何做到这一点。

另一方面,avgAlphaColor看起来是灰色的。嗯,这实际上不是真的,只是looks灰色,因为我们现在使用 Alpha 通道,这使其半透明并允许我们看到页面的背景,在本例中为白色。

???? Alpha 加权平均值(@SMT 评论的解决方案)

那么,我们能做些什么来解决这个问题呢?好吧,事实证明我们只需要使用 alpha 通道作为我们(现在加权)平均值的权重:

这意味着如果一个像素r, g, b, a, where a是在区间内[0, 255],我们将像这样更新我们的变量:

const w = a / 255; // w is in the interval [0, 1]
wR += r * w;
wG += g * w; 
wB += b * w; 
wTotal += w;

请注意像素越透明(w越接近 0),我们在计算中就越不关心它的值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript - 获取图像特定区域的平均颜色 的相关文章

  • 如何使用javascript将大图像转换为十六进制?

    如果我尝试将图像转换为十六进制 无论我使用哪个函数 我都会收到此错误消息 该图像的大小为 7 MB 19812 毫秒 清理 1401 2 1455 0 gt 1401 2 1455 0 MB 9 9 0 ms 自上次 GC 以来 8 3 m
  • 如何格式化 Highcharts 的 (x,y) 对数据的日期时间

    我的序列化方法会产生如下所示的日期时间字符串 2014 07 09T12 30 41Z 为什么下面的代码不起作用 function container highcharts xAxis type datetime series data x
  • 图像无法在带有 DOM 的 IE 中加载:控制台中的 7009 错误(无法解码)

    当在 IE 中的单个页面上加载许多图像时 在 IE11 中重现 其中一些图像开始加载失败 并在控制台中出现类似以下警告的内容 DOM7009 无法解码 URL 处的图像 某些唯一的 url 当我查看网络流量时 似乎确实从服务器收到了每个图像
  • 引导程序提前输入未填充承诺的响应

    我的引导程序预输入如下
  • React-Redux:state.setIn() 和 state.set() 有什么区别?

    我见过使用setIn and set 在一些react redux代码中 state setIn state set 我在这里找到了一些文档https facebook github io immutable js https facebo
  • 防止 iOS 键盘在 cordova 3.5 中滚动页面

    我正在使用 Cordova 3 5 和 jQuery mobile 构建 iOS 应用程序 我在大部分应用程序中禁用了滚动功能 但是 当我选择输入字段时 iOS 键盘会打开并向上滚动页面 我不想要这个功能 由于输入足够高 键盘不会覆盖它 我
  • 如何将函数附加到弹出窗口关闭事件(Twitter Bootstrap)

    我做了一些搜索 但我只能认为我可以将事件附加到导致其关闭的按钮 https stackoverflow com questions 13205103 attach event handler to button in twitter boo
  • 如何纠正流警告:解构(缺少注释)

    我正在编写一个小型 React Native 应用程序 并且正在尝试使用 Flow 但我无法在任何地方真正获得有关它的正确教程 我不断收到错误 destructuring Missing annotation 有关 station 这段代码
  • 尝试将数据存储在点击器网站中

    我正在尝试存储一个名为的变量score无论何时刷新 您都会一次又一次地使用它 我不明白的是它的代码是什么 我尝试了一些方法 但似乎都不起作用 这是我的答题器网站 但是当我尝试使用 JavaScript 来存储它时 它不起作用window o
  • Chrome 扩展程序在代码中使用 client_secret

    我正在开发具有自己的 oAuth 授权的 Google Chrome 扩展 当然 我必须使用 client id 和 client secret 作为请求令牌 有什么办法可以向用户隐藏这些数据吗 由于此请求只是 javascript 源代码
  • 将 UMD Javascript 模块导入浏览器

    你好 我正在对 RxJS 进行一些研究 我可以通过在浏览器中引用它来使用该库 如下所示 它使用全局对象命名空间变量 Rx 导入 我可以制作可观察的东西并做所有有趣的事情 当我将 src 更改为指向最新的 UMD 文件时 一切都会崩溃 如下所
  • 有没有办法在 onclick 触发时禁用 iPad/iPhone 上的闪烁/闪烁?

    所以我有一个有 onclick 事件的区域 在常规浏览器上单击时 它不会显示任何视觉变化 但在 iPad iPhone 上单击时 它会闪烁 闪烁 有什么办法可以阻止它在 iPad iPhone 上执行此操作吗 这是一个与我正在做的类似的示例
  • Jquery一键提交多个同名表单

    我有动态创建的循环表单 我需要一键提交所有表单 我正在遵循下面的代码 你能建议我怎么做吗 谢谢
  • Firebase 函数 onWrite 未被调用

    我正在尝试使用 Firebase 函数实现一个触发器 该触发器会复制数据库中的一些数据 我想观看所有添加的内容votes user vote 结构为 我尝试的代码是 const functions require firebase func
  • Three.js 各种大小的粒子

    我是 Three js 的新手 正在尝试找出添加 1000 个粒子的最佳方法 每个粒子都有不同的大小和颜色 每个粒子的纹理是通过绘制画布创建的 通过使用粒子系统 所有粒子都具有相同的颜色和大小 为每个粒子创建一个粒子系统是非常低效的 有没有
  • 对于只触及我的工作表的 Google 表格脚本,收到“此应用程序未经验证”

    我正在编写一个 Google Sheets 脚本 我只想访问与 gs 文件关联的同一电子表格中的数据 似乎我应该有权在自己的电子表格中运行脚本 但是每当我运行一个函数时 我都会得到一个This app isn t verified信息 我该
  • Chrome//kendoUI/jQuery:超出最大调用堆栈大小

    我正在使用 hottowell 模板来创建 spa 应用程序 并且我从 jquery 中收到了一个很好的错误 基本上我的问题从此刻开始尝试绑定我的视图 viewModelBinder js 来自 durandal 库 viewModelBi
  • Javascript Replace() 和 $1 问题

    我正在尝试创建一个脚本来搜索文本中的模式并在它找到的字符串周围包裹一个标签 shop attributes td each function this html function i html return html replace E 0
  • 如何在执行新操作时取消先前操作的执行?

    我有一个动作创建器 它会进行昂贵的计算 并在每次用户输入内容时调度一个动作 基本上是实时更新 但是 如果用户输入多个内容 我不希望之前昂贵的计算完全运行 理想情况下 我希望能够取消执行先前的计算并只执行当前的计算 没有内置功能可以取消Pro
  • 如何使用asm.js进行测试和开发?

    最近我读到asm js规范 看起来很酷 但是是否有任何环境 工具来开发和测试这个工具 这还只是处于规范阶段吗 您可以尝试使用 emscripten 和 ASM JS 1 并从侧分支在 firefox 构建中运行它 有关 asm js 的链接

随机推荐

  • 从静态方法调用 startActivityForResult

    我有一个按钮监听器 当用户单击按钮时我想启动相机意图 目前我有这个 public class ButtonListener implements View OnClickListener private ArrayList
  • 在整个页面加载之前显示加载栏

    我想在加载整个页面之前显示一个加载栏 目前 我只是使用了一个小的延迟 document ready function page fadeIn 2000 该页面已使用 jQuery 注意 我已经尝试过这个 但它对我不起作用 脚本运行时加载栏
  • Python“for i in”+变量

    我有以下代码 Euler Problem 1 print We are going to solve Project Euler s Problem 1 euler number input What number do you want
  • 如何在Linux上指定时间运行脚本? [关闭]

    Closed 这个问题不符合堆栈溢出指南 目前不接受答案 我有一个包含特定日期和时间的文本文件 我希望能够在该文件中指定的时间运行脚本 你将如何实现这一目标 创建另一个在后台运行的脚本 类似于守护程序 并每秒检查当前时间是否与文件中的时间匹
  • 将电子邮件另存为 MSG 文件,无需使用 Outlook(COM 对象等)或第 3 方软件

    现在 我正在使用 Exchange Web 服务 API 和 PowerShell 从 Exchange 中提取特定电子邮件并将其保存为 EML 文件 这很好用 但是 用户 客户要求电子邮件采用 msg 格式 我见过有两种方法可以做到这一点
  • 录制时拍摄相机屏幕截图 - 就像 Galaxy S3 一样?

    我正在开发一个使用 SurfaceView 进行显示的相机应用程序 我可以截取 SurfaceView 的屏幕截图 并将其保存为位图 使用 getDrawingCache 在包装 SurfaceView 的布局上 还有canvas draw
  • VBA:等待 Bloomberg BDP 通话完成

    我有一个脚本将一些外部数据导入到工作表中 这反过来会影响一些 BDP 公式 最好 我想在复制数据后立即对 BDP 结果进行一些检查 Bloomberg Excel 插件异步更新 如何等待结果然后恢复脚本 似乎只有在 VBA 脚本完成后才会导
  • 为 Nitrogen6x 构建 Qt 5 时出现 libm 重定位错误

    我正在尝试在 Qt 5 上构建氮气6x板由 i MX6Q 供电 我已经安装了Debian 喘息在板上 我正在使用乌班图12 10交叉编译机 配置 Qt 就像一个魅力 但我陷入了 make 步骤 这是我运行的配置脚本 configure v
  • 如何设计一封安全且“自毁”的电子邮件?

    正如大多数人所知 电子邮件非常不安全 即使客户端和发送电子邮件的服务器之间有 SSL 安全连接 消息本身在 Internet 上的节点间跳跃时也将采用明文形式 从而容易被窃听 另一个考虑因素是 发件人可能不希望邮件在一段时间后或在被阅读一次
  • 读取 PDF 文档中的所有书签,并使用书签的页码和标题创建字典

    我尝试使用 Python 和 PyPDF2 包来阅读 PDF 文档 目标是读取pdf中的所有书签 并构建一个以书签页码为键 书签标题为值的字典 互联网上没有太多关于如何实现它的支持 除了this文章 其中发布的代码不起作用 我不是 pyth
  • 无法将我自己的域添加到 google api 通知端点

    我正在尝试使用谷歌推送通知 我已经关注了此处列出的注册过程 简而言之 我的领域已在 https 中验证在 Google 网站管理员工具中 但是 当我尝试添加通知端点在 Google Cloud Console 中 我收到以下错误 You d
  • (git tfs fetch)TF400324:Team Foundation 服务不可用,底层连接已关闭

    我使用 git tfs 已经快 5 年了 然后有一天我在运行时遇到以下错误git tfs fetch TF400324 Team Foundation services are not available from server https
  • 如何在低于 KitKat 的 Android 版本的 Android WebView 中重置代理?

    我使用以下 2 种方法在 Android WebView 中为 Android 版本 ICS 和 JB 设置代理 但我无法重置 删除这两个版本的代理 如何重置 删除通过这些方法设置的代理 For ICS private static boo
  • 使用 Perl 获取 WMI 内存值

    我需要使用WMI收集Windows操作系统的内存数据 从这个意义上说 我开发了一个 Perl 脚本来生成此类数据 但是 我想知道我的方法是否正确以及有哪些替代方案 收集数据的方法旨在尽可能广泛地应用于 Windows 操作系统 如果你不是一
  • 信中信,模式识别

    我想检测这种模式 正如您所看到的 它基本上是一个字母 C 位于另一个字母内部 具有不同的方向 我的模式可以相互包含多个 C 我发布的带有 2 个 C 的模式只是一个示例 我想检测有多少个 C 以及每个 C 的方向 现在我已经成功地检测到了这
  • 如何检索用于编译给定 ELF 可执行文件的 GCC 版本?

    我想检索用于编译给定可执行文件的 GCC 版本 我试过readelf但没有得到信息 有什么想法吗 一般存放在评论区 strings a
  • Vagrant 端口转发不起作用。主机无法访问杯子

    所以我正在使用 vagrant 并尝试将其用作打印服务器 我安装了杯子 内部一切正常 我什至可以快速做一个curl到我的本地主机 631 我的流浪汉中的杯子端口 一切都有 问题是我无法以任何方式从主机尝试访问它 显然我转发了端口并且尝试了多
  • SQL从查询中的数据中选择该数据尚未在数据库中?

    我想在进行 Web 服务调用之前检查数据库中已记录的记录 这是我想象的查询的样子 我只是似乎无法弄清楚语法 SELECT FROM 1 2 3 4 as temp table WHERE temp table id LEFT JOIN ta
  • Objective-C:查找字符串中的数字

    我有一个包含单词和数字的字符串 如何从字符串中提取该数字 NSString str This is my string 1234 我希望能够将 1234 作为 int 去掉 每次我搜索该字符串时 该字符串都会有不同的数字和单词 Ideas
  • JavaScript - 获取图像特定区域的平均颜色

    我需要使用以下命令从图像的矩形区域获取平均颜色JavaScript 我尝试使用tracking js但它不允许指定区域而不是单个像素 如果您需要获取单个像素的平均颜色 而不是矩形区域的颜色 请看一下另一个问题 鼠标悬停时从画布获取像素颜色