如何在 html5 和 javascript 中编辑像素并删除画布图像中的白色背景

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11472273/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 13:21:11  来源:igfitidea点击:

how to edit pixels and remove white background in a canvas image in html5 and javascript

javascripthtmlcanvas

提问by omega

If I load an image, how can I loop through all its pixels and turn the white ones (or which ever color I specify) to be turned transparent?

如果我加载图像,如何遍历其所有像素并将白色(或我指定的任何颜色)变为透明?

I have an idea on how to do this, but the looping process should be like a 2d array, so it would involve two for loops.

我有一个关于如何做到这一点的想法,但循环过程应该像一个二维数组,所以它会涉及两个 for 循环。

I was thinking I would start on the top row first pixel, iterating to the right, if its a white pixel, then i turn it transparent, and move 1 pixel to the right, if its not white, then I stop. Then in the same row, I start from the left most pixel, and check, if white, I turn it transparent, then move 1 pixel to the left, etc, etc...

我想我会从顶行的第一个像素开始,向右迭代,如果它是白色像素,那么我把它变成透明,然后向右移动 1 个像素,如果它不是白色,那么我停止。然后在同一行,我从最左边的像素开始,检查,如果是白色,我把它变成透明,然后向左移动 1 个像素,等等......

Then I move 1 row down and repeat the whole process..

然后我向下移动 1 行并重复整个过程..

This way I don't remove any white pixels in the actual image.

这样我就不会删除实际图像中的任何白色像素。

回答by Loktar

Its pretty simple to do using getImageDataand putImageDatajust note you can take a pretty significant hit on performance the larger the image gets. You just need to determine if the current pixel is white, then change its alpha to 0.

它使用起来非常简单getImageDataputImageData只需注意,图像越大,性能就会受到相当大的影响。您只需要确定当前像素是否为白色,然后将其 alpha 更改为 0。

Live Demo

现场演示

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    image = document.getElementById("testImage");

canvas.height = canvas.width = 135;
ctx.drawImage(image,0,0);

var imgd = ctx.getImageData(0, 0, 135, 135),
    pix = imgd.data,
    newColor = {r:0,g:0,b:0, a:0};

for (var i = 0, n = pix.length; i <n; i += 4) {
    var r = pix[i],
            g = pix[i+1],
            b = pix[i+2];

        // If its white then change it
        if(r == 255 && g == 255 && b == 255){ 
            // Change the white to whatever.
            pix[i] = newColor.r;
            pix[i+1] = newColor.g;
            pix[i+2] = newColor.b;
            pix[i+3] = newColor.a;
        }
}

ctx.putImageData(imgd, 0, 0);?

It was also asked if you could make the values sort of fuzzy to check. Its really easy, just check if its in a certain range. The following will turn off white to pure white transparent.

还询问您是否可以使值变得模糊以进行检查。真的很简单,只要检查它是否在一定范围内。以下将关闭白色变为纯白色透明。

 // If its white or close then change it
    if(r >=230 && g >= 230 && b >= 230){ 
        // Change the white to whatever.
        pix[i] = newColor.r;
        pix[i+1] = newColor.g;
        pix[i+2] = newColor.b;
        pix[i+3] = newColor.a;
    }

More resources

更多资源

回答by Gabriel Ambrósio Archanjo

You can use an image processing framework to not handle pixel data manually.

您可以使用图像处理框架来不手动处理像素数据。

In the case of MarvinJ, given a color, you can set all pixels to transparent with a single line:

MarvinJ的情况下,给定颜色,您可以使用一行将所有像素设置为透明:

image.setColorToAlpha(0, 0);

Input image:

输入图像

enter image description here

在此处输入图片说明

Result:

结果:

enter image description here

在此处输入图片说明

Runnable Example:

可运行示例:

var canvas = document.getElementById("canvas");
image = new MarvinImage();
image.load("https://i.imgur.com/UuvzbLx.png", imageLoaded);

