如何从PNG使用javascript(getImageData替代)提取像素信息 [英] How to extract pixel information from PNG using javascript (getImageData alternatives)

查看:284
本文介绍了如何从PNG使用javascript(getImageData替代)提取像素信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想从PNG图片获取像素数据进行处理。目前的方式是使用 canvas.drawImage 后跟 canvas.getImageData example here )。我正在寻找替代方案。



当前方法的问题是浏览器修改受 alpha 影响的像素值,如在这里此处



此问题已询问

只有方法,而不使用画布和 getImageData()是加载PNG文件作为二进制类型数组,并解析代码手动。



这就像老式的做事方式,它是可行的(一直在那里),但你是在自己的一个小项目,更不要说抛出那些



前提条件:





作为开始 - 我有一个小 项目在GitHub ,它解析基于块的音频文件,如AIFF / WAVE / IFF等,并在浏览器中播放 - 不是PNG,可以看一下解析基于二进制/基于块的文件的方法的源代码,以获得至少感觉。



如果你不确定什么基于块的文件:a典型的基于块的文件具有称为魔术标识符的四字节报头,随后是大小或一些misc。



然后将 chunks 放置在其后面,通常包含一个FOURCC(或四个字符代码),然后没有块头的块的大小。原则上:

 MAGIC FOURCC 
SIZE / MISC - 视定义而定
...

CHK1 - Chunk FourCC
SIZE - unsigned long
....数据

CHK2
SIZE
....数据

这种格式原则最初来自CBM Amiga平台,EA / IFF早在80年代中期。



但是在现代,一些供应商已经扩展了基于块的格式,因此对于PNG块,它实际上看起来像这样:



头部(总是8字节和相同的字节值) :

‰PNG(第一个字节为0x89,原因见规格)
CR + LF 0x0C0A
EOC + LF 0x1A0A

Chunks:

 SIZE(4个字节, 。chunk header和crc)
FOURCC(4字节,即IHDR,IDAT)
[... data](长度:SIZE字节,可以为空)
CRC(4字节,包括块类型的crc但不是长度)

(参见上面引用的规范链接)。



这使得通过只支持一些(或所有)块的文件进行解析变得很容易。对于PNG,您需要至少支持():




  • IHDR 必须是第一个块;

  • IDAT 包含图片的宽度,高度,位元深度和颜色类型被分割在多个IDAT块中。这种拆分稍微增加了文件大小,但是使得可以以流的方式生成PNG。 IDAT块包含实际的图像数据,它是压缩算法的输出流。

  • IEND 标记图像结尾。 / li>


如果您打算支持调色板(彩色索引)文件,您还需要支持 PLTE chunk。当您解析IHDR块时,您将能够看到使用什么颜色格式(即RGB数据类型2,或RGBA 6)。



解析很容易所以你最大的挑战可能是支持ICC配置文件(当存在于 iCCP 块中)来调整图像颜色数据。我可能会错过这里,但是IIRC你将需要访问系统的颜色配置文件,以便能够更正颜色信息 - 如果是这样,这将是一个问题,因为我们无法通过JavaScript访问系统信息。您还有各种级别的sRGB颜色空间(如果存在 sRGB 块)。最后是gamma块( gAMA )(还有其他特殊的块与颜色相关)。



第二大挑战是使用INFLATE的解压缩。您可以使用 等项目为您完成此项工作(只是检查许可证)。除此之外,如果你想对数据进行错误检查(推荐),还应该支持CRC检查。



出于安全考虑,应该始终检查字段是否包含



希望这有助于!



示例块解析器:(注意:由于使用Promise的加载器,它将不能在当前IE中工作)。



  function pngParser(buffer){var view = new DataView(buffer),len = buffer.byteLength,magic1,magic2,chunks = [],size,fourCC,crc,offset,pos = 0 ; // current offset infile// check header magic1 = view.getUint32(pos); pos + = 4; magic2 = view.getUint32(pos); pos + = 4; if(magic1 === 0x89504E47&&& magic2 === 0x0D0A1A0A){// parse chunks while(pos > 0); }} //只是加载一个PNG filepngParser.loadXHR = function(url){return new Promise(function(resolve,reject){try {var xhr = new XMLHttpRequest(); xhr.open(GET,url) xhr.onload = function(){if(xhr.status === 200)resolve(xhr.response)xhr.onload = function() else reject(Loading error:+ xhr.statusText)}; xhr.send();} catch(err){reject(err.message)}});}; // USAGE: ----------------------------------------- pngParser.loadXHR(http:// i.imgur.com/YxO8CtB.png\").then(function(buffer){var info = pngParser(buffer); //在这里解析每个块... for(var i = 0,chunks = info.chunks,chunk ; chunk = chunks [i ++];){out(CHUNK:+ chunk.fourCC); out(SIZE:+ chunk.size +bytes); out(OFFSET:+ chunk.offset +字节); out(CRC:0x+(chunk.crc>> 0).toString(16)); out(----------------- --------------);} function out(txt){document.getElementById(out)。innerHTML + = txt +< br>}});  

  body {font:14px monospace}  

 < pre id =out>< / pre>  



从这里你可以提取IHRD找到颜色类型,然后IDAT块s)放气,你几乎完成;)


I am trying to get pixel data from PNG images for processing. The current way is by using canvas.drawImage followed canvas.getImageData (example here). I am looking for alternatives.

The problem with the current approach is that browsers modify pixel values influenced by alpha, as discussed here and here.

This question has been asked before, but no satisfactory answers are available.

解决方案

The only way to do this without using canvas and getImageData() is to load the PNG file as a binary typed array and parse the file in code "manually".

This is like the old-school way of doing things, it's doable (been there, done that), but you are in for a small project on its own, not to mention throwing out those bugs and special case scenarios that tends to follow these kind of projects in the beginning.

