Есть ли более быстрый способ, чем при l oop для порогового изображения в javascript? - PullRequest
0 голосов
/ 24 января 2020

Я хочу выполнять интерактивную настройку порога больших изображений на стороне клиента с помощью ползунка. Возможно ли пороговое значение изображения в javascript для получения двоичного изображения без использования for-l oop для всех пикселей? И если да, то быстрее?

1 Ответ

0 голосов
/ 24 января 2020

Это можно сделать, используя только 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");

Загрузите изображение в градациях серого в greyscaleImageCanvas, затем выполните следующие две операции, чтобы выполнить шаг 1, где thresh_str - шестнадцатеричная строка для порогового значения между 0-FF для каждого из RGB

        //(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);

Этот метод оказывается в 3-5 раз быстрее, чем for-l oop, по крайней мере Chrome 79 для Ma c и 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...