如何在Libgdx或Java中查找Pixmap内指定颜色的边界矩形
查找Pixmap指定颜色边界矩形的代码问题与修正
问题概述
你尝试基于Stack Exchange上的思路,将其转换为Java(LibGDX)代码来查找Pixmap中指定颜色的所有边界矩形,但现有代码存在多处逻辑错误,无法正确实现需求。
现有代码的核心问题
getBoundingRectangle逻辑完全失效:方法中color.equals(compare)的compare变量从未更新为当前扫描的像素颜色,始终是初始空值,导致无法正确收集同颜色点;同时arrayMax方法未定义,代码无法编译。- 区域归属判断错误:用扩大2像素的矩形来判断点是否属于已有区域,这种方式完全不符合连通区域的判断逻辑,会导致大量误判(将不连通的区域归为同一矩形)或漏判(将连通区域拆分为多个矩形)。
- 扫描逻辑不完整:仅从第一个点的下一行开始扫描到maxX,无法覆盖所有同颜色的连通区域,遗漏大量像素。
- 颜色比较精度问题:使用
Color.equals进行精确比较,而Pixmap的像素颜色可能因存储格式导致精度损失,应该直接比较像素的整数值更可靠。
修正后的实现方案
采用连通区域遍历+极值记录的方式,先标记已访问的像素,对每个未访问的目标颜色像素,用BFS遍历整个连通区域,同时记录区域的最小/最大X、Y坐标,最终生成边界矩形。
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Queue; public class Magik { // 存储最终的边界矩形列表 private final Array<Rectangle> bounds = new Array<>(); // 标记已处理的像素,避免重复遍历 private boolean[][] visited; // 目标颜色的打包整数值,用于快速比较 private int targetColorInt; private Pixmap pixmap; public Array<Rectangle> getBounds(Pixmap pixmap, Color targetColor) { this.pixmap = pixmap; this.targetColorInt = Color.rgba8888(targetColor); this.visited = new boolean[pixmap.getWidth()][pixmap.getHeight()]; bounds.clear(); // 遍历所有像素 for (int y = 0; y < pixmap.getHeight(); y++) { for (int x = 0; x < pixmap.getWidth(); x++) { // 如果当前像素是目标颜色且未被访问过 if (!visited[x][y] && pixmap.getPixel(x, y) == targetColorInt) { // 遍历整个连通区域,计算边界矩形 Rectangle rect = calculateBoundingRectForRegion(x, y); bounds.add(rect); } } } // 打印结果(可选) System.out.println("检测到的边界矩形:"); for (Rectangle rect : bounds) { System.out.println(rect); } return bounds; } /** * 从(x,y)开始,用BFS遍历所有连通的目标颜色像素,计算最小包围矩形 */ private Rectangle calculateBoundingRectForRegion(int startX, int startY) { Queue<int[]> queue = new Queue<>(); queue.add(new int[]{startX, startY}); visited[startX][startY] = true; // 初始化区域极值 int minX = startX; int maxX = startX; int minY = startY; int maxY = startY; // 四个方向:上、下、左、右(如果需要8连通可以增加斜向) int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; while (!queue.isEmpty()) { int[] pos = queue.removeFirst(); int x = pos[0]; int y = pos[1]; // 更新区域极值 minX = Math.min(minX, x); maxX = Math.max(maxX, x); minY = Math.min(minY, y); maxY = Math.max(maxY, y); // 遍历四个相邻像素 for (int[] dir : directions) { int newX = x + dir[0]; int newY = y + dir[1]; // 检查坐标是否在Pixmap范围内,且未被访问,且是目标颜色 if (isValidCoordinate(newX, newY) && !visited[newX][newY] && pixmap.getPixel(newX, newY) == targetColorInt) { visited[newX][newY] = true; queue.add(new int[]{newX, newY}); } } } // 生成边界矩形:x=minX, y=minY, width=maxX-minX+1, height=maxY-minY+1 return new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1); } /** * 检查坐标是否在Pixmap的有效范围内 */ private boolean isValidCoordinate(int x, int y) { return x >= 0 && x < pixmap.getWidth() && y >= 0 && y < pixmap.getHeight(); } }
关键优化说明
- 高效颜色比较:用
Color.rgba8888(targetColor)将目标颜色转换为和Pixmap.getPixel()一致的RGBA8888整数值,避免浮点精度问题,比较更快。 - 连通区域遍历:使用BFS(广度优先搜索)遍历所有连通的目标颜色像素,确保不会遗漏任何属于同一区域的像素。
- 极值记录:遍历区域时直接记录min/max X/Y,不需要收集所有点,节省内存且效率更高。
- 已访问标记:避免重复处理同一像素,确保每个区域只被计算一次。
内容的提问来源于stack exchange,提问作者WASD123
相关产品推荐
相关产品推荐

