使用图像标签在网页上显示原始像素数组

Posted

技术标签:

【中文标题】使用图像标签在网页上显示原始像素数组【英文标题】:Displaying raw pixel array on a web page using image tag 【发布时间】:2021-11-18 11:33:18 【问题描述】:

我正在尝试找出将原始像素数组放入图像标签的最佳方法。像素是从没有 png 或 jpg 压缩库的服务器提供的,因此原始数组通过 HTTP 请求进入。我可以控制返回标头,因此我在响应中放置了一个 mime 类型。我想做:

<img src="http://myserver.com/id/" />

但我认为我做不到。如果可行,我可以使用 src="data:XXXXXX;base64,http://myserver.com/id/",但我需要知道如何处理 XXXX。

如果我可以将图像设置为 SVG,我的另一个想法是使用 svg。不确定我是否必须将每个像素包装在一个元素中。

也许有办法用 CSS 做到这一点?

我可以很容易地用js将数据写入canvas元素,但我希望有一种非js的方式。

我可以对来自服务器的数据的二进制结构做一些小的操作,所以如果有一种简单的方法可以告诉 jpg 或 png 格式这是未压缩的数据,我可以这样做......我只是没有能力或时间将 png 或 jpg 库转换为我必须使用的基于(区块链)的语言。

【问题讨论】:

原始像素数组的格式是什么? RGBA 一遍又一遍。每个像素四个字节。您需要分别知道高度和宽度。 @AustinFathree 如果有机会,请查看我的示例。我从您之前的评论中看到,您收到的像素数据采用 RGBA 格式,每个像素有四个字节(例如 R、G、B、A)。如果您可以提供返回数据的样本,我可以重构我的示例以使用直接返回的 RGBA 值。它实际上应该更容易,因为我相信我可以将它们直接插入rgba(r, g, b, a) CSS 函数,而无需将它们转换为十六进制。 【参考方案1】:

相关链接:zlib RFC、DEFLATE RFC 和 PNG spec。您可能希望在阅读时参考这些内容。

让我们制作一个PNG! 从图像数据作为 RGBA 图像开始,例如 2x2 图像:

12 34 56 78  9a bc de f0
cd ef 01 23  45 67 89 ab 

指定过滤器

对于图像的每一行,在开头添加一个空字节以将 PNG 过滤器设置为无。例如,

00  12 34 56 78  9a bc de f0 # row 1 of image
00  cd ef 01 23  45 67 89 ab # row 2 of image

DEFLATE 对其进行编码

虽然这是一种压缩格式,但我们实际上并不需要压缩它! DEFLATE 提供了一种无需压缩即可对数据进行编码的方法(请参阅 DEFLATE 规范的第 3.2.4 节)。从上面找到过滤后的图像数据的长度为16位整数(如果大于65535,则将图像数据拆分为65535大小的块,并为每个块执行此步骤)。创建一个空数组来保存 DEFLATE 数据流。将该大小作为大端格式的 16 位整数插入到当前空的 DEFLATE 数据流中。接下来,附加长度的按位倒数。最后,将过滤后的图像数据插入数据流中。

将其包装在 zlib 中

接下来,我们需要对数据进行 zlib 编码。从一个空的 zlib 数据流开始,并为标头插入 2 个空字节。接下来,从上面插入 DEFLATEd 数据。最后,插入 未压缩 数据的 Adler32 校验和。具体来说,应根据过滤后的图像数据创建 Adler32 校验和。

将其包装成 PNG 格式

最后,让我们将其包装成 PNG。替换宽度和高度值,指定IDAT块的长度作为zlib编码数据的长度,并通过取块名称的CRC32校验和替换IDHRIDAT块的CRC与数据连接。

# PNG signature
137 80 78 71 13 10 26 10

# IDHR chunk
00 00 00 0d # IDHR length
73 72 68 82 # IDHR type
00 00 00 02 # width: COMPUTE THIS
00 00 00 02 # height: COMPUTE THIS
08 # bit depth: 8
06 # colour type: truecolour with alpha
00 # compression method: zlib
00 # filter method: normal
00 # no interlacing
55 55 55 55 # CRC: COMPUTE THIS from "IDHR" + data

# IDAT chunk
55 55 55 55 # IDAT length: COMPUTE THIS
73 68 65 84 # IDAT type
[zlib data]
55 55 55 55 # CRC: COMPUTE THIS from the "IDAT" + data

# IEND chunk
00 00 00 00 # IEND length
73 69 78 68 # IEND type
AE 42 60 82 # CRC (always the same value, doesn't need to be computed)

