【问题标题】:How to get image color mode (CMYK, RGB ...) in Javascript如何在Javascript中获取图像颜色模式(CMYK,RGB ...)
【发布时间】:2016-10-25 19:36:45
【问题描述】:

JavaScript 可以检查图像的颜色模式吗?

我对此进行了很多搜索,但我唯一看到的是颜色模式转换(但转换希望您设置原始颜色模式)


我添加这个:--allow-file-access-from-files 可以完全控制画布中的 img,因为我使用的是 GoogleChrome

HTML

<canvas id="canvas" width=6000 height=7919></canvas>

Js

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

var img = new Image();
img.crossOrigin = "anonymous";
img.onload = start;
img.src = "file:///D:/vincent-van-gogh-the-starry-night-picture-157544-2.png";

【问题讨论】:

  • 没有相应的 API; canvas API 处理 CSS (RGB) 颜色空间方法(包括 HSL 表示),当然显示硬件是 RGB。你到底在处理什么类型的图像?
  • 你没有提到你用来获取图像的方法。如果您正在采购自己的图像并且不允许第三方自定义,那么您可以在内容管道中以某种方式确保特定的颜色模式(例如告诉您的艺术家/摄影师以特定格式为您提供图像),然后只需总是假设相同的模式。
  • 如果您将图像加载到画布中然后提取像素,那么无论最初的图像格式是什么,如果浏览器完全可以理解,您都会得到 RGB。
  • 如果您允许自定义图像内容,那么您可以创建一个用于提交图像的过程,该过程在服务器端执行您需要的任何颜色模式转换,并继续在客户端。
  • @MerlynMorgan-Graham 我添加了一些代码

标签: javascript image-processing html5-canvas


【解决方案1】:

是的 - 基本上 JavaScript 能够确定 png 的颜色模式,但因此需要 1.将png转换为base64 2.将base64转换为字节数组 3. 读取/解析有关png规范的数组

一种可能的方法如下所示:

