Java – 从图像获取像素数组
我正在寻找从BufferedImage
获取像素数据(intforms为int[][]
)的最快方法。 我的目标是能够使用int[x][y]
从图像中处理像素(x, y)
。 我发现的所有方法都不这样做(大多数方法返回int[]
s)。
我只是玩这个相同的主题,这是访问像素的最快方法。 我目前知道这样做的两种方法:
- 使用BufferedImage的
getRGB()
方法,如@ tskuzzy的答案中所述。 -
通过直接访问像素数组:
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
如果你正在处理大图像,性能是一个问题,第一种方法绝对不是。 getRGB()
方法将alpha,红色,绿色和蓝色值组合成一个int,然后返回结果,在大多数情况下,您将执行相反操作以获取这些值。
第二种方法将直接为每个像素返回红色,绿色和蓝色的值,如果有一个alpha通道,它将添加alpha值。 使用这种方法在计算指数方面比较困难,但比第一种方法快得多。
在我的应用程序中,通过从第一种方法切换到第二种方法,我能够将像素处理时间缩短90%以上!
这里是我设置比较两种方法的比较:
import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.IOException; import javax.imageio.ImageIO; public class PerformanceTest { public static void main(String[] args) throws IOException { BufferedImage hugeImage = ImageIO.read(PerformanceTest.class.getResource("12000X12000.jpg")); System.out.println("Testing convertTo2DUsingGetRGB:"); for (int i = 0; i < 10; i++) { long startTime = System.nanoTime(); int[][] result = convertTo2DUsingGetRGB(hugeImage); long endTime = System.nanoTime(); System.out.println(String.format("%-2d: %s", (i + 1), toString(endTime - startTime))); } System.out.println(""); System.out.println("Testing convertTo2DWithoutUsingGetRGB:"); for (int i = 0; i < 10; i++) { long startTime = System.nanoTime(); int[][] result = convertTo2DWithoutUsingGetRGB(hugeImage); long endTime = System.nanoTime(); System.out.println(String.format("%-2d: %s", (i + 1), toString(endTime - startTime))); } } private static int[][] convertTo2DUsingGetRGB(BufferedImage image) { int width = image.getWidth(); int height = image.getHeight(); int[][] result = new int[height][width]; for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { result[row][col] = image.getRGB(col, row); } } return result; } private static int[][] convertTo2DWithoutUsingGetRGB(BufferedImage image) { final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); final int width = image.getWidth(); final int height = image.getHeight(); final boolean hasAlphaChannel = image.getAlphaRaster() != null; int[][] result = new int[height][width]; if (hasAlphaChannel) { final int pixelLength = 4; for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) { int argb = 0; argb += (((int) pixels[pixel] & 0xff) << 24); // alpha argb += ((int) pixels[pixel + 1] & 0xff); // blue argb += (((int) pixels[pixel + 2] & 0xff) << 8); // green argb += (((int) pixels[pixel + 3] & 0xff) << 16); // red result[row][col] = argb; col++; if (col == width) { col = 0; row++; } } } else { final int pixelLength = 3; for (int pixel = 0, row = 0, col = 0; pixel < pixels.length; pixel += pixelLength) { int argb = 0; argb += -16777216; // 255 alpha argb += ((int) pixels[pixel] & 0xff); // blue argb += (((int) pixels[pixel + 1] & 0xff) << 8); // green argb += (((int) pixels[pixel + 2] & 0xff) << 16); // red result[row][col] = argb; col++; if (col == width) { col = 0; row++; } } } return result; } private static String toString(long nanoSecs) { int minutes = (int) (nanoSecs / 60000000000.0); int seconds = (int) (nanoSecs / 1000000000.0) - (minutes * 60); int millisecs = (int) ( ((nanoSecs / 1000000000.0) - (seconds + minutes * 60)) * 1000); if (minutes == 0 && seconds == 0) return millisecs + "ms"; else if (minutes == 0 && millisecs == 0) return seconds + "s"; else if (seconds == 0 && millisecs == 0) return minutes + "min"; else if (minutes == 0) return seconds + "s " + millisecs + "ms"; else if (seconds == 0) return minutes + "min " + millisecs + "ms"; else if (millisecs == 0) return minutes + "min " + seconds + "s"; return minutes + "min " + seconds + "s " + millisecs + "ms"; } }
你能猜到输出吗? ;)
Testing convertTo2DUsingGetRGB: 1 : 16s 911ms 2 : 16s 730ms 3 : 16s 512ms 4 : 16s 476ms 5 : 16s 503ms 6 : 16s 683ms 7 : 16s 477ms 8 : 16s 373ms 9 : 16s 367ms 10: 16s 446ms Testing convertTo2DWithoutUsingGetRGB: 1 : 1s 487ms 2 : 1s 940ms 3 : 1s 785ms 4 : 1s 848ms 5 : 1s 624ms 6 : 2s 13ms 7 : 1s 968ms 8 : 1s 864ms 9 : 1s 673ms 10: 2s 86ms BUILD SUCCESSFUL (total time: 3 minutes 10 seconds)
像这样的东西?
int[][] pixels = new int[w][h]; for( int i = 0; i < w; i++ ) for( int j = 0; j < h; j++ ) pixels[i][j] = img.getRGB( i, j );
我发现莫塔的回答给了我10倍的提速 – 所以谢谢莫塔。
我将代码封装在一个方便的类中,该类使用构造函数中的BufferedImage,并公开了一个等价的getRBG(x,y)方法,使用BufferedImage.getRGB(x,y)替代代码,
import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; public class FastRGB { private int width; private int height; private boolean hasAlphaChannel; private int pixelLength; private byte[] pixels; FastRGB(BufferedImage image) { pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData(); width = image.getWidth(); height = image.getHeight(); hasAlphaChannel = image.getAlphaRaster() != null; pixelLength = 3; if (hasAlphaChannel) { pixelLength = 4; } } int getRGB(int x, int y) { int pos = (y * pixelLength * width) + (x * pixelLength); int argb = -16777216; // 255 alpha if (hasAlphaChannel) { argb = (((int) pixels[pos++] & 0xff) << 24); // alpha } argb += ((int) pixels[pos++] & 0xff); // blue argb += (((int) pixels[pos++] & 0xff) << 8); // green argb += (((int) pixels[pos++] & 0xff) << 16); // red return argb; } }
莫塔的答案是伟大的,除非你的BufferedImage来自一个单色的位图。 单色位图的像素只有2个可能的值(例如0 =黑色,1 =白色)。 当使用单色位图时,
final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
调用以每个字节包含多个像素的方式返回原始像素arrays数据。
所以,当你使用单色的位图图像来创build你的BufferedImage对象,那么这就是你想要使用的algorithm:
/** * This returns a true bitmap where each element in the grid is either a 0 * or a 1. A 1 means the pixel is white and a 0 means the pixel is black. * * If the incoming image doesn't have any pixels in it then this method * returns null; * * @param image * @return */ public static int[][] convertToArray(BufferedImage image) { if (image == null || image.getWidth() == 0 || image.getHeight() == 0) return null; // This returns bytes of data starting from the top left of the bitmap // image and goes down. // Top to bottom. Left to right. final byte[] pixels = ((DataBufferByte) image.getRaster() .getDataBuffer()).getData(); final int width = image.getWidth(); final int height = image.getHeight(); int[][] result = new int[height][width]; boolean done = false; boolean alreadyWentToNextByte = false; int byteIndex = 0; int row = 0; int col = 0; int numBits = 0; byte currentByte = pixels[byteIndex]; while (!done) { alreadyWentToNextByte = false; result[row][col] = (currentByte & 0x80) >> 7; currentByte = (byte) (((int) currentByte) << 1); numBits++; if ((row == height - 1) && (col == width - 1)) { done = true; } else { col++; if (numBits == 8) { currentByte = pixels[++byteIndex]; numBits = 0; alreadyWentToNextByte = true; } if (col == width) { row++; col = 0; if (!alreadyWentToNextByte) { currentByte = pixels[++byteIndex]; numBits = 0; } } } } return result; }
如果有用的话,试试这个:
BufferedImage imgBuffer = ImageIO.read(new File("c:\\image.bmp")); byte[] pixels = (byte[])imgBuffer.getRaster().getDataElements(0, 0, imgBuffer.getWidth(), imgBuffer.getHeight(), null);
这对我工作:
BufferedImage bufImgs = ImageIO.read(new File("c:\\adi.bmp")); double[][] data = new double[][]; bufImgs.getData().getPixels(0,0,bufImgs.getWidth(),bufImgs.getHeight(),data[i]);