【问题标题】:Paint Fill multidimensional array油漆填充多维数组
【发布时间】:2015-05-03 17:21:35
【问题描述】:

我的目标是一个“绘画填充”功能,人们可能会在许多图像编辑程序中看到它。也就是说,给定一个屏幕(由一个二维颜色数组表示)、一个点和一种新颜色,填充周围区域,直到颜色从原始颜色发生变化。

我已经为二维数组实现了它,这里是代码:

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...我该如何解决这个问题?

【问题讨论】:

  • 您需要做的第一件事是避免递归函数!请改用队列。

标签: java arrays algorithm multidimensional-array


【解决方案1】:

感谢@Spektre 关于点结构的建议,我设法编写了一个简单的 N 维填充。

我没有使用图像,而是使用 char 矩阵来简化编码。将其更改为 int 作为颜色值以及其他矩阵数据类型的一些更改,将为您执行 100D :)

在这个简单的程序中,我尝试用“B”填充所有“A”,并填充所有连接的字符值,类似于蚂蚁巢。您可以使用其他层跟踪 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;
        }
    }
}

【讨论】:

    【解决方案2】:
    1. 避免递归函数!改为使用队列来填充图像 ith
    2. 您要在哪个图像上开始填充?
    3. 检查第 i 个图像上的图像颜色并将该点添加到您的列表中。
    4. 稍后检查您是否可以从存储的点向上或向下移动到 (i+1)th 或 (i-1)th 图像并重复此过程从那里开始。

    这是一个原始的想法,但您可能只需要这个。

    另外,您需要为每个级别设置一个数组,以检查您是否为该图像填充了该像素。所以你会从无限循环中逃脱:)

    检查这个使用队列填充: Flood Fill Optimization: Attempting to Using a Queue

    【讨论】:

    • (+1) 用于优化建议(在 N-D 中需要,因为由于内存不足而无法递归),但这并不能回答如何进行任意维度洪水填充的 OP 问题
    【解决方案3】:

    Salivan 的建议是正确的,但他没有理解您所问的真正问题。 对于任意维度,您需要将点结构从 pnt.x,pnt.y,pnt.z 之类的符号更改为 pnt[0],pnt[1],pnt[2],然后有几种方法可以处理这个问题:

    1. 用零填充的固定限制大小

      所以像10D 一样处理所有东西(如果10D 是使用的最大维度)并用零填充未使用的轴。这是缓慢而丑陋的,对内存的要求很高,并且限制了最大维度。

    2. 使用嵌套 for 循环(用于初始化等)

      看这里:rasterize and fill a hypersphere

      许多多维操作需要嵌套循环,这个循环具有任意深度。您可以将其视为多位数字的增量函数,其中每个数字代表您空间中的轴。

    3. 在 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
       }
      

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-11
      • 1970-01-01
      • 2013-02-12
      • 1970-01-01
      • 2013-02-20
      • 1970-01-01
      相关资源
      最近更新 更多