我的目标是"油漆填充"人们可能会在许多图像编辑程序中看到的功能。也就是说,给定一个屏幕(由二维颜色数组表示),一个点和一个新颜色,填充周围区域,直到颜色从原始颜色变化。
我已经为2D数组实现了它,这里是代码:
public static void paint (int [][] screen,int OldColor,int NewColor,int y,int x)
{
if(y>screen.length-1||y<0||x>screen[0].length||x<0||screen[y][x]!=OldColor)
return;
screen[y][x]=NewColor;
paint(screen,OldColor,NewColor,y-1,x);
paint(screen, OldColor, NewColor, y+1, x);
paint(screen, OldColor, NewColor, y, x-1);
paint(screen, OldColor, NewColor, y, x+1);
}
但我想为3D等多维数组实现它,可以通过添加:
来解决paint(screen, OldColor, NewColor, y, x,z-1);
paint(screen, OldColor, NewColor, y, x,z+1);
但是想象一下阵列是100 D ......我怎么能解决这个问题?
答案 0 :(得分:2)
感谢@ Spektre关于积分结构的建议,我设法编写了一个简单的N维洪水填充。
我使用char矩阵来简化编码,而不是图像。将其更改为int作为颜色值以及其他矩阵的数据类型中的一些更改,将为您执行100D:)
在这个简单的程序中,我尝试用“B”填充所有“A”,并填充所有连接的char值,类似于蚂蚁巢。您可以使用其他图层跟踪A之间的连接以查看填充路径。
在第二张图片中(Im1,添加有意添加了B,然后在其上方添加了一个无法从填充点访问的A),它也可以正常工作。
package test;
import java.awt.Point;
import java.util.LinkedList;
import java.util.Queue;
/**
*
* @author Pasban
*/
public class NDFloodFill {
public int N1 = 8; // width
public int N2 = 6; // height
public int N = 3; // number of layers
public ImageData[] images = new ImageData[N];
public static void main(String[] args) {
NDFloodFill ndf = new NDFloodFill();
//print original data
//ndf.print();
ndf.fill(0, 0, 0, 'A', 'B');
ndf.print();
}
public NDFloodFill() {
String im0 = ""
+ "AA...A..\n"
+ ".....A..\n"
+ "....AA..\n"
+ "........\n"
+ "........\n"
+ "...AA.AA";
String im1 = ""
+ ".A..A...\n"
+ "....B...\n"
+ "..AAA...\n"
+ "........\n"
+ "...AA.A.\n"
+ "..AA..A.";
String im2 = ""
+ ".A......\n"
+ ".AA.....\n"
+ "..A.....\n"
+ "..A.....\n"
+ "..A.AAA.\n"
+ "..A.....";
images[0] = new ImageData(im0, 0);
images[1] = new ImageData(im1, 1);
images[2] = new ImageData(im2, 2);
}
private void print() {
for (int i = 0; i < N; i++) {
System.out.println(images[i].getImage());
}
}
private void fill(int x, int y, int index, char original, char fill) {
Queue<PixFill> broadCast = new LinkedList<>();
broadCast.add(new PixFill(new Point(x, y), index));
for (int i = 0; i < N; i++) {
images[i].reset();
}
while (!broadCast.isEmpty()) {
PixFill pf = broadCast.remove();
Queue<PixFill> newPoints = images[pf.index].fillArea(pf.xy, original, fill);
if (newPoints != null) {
broadCast.addAll(newPoints);
}
}
}
public class PixFill {
Point xy;
int index;
public PixFill(Point xy, int index) {
this.xy = xy;
this.index = index;
}
@Override
public String toString() {
return this.xy.x + " : " + this.xy.y + " / " + this.index;
}
}
public class ImageData {
char[][] pix = new char[N1][N2];
boolean[][] done = new boolean[N1][N2];
int index;
public ImageData(String image, int index) {
int k = 0;
this.index = index;
for (int y = 0; y < N2; y++) { // row
for (int x = 0; x < N1; x++) { // column
pix[x][y] = image.charAt(k++);
}
k++; // ignoring the \n char
}
}
public void reset() {
for (int y = 0; y < N2; y++) {
for (int x = 0; x < N1; x++) {
done[x][y] = false;
}
}
}
public String getImage() {
String ret = "";
for (int y = 0; y < N2; y++) { // row
String line = "";
for (int x = 0; x < N1; x++) { // column
line += pix[x][y];
}
ret += line + "\n";
}
return ret;
}
public Queue<PixFill> fillArea(Point p, char original, char fill) {
if (!(p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) || !(pix[p.x][p.y] == original)) {
return null;
}
// create queue for efficiency
Queue<Point> list = new LinkedList<>();
list.add(p);
// create broadcasting to spread filled points to othwer layers
Queue<PixFill> broadCast = new LinkedList<>();
while (!list.isEmpty()) {
p = list.remove();
if ((p.x >= 0 && p.y >= 0 && p.x < N1 && p.y < N2) && (pix[p.x][p.y] == original) && (!done[p.x][p.y])) {
//fill
pix[p.x][p.y] = fill;
done[p.x][p.y] = true;
//look for neighbors
list.add(new Point(p.x - 1, p.y));
list.add(new Point(p.x + 1, p.y));
list.add(new Point(p.x, p.y - 1));
list.add(new Point(p.x, p.y + 1));
// there will not be a duplicate pixFill as we always add the filled points that are not filled yet,
// so duplicate fill will never happen, so do pixFill :)
// add one for upper layer
if (index < N - 1) {
broadCast.add(new PixFill(p, index + 1));
}
// add one for lower layer
if (index > 0) {
broadCast.add(new PixFill(p, index - 1));
}
//layers out of range <0, N> can be filtered
}
}
return broadCast;
}
}
}
答案 1 :(得分:1)
这是一个原创的想法,但你可能需要的就是这个。
另外,您需要为每个级别设置一个数组,以检查是否已为该图像填充了该像素。所以你将摆脱无限循环:)
使用队列检查此洪水填充: Flood Fill Optimization: Attempting to Using a Queue
答案 2 :(得分:0)
Salivan对他的建议是正确的,但他没有理解你所问的真正问题。 对于任意维度,您需要将点结构从pnt.x,pnt.y,pnt.z
等符号更改为pnt[0],pnt[1],pnt[2]
,然后几乎没有办法处理此问题:
用零填充的固定限制大小
使用嵌套for循环(用于初始化等)
在N-D中使用普通for
循环生成邻居
// point variables
int p[N],q[N];
// here you have actual point p and want to find its neighbors
for (int i=0;i<N;i++)
{
for (int j=0;i<N;i++) q[j]=p[j]; // copy point
q[i]--;
// add q to flood fill
q[i]+=2;
// add q to flood fill
}