You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java通过ADB免存截图获取指定坐标RGB颜色及红色识别异常问题

Java结合ADB获取指定坐标RGB时红色系颜色识别异常的解决方法

我通过Java结合ADB实现游戏自动化,编写了无需保存截图到本地即可获取指定(x,y)坐标RGB值的方法(保存截图耗时约3秒,效率太低)。但在YouTube的RGB测试视频中测试时,红色及红色系颜色出现识别异常,其他颜色显示正常,无法输出目标坐标的准确结果。现有代码如下:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class RGBColorAtCoordinates {

    public static void main(String[] args) {
        int x = 1200; // Replace with your desired x-coordinate
        int y = 500; // Replace with your desired y-coordinate

        try {
            // ADB command to get pixel color at (x, y)
            String adbCommand = String.format("adb shell screencap -p | sed 's/\\r$//' | head -c %d | tail -c %d", (x + 1) * 4, 4);
            
            // Execute adb command
            Process process = Runtime.getRuntime().exec(adbCommand);
            
            // Read output
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            StringBuilder output = new StringBuilder();
            while ((line = reader.readLine()) != null) {
                output.append(line);
            }
            reader.close();

            // Parse RGB values from output (example, you may need to adjust based on actual output format)
            String colorOutput = output.toString();
            if (colorOutput.length() == 4) {
                int red = (colorOutput.charAt(0) & 0xff);
                int green = (colorOutput.charAt(1) & 0xff);
                int blue = (colorOutput.charAt(2) & 0xff);
                int alpha = (colorOutput.charAt(3) & 0xff);
                System.out.printf("RGB color at (%d, %d): (%d, %d, %d)\n", x, y, red, green, blue);
            } else {
                System.err.println("Failed to get color information.");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

问题原因分析

  1. PNG像素字节顺序错误:screencap -p输出的是PNG格式图片,PNG的像素存储顺序为BGRA(蓝-绿-红-透明度),而代码中错误地按RGBA顺序解析,红色系颜色的红分量高、蓝分量低,顺序颠倒后会直接导致识别结果错误,其他颜色因RGB数值接近可能表现正常。
  2. 二进制数据用文本流读取:BufferedReader是用于读取文本的工具,处理PNG二进制字节时会触发编码转换,导致字节值失真,红色相关字节更容易出现解析错误。
  3. 像素位置计算错误:代码中用(x + 1) * 4计算截取长度,完全忽略了PNG文件的头部和块结构,实际截取的是PNG文件头内容,根本不是目标坐标的像素数据。

修正方案

方案1:Java直接解析PNG输入流(推荐,不依赖设备额外工具)

直接通过InputStream读取ADB输出的二进制PNG数据,解析PNG结构找到像素数据区域,再定位目标坐标的字节:

import java.io.IOException;
import java.io.InputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

public class RGBColorAtCoordinates {

    public static void main(String[] args) {
        int targetX = 1200;
        int targetY = 500;

        try {
            Process process = Runtime.getRuntime().exec("adb shell screencap -p");
            InputStream inputStream = process.getInputStream();

            // 跳过PNG文件标识头(固定8字节:89 50 4E 47 0D 0A 1A 0A)
            inputStream.skip(8);

            // 读取IHDR块,获取屏幕宽高
            byte[] ihdrLenBytes = new byte[4];
            inputStream.read(ihdrLenBytes);
            int ihdrSize = bytesToInt(ihdrLenBytes, true);
            
            byte[] ihdrData = new byte[ihdrSize];
            inputStream.read(ihdrData);
            int screenWidth = bytesToInt(new byte[]{ihdrData[0], ihdrData[1], ihdrData[2], ihdrData[3]}, true);
            int screenHeight = bytesToInt(new byte[]{ihdrData[4], ihdrData[5], ihdrData[6], ihdrData[7]}, true);

            // 跳过IHDR块的CRC校验(4字节)
            inputStream.skip(4);

            // 定位到IDAT块(存储像素压缩数据的块)
            byte[] chunkLen = new byte[4];
            byte[] chunkType = new byte[4];
            while (true) {
                inputStream.read(chunkLen);
                inputStream.read(chunkType);
                int chunkSize = bytesToInt(chunkLen, true);
                
                if (new String(chunkType).equals("IDAT")) {
                    break;
                }
                // 跳过非IDAT块的数据和CRC校验
                inputStream.skip(chunkSize + 4);
            }

            // 解压IDAT块的像素数据
            Inflater inflater = new Inflater(true);
            InflaterInputStream inflaterStream = new InflaterInputStream(inputStream, inflater);

            // 计算目标像素的偏移量:每行screenWidth*4字节,跳过targetY行,再跳过targetX个像素的字节
            long skipOffset = (long) targetY * screenWidth * 4 + (long) targetX * 4;
            inflaterStream.skip(skipOffset);

            // 读取目标像素的BGRA字节
            byte[] pixelBytes = new byte[4];
            inflaterStream.read(pixelBytes);

            // 转换为RGB格式(BGRA -> RGB)
            int red = pixelBytes[2] & 0xFF;
            int green = pixelBytes[1] & 0xFF;
            int blue = pixelBytes[0] & 0xFF;
            // int alpha = pixelBytes[3] & 0xFF; // 如需透明度可保留

            System.out.printf("RGB color at (%d, %d): (%d, %d, %d)\n", targetX, targetY, red, green, blue);

            // 关闭资源
            inflaterStream.close();
            inputStream.close();
            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 字节数组转int(bigEndian为true表示大端字节序,PNG采用大端)
    private static int bytesToInt(byte[] bytes, boolean bigEndian) {
        int result = 0;
        if (bigEndian) {
            for (byte b : bytes) {
                result = (result << 8) | (b & 0xFF);
            }
        } else {
            for (int i = bytes.length - 1; i >= 0; i--) {
                result = (result << 8) | (bytes[i] & 0xFF);
            }
        }
        return result;
    }
}

方案2:依赖设备工具简化命令(需设备预装busybox/imagemagick)

如果你的设备已经安装了busybox和imagemagick,可以用以下ADB命令直接获取RGB值,Java只需解析输出即可:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class RGBColorAtCoordinates {

    public static void main(String[] args) {
        int x = 1200;
        int y = 500;

        try {
            String adbCommand = String.format("adb shell \"screencap -p | convert - -format '%%[pixel:p(%d,%d)]' info:\"", x, y);
            Process process = Runtime.getRuntime().exec(adbCommand);

            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String colorStr = reader.readLine();
            reader.close();

            // 解析输出格式(如rgb(255,0,0))
            if (colorStr != null && colorStr.startsWith("rgb(")) {
                String[] rgbParts = colorStr.substring(4, colorStr.length()-1).split(",");
                int red = Integer.parseInt(rgbParts[0].trim());
                int green = Integer.parseInt(rgbParts[1].trim());
                int blue = Integer.parseInt(rgbParts[2].trim());
                System.out.printf("RGB color at (%d, %d): (%d, %d, %d)\n", x, y, red, green, blue);
            } else {
                System.err.println("Failed to parse color output.");
            }

            process.waitFor();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

关键修正点

  • 修正PNG像素字节顺序为BGRA,转换为RGB时调换红、蓝分量
  • 用二进制流InputStream读取PNG数据,避免文本编码导致的字节失真
  • 正确解析PNG文件结构,跳过文件头和非像素块,定位到真实像素数据区域

内容的提问来源于stack exchange,提问作者AomineDaici

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 08:19:52