【问题标题】:how to see each pixel being changed on the canvas html5如何在画布 html5 上查看正在更改的每个像素
【发布时间】:2015-06-28 11:44:42
【问题描述】:

我在 javascript 中创建了一个过滤器,它可以反转图像的颜色,即创建一个负图像,现在当我在浏览器中运行它时,它需要一些时间来处理,然后返回最终的负图像。我怎样才能看到图像的每个像素都被反转,而不仅仅是最终的反转图像?我不想等待代码在整个像素阵列上实现然后查看其效果,而是希望看到每个像素都被代码更改直到最后一个像素。

var imgData = ctx.getImageData(0,0,x.width,x.height);
var d = imgData.data;
        for (var i=0; i< d.length; i+=4) {
        d[i] = 255 - d[i];
       d[i+1] = 255 - d[i+1];
       d[i+2] = 255 - d[i+2];

        }
ctx.putImageData(imgData,0,0);

新代码

invert(d,0);
function invert(d,i){
    if(i < d.length){
         d[i] = 255 - d[i];
               d[i+1] = 255 - d[i+1];
               d[i+2] = 255 - d[i+2];
               d[i+3] = d[i+3];
               //alert(i);
               var n=i/4;
            var h=parseInt(n/x.width);

            var w = n - h*x.width;

            ctx.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255+')';

            ctx.fillRect(w,h,1,1);

            //if(i>91000){alert(i);}
          setTimeout(invert(d,i+4),50);
    }
    else{return ;}
    }

【问题讨论】:

    标签: javascript html algorithm image-processing canvas


    【解决方案1】:

    您可以 context.fillRect 计算画布上每个新更改的像素。

    类似这样的事情(警告:未经测试的代码,可能需要 tweeks!):

    var n=i/4;
    var y=parseInt(n/canvas.width);
    var x=n-y*canvas.width;
    context.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255')';
    context.fillRect(x,y,1,1);
    

    这里的演示代码首先进行反转,然后随着时间的推移显示效果:

            var canvas=document.getElementById("canvas");
            var ctx=canvas.getContext("2d");
            var cw=canvas.width;
            var ch=canvas.height;
    
    
    var nextTime=0;
    // a new line of converted image will be displayed
    // after this delay
    var delay=1000/60*2;
    var y=0;
    
    var imgData,d;
    
    var img=new Image();
    img.crossOrigin='anonymous';
    img.onload=start;
    img.src="https://dl.dropboxusercontent.com/u/139992952/multple/sun.png";
    function start(){
    
        cw=canvas.width=img.width;
        ch=canvas.height=img.height;
        
        ctx.drawImage(img,0,0);
        
        imgData=ctx.getImageData(0,0,cw,ch);
        d=imgData.data;
        
        for (var i=0; i< d.length; i+=4) {
            d[i] = 255 - d[i];
            d[i+1] = 255 - d[i+1];
            d[i+2] = 255 - d[i+2];
        }
    
        requestAnimationFrame(animate);
    }
    
    function animate(time){
    
        if(time<nextTime){requestAnimationFrame(animate); return;}
        nextTime=time+delay;
       
        for(var x=0;x<cw;x++){
            var i=(y*cw+x)*4;
            ctx.fillStyle='rgba('+d[i]+','+d[i+1]+','+d[i+2]+','+d[i+3]/255+')';
            ctx.fillRect(x,y,1,1);
        }
    
        if(++y<ch){
            requestAnimationFrame(animate);
        }else{
            ctx.putImageData(imgData,0,0);
        }
    
    }
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
    &lt;canvas id="canvas" width=300 height=300&gt;&lt;/canvas&gt;

    【讨论】:

    • 它应该是 var x = n - y*canvas.width,在更正逻辑是正确的之后,图片仍然在快速反转,所以我决定在每个像素更新后添加一个 setTimeout,并且更改了代码,但这不起作用。我正在再次编辑代码。
    • 感谢您发现我的错误——今天早上我很着急!我已经发布了一个演示,它首先完全反转了imageData.data,然后使用这些转换后的像素对查看者进行减慢显示。祝你的项目好运!
    • 谢谢,帮了大忙:)
    【解决方案2】:

    您需要使用异步“循环”,以便更新一些像素,让显示器更新结果,然后继续。

    JavaScript 是单线程的,因此在当前循环结束之前不会更新任何内容,因为线程已被该线程占用。

    看着油漆变干

    这是您可以使用的一种方法。您以要反转的像素数定义“批量”大小(如果您想查看每个像素,则 1 有效,但这可能需要很长时间:

    ie. 16.67ms x total number of pixels.
    

    因此,如果您想使用 640x400 的图像显示每个像素更新(如下面的演示所示),则需要:

    640 x 400 x 16.67ms = 4,267,520 ms, or more than an hour
    

    注意事项(显示器更新速度不能超过每 16.67 毫秒 = 60 fps)。下面我们使用每批 128 像素。

    现场示例

    请注意,批处理值必须与图像的宽度相匹配。前任如果您的图像是 640 像素宽,您可以使用 1、5、10、20、.. 64、128 等。

    如果您想要除 1 或小数值以外的宽度,您必须进行简单的计算以限制最后一批的一行,因为 getImageData() 需要参数来定义图像内的区域。或者,就一行一行地做...

    您还可以对垂直图块使用“batch”值(在这种情况下,框可能是一个更好的术语)。

    var img = new Image();
    img.crossOrigin = "";
    img.onload = painter;
    img.src = "http://i.imgur.com/Hl3I0cx.jpg";
    
    function painter() {
    
      // setup canvas and image
      var canvas = document.querySelector("canvas"),
          ctx = canvas.getContext("2d");
      
      // set canvas size = image size
      canvas.width = this.naturalWidth; canvas.height = this.naturalHeight;
    
      // draw in image
      ctx.drawImage(this, 0, 0);
      
      // prepare loop
      var batch = 128,
          x = 0, y = 0,
          w = canvas.width - batch,
          h = canvas.height;
      
      (function asyncUpdate() {
      
        // do one batch only
        var idata = ctx.getImageData(x, y, batch, 1),  // get a bacth of pixels
            data = idata.data,
            i = 0, len = data.length;
        
        while(i < len) {                               // invert the batch
          data[i] = 255 - data[i++];
          data[i] = 255 - data[i++];
          data[i] = 255 - data[i++];
          i++
        }
        
        ctx.putImageData(idata, x, y);                 // update bitmap
        
        x += batch;
        if (x > w) {                                   // check x pos
          x = 0;
          y++;
        }
        
        if (y < h) {                                  // new batch?
          requestAnimationFrame(asyncUpdate);         // let display update before next
        }
        else {
          // use a callback here...
          console.log("Done");
        }
      })();
    }
    &lt;canvas&gt;&lt;/canvas&gt;

    【讨论】:

    • 解决方案很好但是不使用动画可以吗?我的意思是 fillRect(x,y,1,1) 方法将每个像素一个一个地放在它的位置,如果我们添加一个延迟,那么我们实际上可以看到每个像素被放置?我尝试这样做,并添加了一个新代码,但似乎有一些问题。
    猜你喜欢
    • 2014-01-16
    • 2012-12-19
    • 1970-01-01
    • 2019-10-05
    • 2018-03-20
    • 2014-08-15
    • 2018-06-30
    • 2016-10-22
    • 2019-12-11
    相关资源
    最近更新 更多