JavaScript将Canvas转换为PGM

时间:2017-05-30 10:42:37

标签: javascript image canvas image-conversion pgm

我正在尝试将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();
}

1 个答案:

答案 0 :(得分:2)

PGM作家

要将画布保存为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亮度保留

我想我还会添加一种格式来处理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原色的能力,并将灰度(亮度)结果与感知的颜色亮度相匹配。