function imageLoaded(){
 image.setColorToAlpha(0, 0); 
 image.draw(canvas);
}
<script src="https://www.marvinj.org/releases/marvinj-0.8.js"></script>
<div style="width:400px; height:352px;  background-image: linear-gradient(45deg, #808080 25%, transparent 25%),
 linear-gradient(-45deg, #808080 25%, transparent 25%),
 linear-gradient(45deg, transparent 75%, #808080 75%), 
 linear-gradient(-45deg, transparent 75%, #808080 75%);
 background-size: 20px 20px;
 background-position: 0 0, 0 10px, 10px -10px, -10px 0px;">
<canvas id="canvas" width="400" height="352"></canvas>
</div> 

回答by 3.1415926535897932384626433833

Loktar may have a method that 'works', but it's performance is rather dismal. This can be a problem if you have lots of images, you don't want your website to drain laptop/mobile-devices batteries, or if you just want speed. Here is a method that works far more efficiently. Click on the "Run Code Snippet" button for a demo of it in action.

Loktar 可能有一种“有效”的方法,但它的性能相当糟糕。如果您有很多图片,您不希望网站耗尽笔记本电脑/移动设备的电池,或者您只想要速度,这可能会成为一个问题。这是一种更有效的方法。单击“运行代码片段”按钮以查看它的实际演示。

'use-strict'
let fileInput = document.getElementById('fileInput'),
  theCANVAS = document.getElementById('theCanvas'),
  theCANVASctx = theCANVAS.getContext('2d'),
  imgTMP = document.getElementById('imgTMP'),
  rComponent = document.getElementById('r'),
  gComponent = document.getElementById('g'),
  bComponent = document.getElementById('b'),
  aComponent = document.getElementById('a'),
  transColor = "rgba(255, 255, 255, 1)",
  transCode = 0xffffffff;

let makeColorTransparent =
  function(canvasContext, transparentID, width, height) {
    // where all the magic happens
    let theImageData = canvasContext.getImageData(0, 0, width, height),
      theImageDataBufferTMP = new ArrayBuffer(theImageData.data.length),
      theImageDataClamped8TMP = new Uint8ClampedArray(theImageDataBufferTMP),
      theImageDataUint32TMP = new Uint32Array(theImageDataBufferTMP),
      n = theImageDataUint32TMP.length;
    theImageDataClamped8TMP.set(theImageData.data);

    imgDataLoop: while (n--) {
      // effciency at its finest:
      if (theImageDataUint32TMP[n] !== transparentID)
        continue imgDataLoop;
      theImageDataUint32TMP[n] = 0x00000000; // make it transparent
    }
    theImageData.data.set(theImageDataClamped8TMP);
    theCANVASctx.putImageData(theImageData, 0, 0);
  },
  downloadCanvas = function(downloadfilename) {
    theCanvas.toBlob(function(theIMGblob) {
      var thedataURL = URL.createObjectURL(theIMGblob),
        theAtagLink = document.createElement('a');

      theAtagLink.download = '(proccessed)' + downloadfilename;
      document.body.appendChild(theAtagLink);
      theAtagLink.href = thedataURL;
      theAtagLink.click();
    });
  };

fileInput.onchange = function(fileevent) {
  let efiles = fileevent.target.files,
    localTransColor = transColor,
    localTransCode = transCode;

  let cur = efiles.length,
    nextfile = function() {
      if (!cur--) {
        imgTMP.src = '';
        return;
      }
      let fr = new FileReader();
      console.log(efiles[cur]);
      fr.onload = function(dataevt) {
        fr.onload = null;
        let theArrayBuffer = dataevt.target.result,
          theblob = new Blob([theArrayBuffer]);
        imgTMP.src = URL.createObjectURL(theblob);
        imgTMP.onload = function() {
          imgTMP.onload = null;
          let theImagesWidth = imgTMP.naturalWidth,
            theImagesHeight = imgTMP.naturalHeight;

          theCANVAS.width = theImagesWidth;
          theCANVAS.height = theImagesHeight;

          theCANVASctx.fillStyle = localTransColor;
          theCANVASctx.clearRect(
            0,
            0,
            theImagesWidth,
            theImagesHeight
          );
          theCANVASctx.drawImage(imgTMP, 0, 0);
          makeColorTransparent(
            theCANVASctx,
            localTransCode,
            theImagesWidth,
            theImagesHeight
          );

          //now, download the file:
          downloadCanvas(efiles[cur].name);

          //Finally, procced to proccess the next file
          nextfile();
        };
      };
      fr.readAsArrayBuffer(efiles[cur]);
    };
  nextfile();
}

rComponent.oninput = gComponent.oninput =
  bComponent.oninput = aComponent.oninput =
  function() {
    rComponent.value = Math.max(0, Math.min(rComponent.value, 255));
    gComponent.value = Math.max(0, Math.min(gComponent.value, 255));
    bComponent.value = Math.max(0, Math.min(bComponent.value, 255));
    aComponent.value = Math.max(0, Math.min(aComponent.value, 255));
  };

rComponent.onchange = gComponent.onchange =
  bComponent.onchange = aComponent.onchange =
  function() {
    transColor = 'rgba(' +
      rComponent.value + ',' +
      gComponent.value + ',' +
      bComponent.value + ',' +
      aComponent.value / 255 + ',' +
      ')';
    // numberical equivelent of the rgba
    transCode =
      rComponent.value * 0x00000001 +
      gComponent.value * 0x00000100 +
      bComponent.value * 0x00010000 +
      aComponent.value * 0x01000000;
  };
<pre>rgba(<input type="number" value="255" max="255" min="0" step="1" id="r" maxlength="3" minlength="1" />,<input type="number" value="255" max="255" min="0" step="1" id="g" maxlength="3" minlength="1" />,<input type="number" value="255" max="255" min="0" step="1" id="b" maxlength="3" minlength="1" />,<input type="number" value="255" max="255" min="0" step="1" id="a" maxlength="3" minlength="1" />)</pre>

<input type="file" name="filefield" multiple="multiple" accept="image/*" id="fileInput" /><br />
<img id="imgTMP" />
<canvas id="theCanvas"></canvas>

<style>input[type=number]{width: 3em}#theCanvas {display: none}</style>