簡體   English   中英

有沒有比 for 循環更快的方法來對 javascript 中的圖像進行閾值處理?

[英]Is there a faster way than a for loop for thresholding an image in javascript?

我想使用滑塊對大圖像進行客戶端實時交互式閾值處理。 是否可以在 javascript 中對圖像進行閾值處理以生成二進制圖像而不對所有像素使用 for 循環? 如果是這樣,它會更快嗎?

這可以僅使用 globalCompositeOperations 來完成,分為兩個階段。

  1. 將低於閾值的所有像素設置為 0(黑色)。
  2. 使用定義 0/0 = 0 的算法自行“划分”此圖像

定義三個畫布,一個在屏幕上,一個在屏幕外保存灰度圖像,另一個在屏幕外“工作”畫布。

    //--- on-screen canvas
    var onScreenCanvas=document.getElementById("canvasTest");
    var ctxOnScreen=onScreenCanvas.getContext("2d");

    //--- off-screen working canvas
    var drawingCanvas = document.createElement('canvas');
    var ctx=drawingCanvas.getContext("2d");

    //--- off-screen canvas to store the greyscale image
    var greyscaleImageCanvas = document.createElement('canvas');
    var ctxGreyscaleImage=greyscaleImageCanvas.getContext("2d");

將灰度圖像加載到thresh_str上,然后通過以下兩個操作實現步驟1,其中thresh_str是每個RGB的0-FF之間閾值的十六進制字符串

        //(1a) Threshold the image on the offscreen working canvas, 
        // reducing values above threshold to have threshold value
        ctx.drawImage(greyscaleImageCanvas, 0, 0);
        ctx.globalCompositeOperation='darken';
        ctx.fillStyle=thresh_str;
        ctx.fillRect(0,0, drawingCanvas.width, drawingCanvas.height);

        //(1b) Set everything *below* threshold to 0 (black) since that part is unchanged
        // from the original image. Pixels above threshold are all non-zero.
        ctx.globalCompositeOperation='difference';
        ctx.drawImage(greyscaleImageCanvas, 0, 0);

沒有為 HTML globalCompositeOperations 定義直接的“划分”操作,但是有一個“顏色減淡”,它將底層除以倒置的頂層。 因此,通過首先制作步驟 1 輸出的反轉副本,然后使用顏色減淡操作(它定義 0/0=0)在除法之前“取消反轉”它,可以實現所需的結果。 結果是非零(閾值以上)像素變為 1,零(亞閾值)像素保持為零。

        //(2a) Copy the result of (1b) to the onscreen canvas
        ctxOnScreen.globalCompositeOperation='copy';
        ctxOnScreen.drawImage(drawingCanvas, 0, 0);

        //(2b) Invert the result of step (1b) so that it can be 'un-inverted' by color dodge
        ctx.globalCompositeOperation='difference';
        ctx.fillStyle='white';
        ctx.fillRect(0,0,onScreenCanvas.width,onScreenCanvas.height);

        //(2c) 'color-dodge' the results of (1b) with it's own inverse (2b) 
        ctxOnScreen.globalCompositeOperation='color-dodge';
        ctxOnScreen.drawImage(drawingCanvas, 0, 0);

這種方法似乎比 for 循環快 3-5 倍,至少在 Mac 上的 Chrome 79 和 android (Huawei P10) JSPerf

 function img2grey(canvasContext) { canvasContext.globalCompositeOperation='color'; canvasContext.fillStyle='white'; canvasContext.fillRect(0,0,onScreenCanvas.width,onScreenCanvas.height); } //--- on-screen canvas var onScreenCanvas=document.getElementById("canvasTest"); var ctxOnScreen=onScreenCanvas.getContext("2d"); //--- off-screen working canvas var drawingCanvas = document.createElement('canvas'); var ctx=drawingCanvas.getContext("2d"); //--- off-screen canvas to store the greyscale image var greyscaleImageCanvas = document.createElement('canvas'); var ctxGreyscaleImage=greyscaleImageCanvas.getContext("2d"); var image = new Image(); function thresholdImage(thresh_val) { if(thresh_val.length == 1){ thresh_val = '0' + thresh_val; } thresh_str = '#'+thresh_val+thresh_val+thresh_val; ctxOnScreen.clearRect(0, 0, onScreenCanvas.width, onScreenCanvas.height); ctx.clearRect(0, 0, drawingCanvas.width, drawingCanvas.height); //----- (1) Threshold the image on the offscreen working canvas, // reducing values above threshold to have threshold value ctx.drawImage(greyscaleImageCanvas, 0, 0); ctx.globalCompositeOperation='darken'; ctx.fillStyle=thresh_str; ctx.fillRect(0,0,onScreenCanvas.width,onScreenCanvas.height); //----- (2) Set everything *below* threshold to 0 (black) since that part is unchanged // from the original image ctx.globalCompositeOperation='difference'; ctx.drawImage(greyscaleImageCanvas, 0, 0); //----- (3) Copy the result to the onscreen canvas ctxOnScreen.globalCompositeOperation='copy'; ctxOnScreen.drawImage(drawingCanvas, 0, 0); //----- (4) Invert the result of step (2) so that it can be 'un-inverted' by color dodge ctx.globalCompositeOperation='difference'; ctx.fillStyle='white'; ctx.fillRect(0,0,onScreenCanvas.width,onScreenCanvas.height); //----- (5) 'color-dodge' the results of (2) with it's own inverse (4) //----- This makes use of 0/0 defined as 0 in this globalCompositeOperation, //----- so that non-zero (suprathreshold) pixels become 1, zero (sub-threshold) pixels stay zero //~ ctxOnScreen.globalCompositeOperation='color-dodge'; ctxOnScreen.globalCompositeOperation='color-dodge'; ctxOnScreen.drawImage(drawingCanvas, 0, 0); } image.onload = function() { onScreenCanvas.width = image.width; onScreenCanvas.height = image.height; drawingCanvas.width = image.width; drawingCanvas.height = image.height; greyscaleImageCanvas.width = image.width; greyscaleImageCanvas.height = image.height; //!!NB Doesn't work on chrome for local files, use firefox // https://stackoverflow.com/questions/45444097/the-canvas-has-been-tainted-by-cross-origin-data-local-image ctxGreyscaleImage.drawImage(image, 0, 0); img2grey(ctxGreyscaleImage); thresholdImage((Math.round(rng.value)).toString(16)); }; var rng = document.querySelector("input"); var listener = function() { window.requestAnimationFrame(function() { thresholdImage( (Math.round(rng.value)).toString(16) ); }); }; rng.addEventListener("mousedown", function() { listener(); rng.addEventListener("mousemove", listener); }); rng.addEventListener("mouseup", function() { rng.removeEventListener("mousemove", listener); }); image.src = "https://i.imgur.com/vN0NbVu.jpg";
 .slider-width100 { width: 255px; }
 <html> <head> </head> <body> <canvas id="canvasTest"></canvas> <input class="slider-width100" type="range" min="0" max="254" value="122" /> </body> </html>

暫無
暫無

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

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