将单色图像转换为最小数量的2d形状

时间:2017-08-24 04:50:38

标签: java arrays

基本上,我需要做的是采用二维数组的位标记并生成一个2d矩形列表,以填充整个区域,以完美填充空间所需的最小总形状数。我这样做是为了将地图的2d自上而下的单色转换为2d矩形形状,这些形状完美地表示将用于在3d世界中生成平台的传入图像。我需要最小化所使用的形状总数,因为每个形状将代表一个单独的对象,并且每个像素充满1个单位大小的正方形对于该引擎来说效率非常低。

到目前为止,我已经阅读了图像,对其进行了处理,并填充了一个二维的布尔数组,告诉我像素是应该填充还是未填充,但我不确定最有效的继续方法。

以下是我目前所掌握的,作为参考,如果你不跟随:

public static void main(String[] args) {
    File file = new File(args[0]);
    BufferedImage bi = null;
    try {
        bi = ImageIO.read(file);
    } catch (IOException ex) {
        Logger.global.log(Level.SEVERE, null, ex);
    }
    if (bi != null) {
        int[] rgb = bi.getRGB(0, 0, bi.getWidth(), bi.getHeight(), new int[bi.getWidth() * bi.getHeight()], 0, bi.getWidth());
        Origin origin = new Origin(bi.getWidth() / 2, bi.getHeight() / 2);
        boolean[][] flags = new boolean[bi.getWidth()][bi.getHeight()];
        for (int y = 0; y < bi.getHeight(); y++) {
            for (int x = 0; x < bi.getWidth(); x++) {
                int index = y * bi.getWidth() + x;
                int color = rgb[index];
                int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
                if (type == 2) {
                    origin = new Origin(x, y);
                }
                flags[x][y] = type != 1;
            }
        }
        List<Rectangle> list = new ArrayList();
        //Fill list with rectangles
    }
}

怀特代表没有土地。黑色或红色代表土地。检查红色像素标记了地图的原点位置,这只是为了方便起见,如果找到,矩形将被原点位置偏移。

编辑:处理脚本不需要很快,生成的矩形列表将被转储,这将是以后导入和使用的内容,因此图像的处理不需要特别优化,它并没有什么不同。

我也意识到期待一个完美的&#39;解决方案期望太多,因为这可能成为背包问题的一个问题。对于多维约束的变种,如果我期望完全最少数量的矩形,那么简单地生成最小数量的矩形的算法就足够了。

以下是完成的参考图片:

Reference

编辑2:在没有反馈意见的情况下,这似乎很容易回答,但我已经开始取得进展,但我确信我遗漏的东西会大大减少矩形的数量。以下是更新的进度:

static int mapWidth;
static int mapHeight;

public static void main(String[] args) {
    File file = new File(args[0]);
    BufferedImage bi = null;
    System.out.println("Reading image...");
    try {
        bi = ImageIO.read(file);
    } catch (IOException ex) {
        Logger.global.log(Level.SEVERE, null, ex);
    }
    if (bi != null) {
        System.out.println("Complete!");
        System.out.println("Interpreting image...");
        mapWidth = bi.getWidth();
        mapHeight = bi.getHeight();;
        int[] rgb = bi.getRGB(0, 0, mapWidth, mapHeight, new int[mapWidth * mapHeight], 0, mapWidth);
        Origin origin = new Origin(mapWidth / 2, mapHeight / 2);
        boolean[][] flags = new boolean[mapWidth][mapHeight];
        for (int y = 0; y < mapHeight; y++) {
            for (int x = 0; x < mapWidth; x++) {
                int index = y * mapWidth + x;
                int color = rgb[index];
                int type = color == Color.WHITE.getRGB() ? 1 : (color == Color.RED.getRGB() ? 2 : 0);
                if (type == 2) {
                    origin = new Origin(x, y);
                }
                flags[x][y] = type != 1;
            }
        }
        System.out.println("Complete!");
        System.out.println("Processing...");
        //Get Rectangles to fill space...
        List<Rectangle> rectangles = getRectangles(flags, origin);
        System.out.println("Complete!");

        float rectangleCount = rectangles.size();
        float totalCount = mapHeight * mapWidth;

        System.out.println("Total units: " + (int)totalCount);
        System.out.println("Total rectangles: " + (int)rectangleCount);
        System.out.println("Rectangle reduction factor: " + ((1 - rectangleCount / totalCount) * 100.0) + "%");

        System.out.println("Dumping data...");
        try {
            file = new File(file.getParentFile(), file.getName() + "_Rectangle_Data.txt");
            if(file.exists()){
                file.delete();
            }
            file.createNewFile();
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
            for(Rectangle rect: rectangles){
                bw.write(rect.x + "," + rect.y + "," + rect.width + ","+ rect.height + "\n");
            }
            bw.flush();
            bw.close();
        } catch (Exception ex) {
            Logger.global.log(Level.SEVERE, null, ex);
        }
        System.out.println("Complete!");
    }else{
        System.out.println("Error!");
    }
}

public static void clearRange(boolean[][] flags, int xOff, int yOff, int width, int height) {
    for (int y = yOff; y < yOff + height; y++) {
        for (int x = xOff; x < xOff + width; x++) {
            flags[x][y] = false;
        }
    }
}

public static boolean checkIfFilled(boolean[][] flags, int xOff, int yOff, int width, int height) {
    for (int y = yOff; y < yOff + height; y++) {
        for (int x = xOff; x < xOff + width; x++) {
            if (!flags[x][y]) {
                return false;
            }
        }
    }
    return true;
}

public static List<Rectangle> getRectangles(boolean[][] flags, Origin origin) {
    List<Rectangle> rectangles = new ArrayList();

    for (int y = 0; y < mapHeight; y++) {
        for (int x = 0; x < mapWidth; x++) {
            if (flags[x][y]) {
                int maxWidth = 1;
                int maxHeight = 1;
                Loop:
                //The search size limited to 400x400 so it will complete some time this century.
                for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
                    for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
                        if (w * h > maxWidth * maxHeight) {
                            if (checkIfFilled(flags, x, y, w, h)) {
                                maxWidth = w;
                                maxHeight = h;
                                break Loop;
                            }
                        }
                    }
                }
                //Search also in the opposite direction
                Loop:
                for (int h = Math.min(400, mapHeight - y); h > 1; h--) {
                    for (int w = Math.min(400, mapWidth - x); w > 1; w--) {
                        if (w * h > maxWidth * maxHeight) {
                            if (checkIfFilled(flags, x, y, w, h)) {
                                maxWidth = w;
                                maxHeight = h;
                                break Loop;
                            }
                        }
                    }
                }
                rectangles.add(new Rectangle(x - origin.x, y - origin.y, maxWidth, maxHeight));
                clearRange(flags, x, y, maxWidth, maxHeight);
            }
        }
    }
    return rectangles;
}

我当前代码搜索较大的矩形限制为400x400以加速测试,输出17,979个矩形,如果我将每个像素视为1x1平方(19,095,720像素),则矩形总减少99.9058% 。到目前为止一切都很好。

0 个答案:

没有答案