【讨论】:

我想这正是我需要的!让我在实现它时运行陷阱,然后我会接受答案。非常感谢你把这些放在一起!【参考方案2】:

Austin,我从您之前的评论中看到,您收到的像素数据采用 RGBA 格式,每个像素有四个字节(例如 R、G、B、A)。

** 如果您可以提供返回数据的示例,我可以重构我的示例以使用直接返回的 RGBA 值。它实际上应该更容易,因为我相信我可以将它们直接插入rgba(r, g, b, a) CSS 函数,而无需将它们转换为十六进制。

我在console.draw() 工具中使用了完全相同的过程,它对我来说完美无缺,可以按需将原始像素数据转换为有效的&lt;img&gt; 标签。

如果您以颜色数组的形式接收像素,则必须为您使用的函数提供每行的像素数,以便它知道在哪里换行到下一行,或者更恰当地说,使用数组嵌套数组,每行像素一个嵌套数组。然后,逐行、逐像素地将嵌套像素颜色数组绘制到canvas。最后,使用htmlCanvasElement.toDataURL()方法将画布图像转换为Base64编码字符串,您可以将其分配给img标签的src属性值。

如果您想显示像素化的最终图像,而不使用任何抗锯齿,请确保应用 CSS 规则 image-rendering: pixelated

这就是所有这些,付诸行动:

const pixels2Base64 = pixelColors => 
  const canvas = document.createElement('canvas');
  canvas.width = pixelColors[0].length;
  canvas.height = pixelColors.length;
  const context = canvas.getContext('2d');
  for (let i = 0; i < pixelColors.length; i++) 
    for (let j = 0; j < pixelColors[i].length; j++) 
      context.fillStyle = pixelColors[i][j];
      context.fillRect(j, i, 1, 1);
    
  
  const dataURL = canvas.toDataURL('image/png', 1);
  canvas.remove();
  return dataURL;
;
const nyanCat = [["#00000000","#00000000","#00000000","#ff1111ff","#ff1111ff","#ff1111ff","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#ff1111ff","#ff1111ff","#ff1111ff","#fea70aff","#fea70aff","#fea70aff","#ff1111ff","#ff1111ff","#000000ff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#000000ff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#00000000","#000000ff","#000000ff","#00000000","#00000000"],["#fea70aff","#fea70aff","#fea70aff","#fefe06ff","#fefe06ff","#fefe06ff","#fea70aff","#fea70aff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#fea4feff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#fea4feff","#fea4feff","#ffd29bff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#fefe06ff","#fefe06ff","#fefe06ff","#48fe0bff","#48fe0bff","#48fe0bff","#fefe06ff","#fefe06ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#48fe0bff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff"],["#48fe0bff","#48fe0bff","#48fe0bff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fefefeff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fefefeff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff"],["#48fe0bff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff"],["#0eadfeff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#000000ff"],["#0eadfeff","#0eadfeff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#0eadfeff","#000000ff","#ffd29bff","#fea4feff","#fea4feff","#eb4ab4ff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#a9a7aaff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#a9a7aaff","#fea4a6ff","#fea4a6ff","#000000ff"],["#0eadfeff","#0eadfeff","#0eadfeff","#7543feff","#7543feff","#7543feff","#0eadfeff","#0eadfeff","#000000ff","#ffd29bff","#ffd29bff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#fea4feff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000"],["#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#000000ff","#000000ff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#ffd29bff","#000000ff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000"],["#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#7543feff","#000000ff","#a9a7aaff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000"],["#7543feff","#7543feff","#7543feff","#00000000","#00000000","#00000000","#7543feff","#7543feff","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#000000ff","#a9a7aaff","#a9a7aaff","#000000ff","#00000000","#00000000","#00000000"],["#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#000000ff","#000000ff","#000000ff","#00000000","#00000000","#00000000"]];
const img = document.querySelector('img');
img.src = pixels2Base64(nyanCat);
img 
  width: 250px;
  height: auto;
  image-rendering: pixelated;
&lt;img&gt;

【讨论】:

以上是关于使用图像标签在网页上显示原始像素数组的主要内容,如果未能解决你的问题,请参考以下文章

网页设计中为啥随便插入一个标签在dreamweaver中都与左边有一个小边距,预览也有边距,这个

为啥网页上没有显示高度和宽度?

干货 |HTML图像标签的基本特点和相关属性

jsp中怎样把图片显示在界面上?

我如何在网站上显示来自Google云端硬盘的图像?

网页上常用的图片格式及使用场景