使用Java获取图像的像素数据

15

我正在尝试从一个64 x 48位图像中获取像素rgb值。我得到了一些值,但远远不及我期望的3072(= 64 x 48)个值。我还得到了:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Coordinate out of bounds!
at sun.awt.image.ByteInterleavedRaster.getDataElements(ByteInterleavedRaster.java:301)
at java.awt.image.BufferedImage.getRGB(BufferedImage.java:871)
at imagetesting.Main.getPixelData(Main.java:45)
at imagetesting.Main.main(Main.java:27)

我找不到数组越界错误...

以下是代码:

package imagetesting;

import java.io.IOException;
import javax.imageio.ImageIO;
import java.io.File;
import java.awt.image.BufferedImage;



public class Main {

public static final String IMG = "matty.jpg";

public static void main(String[] args) {

    BufferedImage img;

    try {
        img = ImageIO.read(new File(IMG));

        int[][] pixelData = new int[img.getHeight() * img.getWidth()][3];
        int[] rgb;

        int counter = 0;
        for(int i = 0; i < img.getHeight(); i++){
            for(int j = 0; j < img.getWidth(); j++){
                rgb = getPixelData(img, i, j);

                for(int k = 0; k < rgb.length; k++){
                    pixelData[counter][k] = rgb[k];
                }

                counter++;
            }
        }


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

}

private static int[] getPixelData(BufferedImage img, int x, int y) {
int argb = img.getRGB(x, y);

int rgb[] = new int[] {
    (argb >> 16) & 0xff, //red
    (argb >>  8) & 0xff, //green
    (argb      ) & 0xff  //blue
};

System.out.println("rgb: " + rgb[0] + " " + rgb[1] + " " + rgb[2]);
return rgb;
}

}

2
为什么不在您的主函数中加入catch语句以获取异常并打印出发生问题的循环索引。 - James Black
6个回答

12
这是:
for(int i = 0; i < img.getHeight(); i++){
    for(int j = 0; j < img.getWidth(); j++){
        rgb = getPixelData(img, i, j);

与此不匹配:
private static int[] getPixelData(BufferedImage img, int x, int y) {

您有一个变量 i 用于计算行数,变量 j 用于计算列数,即 i 包含 y 个值,j 包含 x 个值。这是相反的。


我个人会将您的变量重命名为x和y。 - daveb
4
我会将它们命名为"行"和"列",以使描述更加准确。通常情况下,y轴是倒置的(从上到下),这与我们所熟悉的xy坐标系不同。使用"行"和"列"可以更容易地将图像看作一个矩阵,这样索引就不会产生歧义。 - Hannes Ovrén

5

我也曾经需要这种能力。不想枚举整张图片,所以我进行了一些搜索并使用了PixelGrabber。

Image img = Toolkit.getDefaultToolkit().createImage(filename);
PixelGrabber pg = new PixelGrabber(img, 0, 0, -1, -1, false);

pg.grabPixels(); // Throws InterruptedException

width = pg.getWidth();
height = pg.getHeight();

int[] pixels = (int[])pg.getPixels();

你可以直接使用int[],这里的像素格式由pg.getColorModel()中的ColorModel决定,或者你可以将false改为true并强制将其转换为RGB8-in-ints。
我后来发现Raster和Image类也可以实现这一点,并且在javax.imageio.*中添加了一些有用的类。
BufferedImage img = ImageIO.read(new File(filename)); // Throws IOException
int[] pixels = img.getRGB(0,0, img.getWidth(), img.getHeight, null, 0, img.getWidth());

// also available through the BufferedImage's Raster, in multiple formats.
Raster r = img.getData();
int[] pixels = r.getPixels(0,0,r.getWidth(), r.getHeight(), (int[])null);

Raster中也有几个getPixels(...)方法。


+1 我认为最后一次调用末尾缺少了null参数: int[] pixels = r.getPixels(0, 0, r.getWidth(), r.getHeight(), (int[])null); - Amro

5

这也可以运行:

BufferedImage img = ImageIO.read(file);

int[] pixels = ((DataBufferInt)img.getRaster().getDataBuffer()).getData();

在这种情况下,变量file的类型应该是什么? - Anderson Green
3
这对我来说会导致类转换异常 - java.lang.ClassCastException: java.awt.image.DataBufferByte 无法转换为 java.awt.image.DataBufferInt。 - Hayk Mantashyan
2
你不能盲目地将数据缓冲区强制转换为DataBufferInt,具体类型取决于原始类型(以及特定的解码器决策)。 - leonbloy

3

int argb = img.getRGB(x, y); 您的代码

int argb = img.getRGB(y, x); 我的更改,现在它可以工作了


2

您需要更改:

for(int i = 0; i < img.getHeight(); i++){
    for(int j = 0; j < img.getWidth(); j++){
        rgb = getPixelData(img, i, j);

进入

for(int i = 0; i < img.getWidth(); i++){
    for(int j = 0; j < img.getHeight(); j++){
        rgb = getPixelData(img, i, j);

由于getPixelData的第二个参数是x值,第三个参数是y值。你把这两个参数搞反了。

1
为什么不直接使用以下代码:
public int[] getRGB(int startX,
                    int startY,
                    int w,
                    int h,
                    int[] rgbArray,
                    int offset,
                    int scansize)

这是内置的,伙计。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接