P5洪水填充尝试

时间:2018-03-12 01:26:15

标签: javascript stack p5.js flood-fill

我一直在尝试使用初始mouseX和mouseY值创建泛洪填充功能。在使用4路递归方法进行实验和失败之后,我发现了一篇文章,建议使用数组存储4路值并使用.push()函数对它们进行排队。当数组具有值时,函数将使用.pop()函数将新值设置为x和y,然后测试并更改为所需的颜色。

尝试这种方法进行洪水填充后,我仍然在努力获得任何关于速度的性能。我试图包含一个额外的检查历史记录功能来存储被访问像素的历史记录,并防止任何重复的x,y坐标被推回到队列中。但是(当使用控制台记录"匹配找到")时,该功能似乎找不到任何重复。

我非常感谢有关如何改进我目前编写的代码的任何指导。或者也许是对不同方法的建议,这种方法并不复杂。我很少使用Stack Overflow,也非常感谢如何格式化问题和代码以供将来参考。谢谢您的任何时间。

使用我已经包含的sketch.js,您可以看到填充函数在非常小的对象(第一个初始方块)上是可接受的,但随着大小的增加,性能会停止。

的index.html

<!DOCTYPE html>
<html>
  <head>
    <script src="p5.min.js"></script>
    <script src="sketch.js"></script>
    <style> body {padding: 0; margin: 0;} </style>
  </head>
  <body>
  </body>
</html>

sketch.js

var colorNew = [0,255,0,255];
function setup()
{
    createCanvas(500,500);
    squares();
}

function squares(){

    background(255); 
    noFill();
    stroke(0);
    rect(125,125,10,10);
    rect(125,125,20,20);
    rect(125,125,30,30);
    rect(125,125,40,40);
    rect(125,125,50,50);
    updatePixels();
}

function getPixData(xPos,yPos){    
    colorOld = get(xPos,yPos);
};

function checkValue(xPos,yPos,oldColor){    
    var currentPix
    currentPix = get(xPos,yPos);
    if(currentPix[0] == oldColor[0] && currentPix[1] == oldColor[1] && currentPix[2] == oldColor[2] && currentPix[3] == oldColor[3]){        
        return true;
    }    
};

function checkHistory(x1,y1,historyArray){    
    for (var i = 0 ; i < historyArray.length; i+=2){        
        if(x1 == historyArray[i] && y1 == historyArray[i+1]){            
            console.log("match found");           
            return false;                    
           }else {               
           console.log(historyArray.length)
           return true;             
            }
    } 
};


function floodFill (xPos,yPos){ 
    getPixData(mouseX,mouseY);    
    var stack = [];
    var historyList = [];
    stack.push(xPos,yPos);
    historyList.push(xPos,yPos);    
    console.log(stack);

    while(stack.length> 0){
        var yPos1 = stack.pop();
        var xPos1 = stack.pop();   
        set(xPos1,yPos1,colorNew); 
        updatePixels();

        if(xPos1+1 <= width && xPos1+1 > 0 ){
            if(checkValue(xPos1+1,yPos1,colorOld) && checkHistory(xPos1+1,yPos1,historyList)){
                stack.push(xPos1+1,yPos1);
                historyList.push(xPos1+1,yPos1);
                console.log("right checked")            
            }
        }

        if(xPos1+1 <= width && xPos1+1 > 0 ){
            if(checkValue(xPos1-1,yPos1,colorOld) && checkHistory(xPos1-1,yPos1,historyList)){
                stack.push(xPos1-1,yPos1);
                historyList.push(xPos1-1,yPos1);
                console.log("left checked");            
            }
        }
        if(yPos1+1 <= height && yPos1+1 > 0 ){
            if(checkValue(xPos1,yPos1+1,colorOld) && checkHistory(xPos1,yPos1+1,historyList)){
                stack.push(xPos1,yPos1+1);
                historyList.push(xPos1,yPos1+1);
                console.log("above checked");         
            }
        }

        if(yPos1-1 <= height && yPos1-1 > 0 ){
            if(checkValue(xPos1,yPos1-1,colorOld) && checkHistory(xPos1,yPos1-1,historyList)){
                stack.push(xPos1,yPos1-1);
                historyList.push(xPos1,yPos1-1);
                console.log("below checked");
            }
        }
    }      
    console.log(historyList);     
}

function draw()
{

    if(mouseIsPressed){
        floodFill(mouseX,mouseY)
    }

} 

2 个答案:

答案 0 :(得分:0)

对于代码的行为方式,肯定会有些蠢事。几点意见:

history数组比它应该大得多。从理论上讲,你只需要添加一次像素,对吧?但是检查history数组的大小,注意它比像素数大得多。由于您将xy值都添加到数组中,对于10x10矩形,您应该在数组中包含200个元素。但你最终会得到更多。

我也避免将x和y组件添加到数组中。相反,让每个索引包含一个点对象。像这样:

stack.push({x:xPos,y:yPos});

这实际上并没有解决您的问题,但它会使调试更容易。你的代码肯定包含一个bug。我会尽力让你看得更晚,但希望能让你走上正轨。

答案 1 :(得分:0)

主要的性能瓶颈是get(x,y) is slow。它必须每次调用getImageData来查看画布的像素数据,大约需要4毫秒。

Performance Dev Tools showing that each checkValue(x,y,colorOld) call takes anywhere between 5 and 12 ms to complete due to get(x,y)

泛光填充算法必须检查每个像素一次,因此在20x50的方格中,您必须检查1000个像素。如果每个像素检查花费4毫秒来获取颜色数据,那就是4秒!

p5建议一些performance optimization techniques处理数据,其中一个不经常检查图像的像素。它建议提前缓存像素。幸运的是,p5已经为你做了这件事。

p5在pixels数组中创建一个包含图像所有颜色的数组。您可以使用get数组,而无需使用pixel查看像素数据。该文档提供了如何从点x,y设置/获取颜色数据的代码:

function getPixData(x,y){
  var color = [];

  for (var i = 0; i < d; i++) {
    for (var j = 0; j < d; j++) {
      // loop over
      idx = 4 * ((y * d + j) * width * d + (x * d + i));
      color[0] = pixels[idx];
      color[1] = pixels[idx+1];
      color[2] = pixels[idx+2];
      color[3] = pixels[idx+3];
    }
  }

  return color;
};

function setPixData(x, y, colorNew) {
  for (var i = 0; i < d; i++) {
    for (var j = 0; j < d; j++) {
      // loop over
      idx = 4 * ((y * d + j) * width * d + (x * d + i));
      pixels[idx] = colorNew[0];
      pixels[idx+1] = colorNew[1];
      pixels[idx+2] = colorNew[2];
      pixels[idx+3] = colorNew[3];
    }
  }
}

如果您使用这两种方法替换get(x,y)set(x,y)的引用,并使用setup方法调用loadPixels,您将看到极大的速度提升。并且不要忘记在方法结束时调用updatePixels

function floodFill (xPos,yPos){
    colorOld = getPixData(xPos, yPos);
    var stack = [];
    var historyList = [];
    stack.push(xPos,yPos);
    historyList.push(xPos,yPos);
    console.log(stack);

    while(stack.length> 0){
        var yPos1 = stack.pop();
        var xPos1 = stack.pop();
        setPixData(xPos1,yPos1,colorNew);

        if(xPos1+1 <= width && xPos1+1 > 0 ){
            if(checkValue(xPos1+1,yPos1,colorOld) && checkHistory(xPos1+1,yPos1,historyList)){
                stack.push(xPos1+1,yPos1);
                historyList.push(xPos1+1,yPos1);
                console.log("right checked")
            }
        }

        if(xPos1+1 <= width && xPos1+1 > 0 ){
            if(checkValue(xPos1-1,yPos1,colorOld) && checkHistory(xPos1-1,yPos1,historyList)){
                stack.push(xPos1-1,yPos1);
                historyList.push(xPos1-1,yPos1);
                console.log("left checked");
            }
        }
        if(yPos1+1 <= height && yPos1+1 > 0 ){
            if(checkValue(xPos1,yPos1+1,colorOld) && checkHistory(xPos1,yPos1+1,historyList)){
                stack.push(xPos1,yPos1+1);
                historyList.push(xPos1,yPos1+1);
                console.log("above checked");
            }
        }

        if(yPos1-1 <= height && yPos1-1 > 0 ){
            if(checkValue(xPos1,yPos1-1,colorOld) && checkHistory(xPos1,yPos1-1,historyList)){
                stack.push(xPos1,yPos1-1);
                historyList.push(xPos1,yPos1-1);
                console.log("below checked");
            }
        }
    }

    updatePixels();
    console.log(historyList);
}

另外,我建议更改draw函数以使用mouseClicked代替mousePressed,因为mousePressed会在按住鼠标的同时继续运行{{1}只有在用户放开鼠标后才会运行一次。