var PNG = {

        parse: function(imgTag) {
            var base64 = PNG.asBase64(imgTag);
            var byteData = PNG.utils.base64StringToByteArray(base64);
            var parsedPngData = PNG.utils.parseBytes(byteData);

            return PNG.utils.enrichParsedData(parsedPngData);
        },

        asBase64: function(imgTag) {
            var canvas = document.createElement("canvas");
            canvas.width = imgTag.width; 
            canvas.height = imgTag.height; 
            var ctx = canvas.getContext("2d"); 
            ctx.drawImage(imgTag, 0, 0); 
            var dataURL = canvas.toDataURL("image/png");
            return dataURL.split('base64,')[1];
        },

        utils: {
            base64StringToByteArray: function(base64String) {
                //http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
                var byteCharacters = atob(base64String);
                var byteNumbers = new Array(byteCharacters.length);
                for (var i = 0; i < byteCharacters.length; i++) {
                    byteNumbers[i] = byteCharacters.charCodeAt(i);
                }
                return new Uint8Array(byteNumbers);
            },

            parseBytes: function(bytes) {
                var pngData = {};
                //see https://en.wikipedia.org/wiki/Portable_Network_Graphics

                //verify file header
                pngData['headerIsValid'] = bytes[0] == 0x89
                            && bytes[1] == 0x50
                            && bytes[2] == 0x4E
                            && bytes[3] == 0x47
                            && bytes[4] == 0x0D
                            && bytes[5] == 0x0A
                            && bytes[6] == 0x1A
                            && bytes[7] == 0x0A

                if (!pngData.headerIsValid) {
                    console.warn('Provided data does not belong to a png');
                    return pngData;
                }

                //parsing chunks
                var chunks = [];

                var chunk = PNG.utils.parseChunk(bytes, 8);
                chunks.push(chunk);

                while (chunk.name !== 'IEND') {
                    chunk = PNG.utils.parseChunk(bytes, chunk.end);
                    chunks.push(chunk);
                }

                pngData['chunks'] = chunks;
                return pngData;
            },

            parseChunk: function(bytes, start) {
                var chunkLength = PNG.utils.bytes2Int(bytes.slice(start, start + 4));

                var chunkName = '';
                chunkName += String.fromCharCode(bytes[start + 4]);
                chunkName += String.fromCharCode(bytes[start + 5]);
                chunkName += String.fromCharCode(bytes[start + 6]);
                chunkName += String.fromCharCode(bytes[start + 7]);

                var chunkData = [];
                for (var idx = start + 8; idx<chunkLength + start + 8; idx++) {
                    chunkData.push(bytes[idx]);
                }

                //TODO validate crc as required!

                return {
                    start: start,
                    end: Number(start) + Number(chunkLength) + 12, //12 = 4 (length) + 4 (name) + 4 (crc)
                    length: chunkLength,
                    name: chunkName,
                    data: chunkData,
                    crc: [
                        bytes[chunkLength + start + 8],
                        bytes[chunkLength + start + 9],
                        bytes[chunkLength + start + 10],
                        bytes[chunkLength + start + 11]
                    ],
                    crcChecked: false
                };
            },

            enrichParsedData: function(pngData) {
                var idhrChunk = PNG.utils.getChunk(pngData, 'IDHR');

                //see http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html
                pngData.width = PNG.utils.bytes2Int(idhrChunk.data.slice(0, 4));
                pngData.height = PNG.utils.bytes2Int(idhrChunk.data.slice(4, 8));
                pngData.bitDepth = PNG.utils.bytes2Int(idhrChunk.data.slice(8, 9));
                pngData.colorType = PNG.utils.bytes2Int(idhrChunk.data.slice(9, 10));
                pngData.compressionMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(10, 11));
                pngData.filterMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(11, 12));
                pngData.interlaceMethod = PNG.utils.bytes2Int(idhrChunk.data.slice(12, 13));

                pngData.isGreyScale = pngData.colorType == 0 || pngData.colorType == 4;
                pngData.isRgb = pngData.colorType == 2 || pngData.colorType == 6;
                pngData.hasAlpha = pngData.colorType == 4 || pngData.colorType == 6;
                pngData.hasPaletteMode = pngData.colorType == 3 && PNG.utils.getChunk(pngData, 'PLTE') != null;

                return pngData;
            },

            getChunks: function(pngData, chunkName) {
                var chunksForName = [];
                for (var idx = 0; idx<pngData.chunks.length; idx++) {
                    if (pngData.chunks[idx].name = chunkName) {
                        chunksForName.push(pngData.chunks[idx]);
                    }
                }
                return chunksForName;
            },

            getChunk: function(pngData, chunkName) {
                for (var idx = 0; idx<pngData.chunks.length; idx++) {
                    if (pngData.chunks[idx].name = chunkName) {
                        return pngData.chunks[idx];
                    }
                }
                return null;
            },

            bytes2Int: function(bytes) {
                var ret = 0;

                for (var idx = 0; idx<bytes.length; idx++) {
                    ret += bytes[idx];
                    if (idx < bytes.length - 1) {
                        ret = ret << 8;
                    }
                }

                return ret;
            }
        }
    }

可以如下使用:

var pngData = PNG.parse(document.getElementById('yourImageId'));
console.log(pngData);

它包含一些信息,如颜色模式、块数、块本身、位深度等。

希望对你有帮助。

