代码之家  ›  专栏  ›  技术社区  ›  Afshin Mehrabani Mohd Jafar

在画布上应用平滑过滤器

  •  0
  • Afshin Mehrabani Mohd Jafar  · 技术社区  · 8 年前

    enter image description here

    我想做的是添加一个过滤器来去除方框之间的边界,使图片更加一致,这样矩形就不会太明显,也不会太平滑。我可以使用什么过滤器或算法来实现这一目标?

    3 回复  |  直到 8 年前
        1
  •  2
  •   Emil S. Jørgensen    8 年前

    使用 getImageData drawImage 在主画布上。

    var c = document.body.appendChild(document.createElement("canvas"));
    var size = c.width = c.height = 50;
    var tileSize = 5;
    var padding = 0;
    var ctx = c.getContext("2d");
    c.style.width = c.style.height = "400px";
    for (var x = 0; x < size; x += tileSize) {
      for (var y = 0; y < size; y += tileSize) {
        ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
        ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
      }
    }
    
    function blur(canvas) {
      var ctx = canvas.getContext("2d");
      var tempCanvas = canvas.cloneNode();
      var tempCTX = tempCanvas.getContext("2d");
      var divideBy = 9;
      for (var x = 0; x < size; x += 1) {
        for (var y = 0; y < size; y += 1) {
          var data = ctx.getImageData(x - 1, y - 1, 3, 3).data;
          var arr = [0, 0, 0, 1];
          for (var dataIndex = 0; dataIndex < data.length; dataIndex += 4) {
            arr[0] += (data[dataIndex] === 0 ? 0 : data[dataIndex] / divideBy);
            arr[1] += (data[dataIndex + 1] === 0 ? 0 : data[dataIndex + 1] / divideBy);
            arr[2] += (data[dataIndex + 2] === 0 ? 0 : data[dataIndex + 2] / divideBy);
          }
          arr = arr.map(function(a) {
            return Math.round(a);
          });
          tempCTX.fillStyle = "rgba(" + (arr.join(',')) + ")";
          tempCTX.fillRect(x, y, 1, 1);
        }
      }
      ctx.drawImage(tempCanvas, 0, 0);
    }
    //TEST
    var output = document.body.appendChild(document.createElement("p"));
    var step = 0;
    
    function blurStep() {
      blur(c);
      step++;
      output.innerHTML = "Blur step: " + step;
      if (step < 20) {
        setTimeout(blurStep, 1000 / 60);
      }
    }
    setTimeout(blurStep, 1000);

    稍微编辑一下,删除了当我的函数超出现有画布时生成的黑色边框:

    var output = document.body.appendChild(document.createElement("p"));
    var c = document.body.appendChild(document.createElement("canvas"));
    var size = c.width = c.height = 50;
    var tileSize = 5;
    var padding = 0;
    var ctx = c.getContext("2d");
    c.style.width = c.style.height = "800px";
    for (var x = 0; x < size; x += tileSize) {
      for (var y = 0; y < size; y += tileSize) {
        ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
        ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
      }
    }
    
    function blur(canvas) {
      var ctx = canvas.getContext("2d");
      var tempCanvas = canvas.cloneNode();
      var tempCTX = tempCanvas.getContext("2d");
      var divideBy = 9;
      for (var x = 0; x < size; x += 1) {
        for (var y = 0; y < size; y += 1) {
          var X = (x >= 1 ? x - 1 : x);
          var Y = (y >= 1 ? y - 1 : y);
          var overflowX = size - X < 3;
          var overflowY = size - Y < 3;
          var data = ctx.getImageData(X, Y, 3, 3).data;
          if (overflowX) {
            var i = 8;
            do {
              data[i] = data[i - 4];
              data[i + 1] = data[i - 4 + 1];
              data[i + 2] = data[i - 4 + 2];
              i += 12;
            } while (i <= 32);
          }
          if (overflowY) {
            var i = 24;
            do {
              data[i] = data[i - 12];
              i++;
            } while (i <= 32);
          }
          var arr = [0, 0, 0, 1];
          for (var dataIndex = 0; dataIndex < data.length; dataIndex += 4) {
            arr[0] += (data[dataIndex] === 0 ? 0 : data[dataIndex] / divideBy);
            arr[1] += (data[dataIndex + 1] === 0 ? 0 : data[dataIndex + 1] / divideBy);
            arr[2] += (data[dataIndex + 2] === 0 ? 0 : data[dataIndex + 2] / divideBy);
          }
          arr = arr.map(function(a) {
            return Math.round(a);
          });
          tempCTX.fillStyle = "rgba(" + (arr.join(',')) + ")";
          tempCTX.fillRect(x, y, 1, 1);
        }
      }
      ctx.drawImage(tempCanvas, 0, 0);
    }
    //TEST
    var step = 0;
    var steps = 20;
    
    function blurStep() {
      blur(c);
      step++;
      output.innerHTML = "Blur step: " + step + "/" + steps;
      if (step < steps) {
        requestAnimationFrame(blurStep);
      }
    }
    requestAnimationFrame(blurStep);
        2
  •  2
  •   Daniel Mizerski    8 年前

    做这件事是为了好玩,而不是真正的帮助。但你可以得到基本的想法,继续阅读:)

    some docs about manipulation inside canvas )

    然后-做你想做的。例如,我会做以下事情:

    const channelResolution = 4; // if you don't have somehow alpha channel - make it 3.
    const lineDistance = imageWidth * channelResolution;
    const imageData = [ /* your ImageData from canvas */];
    
    const [r, g, b, a] = [0, 1, 2, 3]; // directions inside channels
    const [left, right, up, down] = [
        -channelResolution,
        channelResolution,
        -lineDistance,
        lineDistance]; // directions inside pixels
    
    // return array of channel values in given directions
    const scan = (data, scanChannel, scanPixel, scanDirections) => {
        const scanResult = [];
    
        scanResult.push(data[scanPixel + scanChannel]);
        return scanResult.concat(scanDirections.map(direction => {
            return data[scanPixel + scanChannel + direction];
        }));
    };
    
    // mixer filter
    const mixChannel = (array) => {
        let sum = 0;
        array.map(channel => sum+=channel);
        return sum / (array.length + 1);
    };
    
    // blur edge filter/shader
    const blurEdges = () => {
      const resultData = clone(imageData); // (you can do json transformation)
      for(let pointer = 0; pointer < imageData * channelResolution - channelResolution; pointer += channelResolution) {
        const [red, green, blue, alpha] = [
            mixChannel(scan(imageData, r, pointer, [up, left, down, right])),
            mixChannel(scan(imageData, g, pointer, [up, left, down, right])),
            mixChannel(scan(imageData, b, pointer, [up, left, down, right])),
            mixChannel(scan(imageData, a, pointer, [up, left, down, right])),
        ];
    
        resultData[pointer + r] = red;
        resultData[pointer + g] = green;
        resultData[pointer + b] = blue;
        resultData[pointer + a] = alpha;
      }
      return resultData; // or putImageData
    }
    
        3
  •  1
  •   Blindman67    8 年前

    模糊有很多种方法。不幸的是,它们中的大多数都是错误的,导致图像比原始图像更暗。模糊不应失去最明亮的部分

    我从 Emil S.Jrgensen 并修正了所用的数学公式,使图像亮度不会因图像模糊而受到影响。

    对于每个通道r、g、b,获得每个附近像素的平方平均值,并使用其平方比来设置新的模糊像素

    meanR = Math.sqrt*((r1 * r1 + r2 * r2 + r3 * r3)/3); 绿色和蓝色也一样。不是alpha。

    我修改的代码有点像黑客,我本来打算把它的重要部分放在一条评论中,但不适合

    很容易看出区别,所以你甚至不需要并排。

    var c = document.body.appendChild(document.createElement("canvas"));
    var size = c.width = c.height = 50;
    var tileSize = 5;
    var padding = 0;
    var ctx = c.getContext("2d");
    c.style.width = c.style.height = "400px";
    for (var x = 0; x < size; x += tileSize) {
      for (var y = 0; y < size; y += tileSize) {
        ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
        ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
      }
    }
    
    function blur(canvas) {
       
       var r,g,b,i,c;
      var s = (v)=>Math.sqrt(v);          // sqrt of a value
      var a = () => d[i] * d[i++];        // sqrt pixel channel and step to next
      var ctx = canvas.getContext("2d");
      var tempCanvas = canvas.cloneNode();
      var tempCTX = tempCanvas.getContext("2d");
      var divideBy = 9;
      for (var x = 0; x < size; x += 1) {
        for (var y = 0; y < size; y += 1) {
    
          var d = ctx.getImageData(x - 1, y - 1, 3, 3).data;
          //============================
          // Change start by BM67
          //============================
          c = i = r = g = b = 0;
          while(i < 36){ 
            if(d[i+3] !== 0){ r += a(); g += a(); b += a(); i ++; c++; }
            else { i+= 4 }
          }
          tempCTX.fillStyle = `rgb(${s(r/c)|0},${s(g/c)|0},${s(b/c)|0})`;
          //============================
          // End change by BM67
          //============================
          tempCTX.fillRect(x, y, 1, 1);
        }
      }
      ctx.drawImage(tempCanvas, 0, 0);
    }
    //TEST
    var output = document.body.appendChild(document.createElement("p"));
    var step = 0;
    
    function blurStep() {
      blur(c);
      step++;
      output.innerHTML = "Blur step: " + step;
      if (step < 20) {
        setTimeout(blurStep, 1000 / 60);
      }
    }
    setTimeout(blurStep, 1000);
    推荐文章