簡體   English   中英

JavaScript將畫布轉換為PGM

[英]JavaScript Convert Canvas To PGM

我正在嘗試將JavaScript中的canvas轉換為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();
}

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位值,以從圖像中提取亮度,這將阻止jpeg的質量差的色相和飽和度數據影響結果( 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圖像,而該格式只需要HSL功能具有良好的RGB。

   // 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};
    },

然后將以下內容添加到format對象

    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種原色並將灰色(亮度)結果與所感知的顏色亮度相匹配的能力。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM