floodfill algorithm - leaving out the edge

ぐ巨炮叔叔 提交于 2020-01-24 11:29:47

问题


so far i have implemented a floodfill algorithm. I wanted to tweak it, so that it would leave out the edge. To demonstrate this i uploaded an image:

Image 1 is the image that i want to modify. Image 3 is what my algorithm is supposed to do. Image 2 is my current result.

So in this case the targetColor is white and the replacementColor is green;

Here is pseudocode of what i have done so far.

doFloodFill(x,y,targetColor,replacementcolor) {

    if (x and y not in bounds of image) {
       return
    }

    if (color(x,y) IS NOT targetColor) {
       return
    }

    if (color(x+1, y) IS NOT targetColorOfFloodFill AND color(x+1, y) IS NOT replacementColor OR
        color(x-1, y) IS NOT targetColorOfFloodFill AND color(x-1, y) IS NOT replacementColor OR
        color(x, y+1) IS NOT targetColorOfFloodFill AND color(x, y+1) IS NOT replacementColor OR
        color(y, y-1) IS NOT targetColorOfFloodFill AND color(x, y-1) IS NOT replacementColor) {
        return;
    }

    image.setColor(x, y, replacementColor)

    doFloodFill(x+3,y,targetcolor,replacementcolor)
    doFloodFill(x-3,y,targetcolor,replacementcolor)
    doFloodFill(x,y+3,targetcolor,replacementcolor)
    doFloodFill(x,y-3,targetcolor,replacementcolor)
}

This tweak is implemented into my floodfill. Leaving it out results in a working floodFill algorithm without any problems. The actual question would be: how can i differentiate an edge pixel from a pixel with different color within the area?

P.S: we can assume that x, y are starting from within the area


回答1:


I would go as well for the floodfill complement.

Still, an other idea:

  • Floodfill like usual
  • Keep track of one pixel of the border: e.g pixel at the utmost top
  • Then follow the border like a mouse looking for escape and make it white again.

const canvas = document.querySelector('canvas');

const M = `
00000000000000000000
00000110011111110000
00001111111110000000
00011111111111100000
00111111111101110010
01111111101111110010
01111111111111110110
01111111111111111110
01111111111111111100
01111111111111111100
01111111111111111100
01111111111111111000
00111111111111111000
00111111111111110000
00011111111111100000
00000000000000000000
`.trim().split('\n').map(x=>x.trim().split('').map(x=>parseInt(x)))

const mat = ({ x, y }, v) => {
  if (v) {
    M[y][x] = v
  }
  return M[y][x]
}
const left = ({x,y}) => ({ x: y, y: -x })
const right = ({x,y}) => ({ x: -y, y: x })
const back = ({x, y}) => ({ x: -x, y: -y})
const front = (pos, { x, y }) => ({ x: pos.x + x, y: pos.y + y })
const splinter = { 
  pos: {
    x: 5, 
    y: 1
  },
  orig: {
    x: 5,
    y: 1
  },
  dir: { x: 1, y: 0 },
  atStart() {
    return this.pos.x === this.orig.x && this.pos.y === this.orig.y
  },
  move () {
    if (this.atStart() && mat(this.pos) === 2) { return false }
    // wall on left
    if (mat(front(this.pos, left(this.dir))) === 0) {
      // wall on front
      if (mat(front(this.pos, this.dir)) === 0) {
        // wall on right
        if (mat(front(this.pos, right(this.dir))) === 0) {
          this.dir = back(this.dir)
        } else {
          this.dir = right(this.dir)
        }
      }
      this.poop()
    } else {
      this.dir = left(this.dir)
    }
    this.moveForward()
    return true
  },
  moveForward () {
    this.pos.x += this.dir.x
    this.pos.y += this.dir.y
  },
  poop () {
    mat({ x: this.pos.x, y: this.pos.y }, 2)
  },
  sprite () {
    if (this.atStart()) { return 'X' }
    if (this.dir.x === -1 && this.dir.y === 0) { return '←' }
    if (this.dir.x === 1 && this.dir.y === 0) { return '→'}
    if (this.dir.x === 0 && this.dir.y === 1) { return '↓' }
    if (this.dir.x === 0 && this.dir.y === -1) { return '↑' }
  }
}

function redraw () {
  const ctx = canvas.getContext('2d')
  const dw = canvas.width / M[0].length
  const dh = canvas.height / M.length
  const fill = ({ x, y }, color) => {
    ctx.fillStyle = color
    ctx.fillRect(x * dw, y * dh, dw, dh)
  }
  const colors = {
    1: 'green',
    0: 'black',
    2: 'white'
  }
  M.forEach((row, i) => {
    row.forEach((el, j) => {
      fill({ x: j, y: i }, colors[el])
    })
  })
  const char = splinter.sprite()
  ctx.strokeText(char, (splinter.pos.x + 0.1) * dw, (splinter.pos.y + 0.8) * dh)
}
redraw()
document.querySelector('button').onclick = _ => {
  splinter.move(); redraw()
}
document.querySelector('button.allmoves').onclick = _ => {
  while(splinter.move()){}
  redraw()
}
canvas{background:#eeeeee;}
<canvas width="160" height="160"></canvas>
<button>move, rat</button>
<button class="allmoves">move for your life, rat</button>



回答2:


First flood fill the black outer area in image 1 for example by starting with the pixel in the upper left corner of the image. Either use a color you haven't used yet (purple for example) or construct a mask during the filing.

Then fill the white area green in the central object like you did but without any tweaks of the algorithm. Just ordinary flood filling.

Finally, go through all the pixels in the second fill and turn those that border the first fill white again.

You have achieved your goal. If you did not use masks, make sure to fill the area resulting from the first fill black again.

No tweaking of the flood fill algorithm was needed.



来源:https://stackoverflow.com/questions/59240491/floodfill-algorithm-leaving-out-the-edge

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!