我正在尝试将JavaScript中的画布转换为pgm。 在我转换文件并将其下载到我的桌面后,它具有pgm扩展名,但是当我用编辑器打开它时,它被编码为png。 我认为如果JavaScript无法识别您要转换为的扩展名,它会默认将其转换为png。 所以JavaScript可以转换为png,jpeg等,但不能转换为pgm?
我的问题是可以使用JavaScript将canvas转换为pgm,如果没有,可以使用任何可用的插件或Js库。
function image_download() {
canvas = $('#canvas').get(0);
var image = new Image();
image.src = canvas.toDataURL("image/pgm");
var download = document.createElement('a');
download.href = image.src;
download.download = 'map.pgm';
download.click();
}
答案 0 :(得分:2)
要将画布保存为PGM,我只是前往http://netpbm.sourceforge.net/doc/pgm.html详细说明了文件格式,并使用该信息创建了一个将画布转换为PGM文件夹的功能。
该文件有一个描述文件和内容的标题,然后是一个二进制数组,值为8位或16位
首先从标题开始并将其创建为字符串
const CR = "\n";
var header = "P5" // required
header += CR; // add whitespace char
header += canvas.width;
header += CR; // add whitespace char
header += canvas.height;
header += CR; // add whitespace char
标题不完整,但我们需要获取像素值的最大值,以便转换图像并找到最大值。
因此,通过2D上下文获取像素数据。
var imgData = ctx.getImageData(0,0,ctx.canvas.width,ctx.height);
您可以选择所需的数据。格式指定某些值的对数刻度,而其他值则为线性。如果保持伽马值很重要,我会留给你找到转换。 (注意实际上只有在你计划广播或保留光值时才需要,例如天文图像)
有一个实用工具可以从线性转换为BT.709伽玛函数。请参阅链接的文档
我添加了一些简单的格式。两个是16位,最大值为3 * 255。
如果要从jpeg编码图像转换,最好从图像中提取亮度,首先将像素RGB转换为HSL并仅将L保存为8位值,这将阻止jpegs质量差的色调和饱和度数据影响结果(jpeg附近有无损灰度数据)
const formats = {
mean : {
convert (r, g, b) { bin[j++] = ((r + g + b) / 3 ) | 0 },
dataStore (w, h) { return new Uint8Array(w * h) },
},
summed : {
convert (r, g, b) { bin[j++] = r + g + b },
dataStore (w, h) { return new Uint16Array(w * h) },
},
meanLog : {
convert (r, g, b) { bin[j++] = Math.sqrt((r * r + g * g + b * b)/ 3) | 0 },
dataStore (w, h) { return new Uint8Array(w * h) },
},
summedLog : {
convert (r, g, b) { bin[j++] = Math.sqrt(r * r + g * g + b * b) | 0 },
dataStore (w, h) { return new Uint16Array(w * h) },
}
}
var imgData = ctx.getImageData(0,0,ctx.canvas.width,ctx.canvas.height);
var format = formats.mean;
var bin = format.dataStore(canvas.width, canvas.height);
var r,g,b,summed,mean,summedLog,meanLog;
var i = 0;
var j = 0;
var max = 0;
var d = imgData.data; // shorthand var to pixel data
while(i < d.length){
format.convert(d[i++],d[i++],d[i++]);
max = Math.max(bin[j-1],max);
i++; // skip alpha
}
header += max; // max value.
header += CR; // add whitespace char
// We have all that is needed to create the file so first convert
// header string to ascII
var fileBin = new Uint8Array(header.length + bin.length);
for(i = 0; i < header.length; i++){
fileBin[i] = header.charCodeAt(i) & 0b01111111; // strip of top bit just in case superstitious coder reads this
}
fileBin.set(new Uint8Array(bin.buffer),header.length); // add the pixel data
// fileBin is the 8bit bin of the PGM file
// Note transparent pixels are black.
// Note I do not check for all black. Format doc indicates that a max val in the head of zero is illegal. What that means you will have to find out.
就是这样。您可以将其转换为Base64编码,或者只是按原样保存
请注意,在此答案之前,我从不使用此图像格式或听说过它,上面的代码是对答案顶部链接的文档的快速解释。如果它有效,我不知道因为我没有任何东西可以测试它。代码没有语法和运行时错误。
它更像是指导如何为特定格式创建二进制文件
我想我还会添加一种格式来处理JPEG图像,这只需要很好的RGB到HSL功能。
// NOTE to prevent data loss the out put of lum has been
// scaled from the normal 0-100 to 0-255 for the example PGM image format
// If you use this function to get HSL values compatible with HSL CSS
// colours change the multipliers from 255 to 100. Commented with [*1]
function rgb2hsl(r,g,b){ // integers in the range 0-255
var minC, maxC, dif, h, l, s;
h = l = s = 0;
r /= 255; // normalize channels
g /= 255;
b /= 255;
min = Math.min(r, g, b);
max = Math.max(r, g, b);
if(min === max){ // [*1] no colour so early exit
return {
h, s,
l : Math.floor(min * 255), // this normally is 0-100
}
}
dif = max - min;
l = (max + min) / 2;
if (l > 0.5) { s = dif / (2 - max - min) }
else { s = dif / (max + min) }
if (max === r) {
if (g < b) { h = (g - b) / dif + 6.0 }
else { h = (g - b) / dif }
} else if(max === g) { h = (b - r) / dif + 2.0 }
else {h = (r - g) / dif + 4.0 }
h = Math.floor(h * 60);
s = Math.floor(s * 100);
l = Math.floor(l * 255); // [*1] this normally is 0-100
return {h, s, l};
},
然后将以下内容添加到格式对象
formats.jpegLumPreserve = {
convert (r, g, b) { bin[j++] = rgb2hsl(r + g + b).l},
dataStore (w, h) { return new Uint8Array(w * h) },
}
如果您从最初存储为jpeg(jpg)的图像中保存,则应使用此格式
format = formats.jpegLumPreserve;
因为总有一些聪明啊...需要吹捧感知亮度转换的人我也会加上它,因为它们通常会弄错。正如我认为转换是一个旧笑话的转换,转换基于2/7/1规则并在对数空间中完成。
formats.perceptual = {
convert (r, g, b) { bin[j++] = Math.sqrt(r * r * 0.2 + g * g * 0.7 + b * b * 0.1) | 0},
dataStore (w, h) { return new Uint8Array(w * h) },
}
这是基于成年人平均感知红,绿和蓝3原色的能力,并将灰度(亮度)结果与感知的颜色亮度相匹配。