【讨论】:

  • 以上代码适用于 png,但不适用于 jpeg 图像。如何获取 jpeg 图像的 parseBytes?" ReferenceError: pngData is not defined at eval (eval at parse (file:///D: /poc/color.html:13:33), :1:1) 在 Object.parse (file:///D:/poc/color.html:19:33) 在点击 (file:// /D:/poc/color.html:160:7) 在 HTMLButtonElement.onclick (file:///D:/poc/color.html:183:30)"
  • 是的,上面的代码是专门为PNG解析而设计的。如果您需要 jpeg 图像的等效实现,则需要按照 jpeg 格式规范实现它。在这种情况下,只有方法“PNG.asBase64(imgTag);”和“PNG.utils.base64StringToByteArray(base64);”对你有用。
  • @MartinAckermann 解析PNG文件以检查每个像素的颜色怎么样?我想在 Javascript 中执行此操作,不幸的是,ChunkyPNG 在 Javascript 中不可用...我要遵循的 Ruby 代码是:image = ChunkyPNG::Image.from_file(file)。 .....然后 .... if image[col, row] == ChunkyPNG::Color::BLACK mask[row, col] = false else mask[row, col] = true end
  • 目前我使用 FileReader API 从 img 标签中获取文件,所以我有类似 File {name: "maze_text.png", lastModified: 1565158981565, lastModifiedDate: Wed Aug 2019 14:23 :01 GMT+0800, webkitRelativePath: "", size: 490, ...}
  • 我也可以直接从图像标签的源中获取文件,并获得一个数据 url。例如数据:图像/ PNG; BASE64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAlCAYAAAAA7LqSAAAABmJLR0QA / WD / AP + gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3gsYEgMhfnutHQAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAABTklEQVRYw91Y2wrDMAjVkP // ZfcwWkq3eNeUBgYr5OLRc4wGAYDgBWMCABD9YkHE5SLL / Iy9pYGIMOAlY1g94 / FYG7WqxhU0Ip7fRLSkF0c7zp .............等跨度>
【解决方案2】:

JavaScript 可以检查图像的颜色模式吗?

是和不是。

,如果您使用普通图像加载。浏览器支持的所有图片在通过Image对象(HTMLImageElement)交给我们之前都会转换成RGB(A)。

此时,除了大​​小和来源之外,没有显示有关原始格式和颜色模型的信息。

Canvas 仅将 RGBA 视为显示的所有其他元素,并且不存在处理 CMYK 等颜色模型的本机方法。

您始终可以读取 RGBA 值并天真地转换为 CMYK,但是,如果没有目标打印机的 ICC 配置文件,由于 RGB 色域更广,打印机以及打印介质的特性,您会遇到各种问题必须通过 ICC 配置文件进行调整。这意味着在大多数情况下,结果不会产生正确的颜色。

您必须设置服务器端解决方案才能使其正常工作。将图像发送到服务器,转换为 CMYK/应用 ICC,然后发送到打印机。 ImageMagick 可能是解决问题的一种方法。浏览器根本不适合(也不打算)用于面向打印的处理。

是的,如果您愿意手动编写图像解析器以及 ICC 解析器。您需要支持多种格式和格式组合,并且能够在导入时应用 ICC/gamma 以及通过目标 ICC 导出到 CMYK(意味着您需要支持 XYZ/LAB 色彩空间)。

导出需要您编写支持 CMYK 颜色空间和 ICC 嵌入的文件编写器。

成熟的过程、错误、不稳定性等将是该过程的自然组成部分,您可能不想立即在生产中使用。

如果你只是在文件的颜色模式之后并且可以像普通一样加载图像你当然可以先通过XHR加载文件,然后使用类型化数组扫描文件以找到描述颜色格式的信息取决于格式。然后将类型化数组作为 Blob -> object-URL 传递为图像加载,通过画布并将每个像素转换为 CMYK 值。

但从这一点开始,您将遇到与上述相同的问题,并且不会逃避写入文件写入器等。

【讨论】:

  • 我只是想在客户端本身获取文件的颜色模式。它是怎么做到的,你能深入解释一下吗?
【解决方案3】:

您可以使用exifreader 包来读取图像元数据。

import ExifReader from 'exifreader'

const reader = new FileReader()

reader.onload = (event) => {
  const metadata = ExifReader.load(event.target.result, { expanded: true })
  const { file: { 'Color Components': { value } } } = metadata

  switch (value) {
  case 3: console.log('RGB'); break
  case 4: console.log('CMYK'); break
  default: console.log('Unknown')
  }
}

reader.readAsArrayBuffer(instaceOfFileObject)

它不一定是 File 对象,但 ExifReader.load() 期望数组缓冲区作为它的第一个参数。

参考:https://www.get-metadata.com/file-info/color-components

【讨论】:

    猜你喜欢
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-20
    • 1970-01-01
    • 2017-11-08
    • 2011-01-26
    • 1970-01-01
    相关资源
    最近更新 更多