【问题标题】:Convert image to variable number of grayscales将图像转换为可变数量的灰度
【发布时间】:2016-10-28 15:39:28
【问题描述】:

我想将图像转换为可变数量的灰度,该数量可由用户配置,范围从 2(单色/黑白)到 256(全灰度)。

我尝试使用 css 和 svg 过滤器来完成此操作,例如在 CSS 中:

filter: grayscale(100%)

将图像转换为灰度,这是我想要的 256 灰度但其他值,例如

filter: grayscale(10%)

不限制灰度的数量,而只是减少图片中使用的颜色数量,所以使用这些滤镜我永远无法得到单色的结果。

感谢您的帮助。

【问题讨论】:

  • 您可以先获取图像,然后将其绘制在画布上,并逐个访问其像素的 RGB 值。然后将使用公式Y = 0.299*R + 0.587*G + 0.114*B 计算 Y(亮度)并将计算出的 Y 插入到每个像素的 R、G 和 B 的位置。
  • 供将来参考,您尝试实现的这种技术称为“量化”,如下面的答案所述。

标签: javascript css svg svg-filters


【解决方案1】:

您可以使用 SVG 过滤器来执行此操作,但 Internet Explorer 中的值最多为 64 个。纯黑/白分离的过滤器是这样的:

<filter id="greyscale-posterize" color-interpolation-filters="sRGB">
<feColorMatrix type="saturate" values="0"/>
<feComponentTransfer>
  <feFuncR type="discrete" tableValues="0 1"/>
  <feFuncG type="discrete" tableValues="0 1"/>
  <feFuncB type="discrete" tableValues="0 1"/>
</feComponentTransfer>
</filter>

十值灰度的过滤器是:

<filter id="greyscale-posterize" color-interpolation-filters="sRGB">
<feColorMatrix type="saturate" values="0"/>
<feComponentTransfer>
  <feFuncR type="discrete" tableValues="0 .1 .2 .3 .4 .5 .6 .7 .8 .9 1"/>
  <feFuncG type="discrete" tableValues="0 .1 .2 .3 .4 .5 .6 .7 .8 .9 1"/>
  <feFuncB type="discrete" tableValues="0 .1 .2 .3 .4 .5 .6 .7 .8 .9 1"/>
</feComponentTransfer>
</filter>

随着输入的变化,使用 JavaScript 写入适当数量的范围。

【讨论】:

  • 非常感谢,这是我所希望的解决方案类型!我已按照您的建议完成并使用 JavaScript 修改 tableValues,过滤器使用 CSS 应用于图像。这在 Chrome 中可以完美运行,但是在 Firefox 中却没有。
  • 没有看到你的代码,我猜这是一个前缀问题。 Firefox 使用无前缀过滤器(Firefox 中没有 -moz-filter)。
【解决方案2】:

我建议:

  1. &lt;image&gt;&lt;svg&gt; 绘制到画布上
  2. 通过组合按视觉吸引力因素加权的每个颜色通道,将画布图像数据降低为灰度
  3. 通过舍入到[0, 255/(n-1), 255/(n-2), ..., 255] 中最接近的颜色来减少颜色数量
  4. 如果需要,将画布图像数据写回&lt;image&gt;

function rgbaToNGrayscales(src, numScales) {
  var d = (numScales - 1) / 255,
      inv_d = 1 / d,
      round = Math.round;
  for (var i = 0; i < src.length; i += 4) {
    src[i] = src[i + 1] = src[i + 2] = round(((src[i] * 4899 + src[i + 1] * 9617 + src[i + 2] * 1868 + 8192) >> 14) * d) * inv_d;
  }
}

var source = document.getElementById("source");
var target = document.getElementById("target");
var slider = document.getElementById("slider");

slider.addEventListener("input", function(event) {
  var canvas = document.createElement('canvas'),
      context = canvas.getContext('2d');
  canvas.width = source.width;
  canvas.height = source.height;
  context.drawImage(source, 0, 0);
  var imageData = context.getImageData(0, 0, canvas.width, canvas.height);
  rgbaToNGrayscales(imageData.data, slider.value);
  context.putImageData(imageData, 0, 0);
  target.src = canvas.toDataURL();
});
<input id="slider" type="range" min="1" max="10" value="0">
<image id="source" crossOrigin="anonymous" src="http://cors.io?u=http://i.stack.imgur.com/kTjOI.png">
<image id="target">

此算法非常快,但如果原始图像中没有颜色落入指定颜色范围之一,则结果图像的颜色可能少于指定数量。在这种情况下,您需要more advanced color quantization algorithm such as median cut

根据您的需要,将调整大小侦听器附加到您的 &lt;svg&gt; 并使用相应尺寸的画布元素动态覆盖它,以显示生成的灰度图像。

示例图片:

【讨论】:

  • 也感谢您的解决方案。我希望避免在 JavaScript 中手动执行所有操作,但这可能是较少依赖浏览器的解决方案。
猜你喜欢
  • 2010-11-20
  • 1970-01-01
  • 2018-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多