Prerequisites:

  • For this you need the PNG specification which you can find here.
  • You need to know how to use typed arrays (for this, a DataView is the most suitable view).
  • PNG files are chunk based and you will need to know how to parse chunks

As a start - I have a small project at GitHub which parses chunk based audio files such as AIFF/WAVE/IFF etc. and plays them in the browser - not PNG, but you can look at the source for one approach to parse binary/chunk based files to get the feel at least.

If you're not sure what chunk based files are: a typical chunk based file has a four byte header called magic identifier, followed by the size or some misc. data depending on the file format definition.

Then chunks are placed right after this containing often a FOURCC (or four character code) and then the size of the chunk without the chunk header. In principle:

    MAGIC FOURCC
    SIZE/MISC    - depending on definition
    ...

    CHK1         - Chunk FourCC
    SIZE         - unsigned long
    .... data

    CHK2
    SIZE
    .... data

This format principle came originally from the CBM Amiga platform and EA/IFF back in mid 80's.

But in modern days some vendors has extended on the chunk based format, so for PNG chunks it will actually look like this:

Header (always 8 bytes and the same byte values):

‰PNG       (first byte is 0x89, see specs for reason)
CR + LF    0x0C0A
EOC + LF   0x1A0A

Chunks:

SIZE      (4 bytes, may be 0. Excl. chunk header and crc)
FOURCC    (4 bytes, ie. IHDR, IDAT)
[...data] (length: SIZE bytes, may be none)
CRC       (4 bytes, incl. crc of chunk type but not length)

(see the referenced specification link above for details).

This makes it easy to parse through the file supporting only some (or all) chunks. For PNG you would need to support at least (source):

  • IHDR must be the first chunk; it contains (in this order) the image's width, height, bit depth and color type.
  • IDAT contains the image, which may be split among multiple IDAT chunks. Such splitting increases filesize slightly, but makes it possible to generate a PNG in a streaming manner. The IDAT chunk contains the actual image data, which is the output stream of the compression algorithm.
  • IEND marks the image end.

If you intend to support palette (color indexed) files you would also need to support the PLTE chunk. When you parse the IHDR chunk you will be able to see what color format is used (ie. type 2 for RGB data, or 6 for RGBA).

Parsing is easy so your biggest challenge will probably be supporting ICC profiles (when present in the iCCP chunk) to adjust the image color data. I could be mistaken here, but IIRC you will need access to the system's color profile as well to be able to correct the color information - if so, that will be a problem as we cannot access system information through JavaScript. You have also various levels of sRGB color space (if a sRGB chunk is present). Finally there is the gamma chunk (gAMA) (there are also other special chunks related to colors).

The second biggest challenge would be the decompression which uses INFLATE. You can use a project such as this to do this job for you (just check license). In addition to that, if you want to do error checking on the data (recommended) CRC checking should also be supported.

For security reason you should always check that fields contain the data they're suppose to as well as that reserved space are initialized with either 0 or the defined data.

Hope this helps!

Example chunk parser: (note: due to using Promise for the loader it won't work in current IE).

function pngParser(buffer) {

  var view = new DataView(buffer),
      len = buffer.byteLength,
      magic1, magic2,
      chunks = [],
      size, fourCC, crc, offset,
      pos = 0;  // current offset in "file"

  // check header
  magic1 = view.getUint32(pos); pos += 4;
  magic2 = view.getUint32(pos); pos += 4;

  if (magic1 === 0x89504E47 && magic2 === 0x0D0A1A0A) {

    // parse chunks
    while (pos < len) {

      // chunk header
      size = view.getUint32(pos);
      fourCC = getFourCC(view.getUint32(pos + 4));

      // data offset
      offset = pos + 8;
      pos = offset + size;

      // crc
      crc = view.getUint32(pos);
      pos += 4;

      // store chunk
      chunks.push({
        fourCC: fourCC,
        size: size,
        offset: offset,
        crc: crc
      })
    }

    return {chunks: chunks}
  } 
  else {
      return {error: "Not a PNG file."}
  }

  function getFourCC(int) {
    var c = String.fromCharCode;
    return c((int & 0xff000000) >>> 24) + c((int & 0xff0000) >>> 16) + c((int & 0xff00) >>> 8) + c((int & 0xff) >>> 0);
  }
}

// just to load a PNG file
pngParser.loadXHR = function(url) {

  return new Promise(function(resolve, reject) {
    try {
      var xhr = new XMLHttpRequest();
      xhr.open("GET", url);
      xhr.responseType = "arraybuffer";
      xhr.onerror = function() {reject("Network error.")};
      xhr.onload = function() {
        if (xhr.status === 200) resolve(xhr.response)
        else reject("Loading error:" + xhr.statusText)
      };
      xhr.send();
    } catch (err) {reject(err.message)}
  });
};

// USAGE: ------------------------------------------------

pngParser.loadXHR("http://i.imgur.com/YxO8CtB.png").then(function(buffer) {

  var info = pngParser(buffer);

  // parse each chunk here...
  for (var i = 0, chunks = info.chunks, chunk; chunk = chunks[i++];) {
    out("CHUNK : " + chunk.fourCC);
    out("SIZE  : " + chunk.size + " bytes");
    out("OFFSET: " + chunk.offset + " bytes");
    out("CRC   : 0x" + (chunk.crc>>>0).toString(16));
    out("-------------------------------");
  }

  function out(txt) {document.getElementById("out").innerHTML += txt + "<br>"}
});

body {font: 14px monospace}

<pre id="out"></pre>

From here you can extract the IHRD to find size color type, then IDAT chunk(s) to deflate and your almost done ;)

这篇关于如何从PNG使用javascript(getImageData替代)提取像素信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