からピクセルデータ(フォームのint[][]
)を取得する最速の方法を探していBufferedImage
ます。私の目標は(x, y)
、を使用して画像からピクセルをアドレス指定できるようにすることint[x][y]
です。私が見つけたすべてのメソッドはこれを行いません(それらのほとんどはを返しますint[]
)。
getRGB
と、setRGB
直接使用するよりも800%以上速くなります。
からピクセルデータ(フォームのint[][]
)を取得する最速の方法を探していBufferedImage
ます。私の目標は(x, y)
、を使用して画像からピクセルをアドレス指定できるようにすることint[x][y]
です。私が見つけたすべてのメソッドはこれを行いません(それらのほとんどはを返しますint[]
)。
getRGB
と、setRGB
直接使用するよりも800%以上速くなります。
回答:
私はちょうど同じ主題で遊んでいました、それはピクセルにアクセスする最も速い方法です。私は現在、これを行う2つの方法を知っています。
getRGB()
@tskuzzyの回答に記載されているBufferedImageのメソッドを使用します。以下を使用してピクセル配列に直接アクセスします。
byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();
大きな画像で作業していて、パフォーマンスが問題である場合、最初の方法は絶対にうまくいきません。このgetRGB()
メソッドは、アルファ、赤、緑、青の値を1つのintに結合し、結果を返します。ほとんどの場合、これらの値を逆にしてこれらの値を取得します。
2番目のメソッドは、各ピクセルの赤、緑、青の値を直接返します。アルファチャネルがある場合は、アルファ値を追加します。この方法を使用すると、インデックスの計算は難しくなりますが、最初の方法よりはるかに高速です。
私のアプリケーションでは、最初のアプローチから2番目のアプローチに切り替えるだけで、ピクセルの処理時間を90%以上削減できました。
2つのアプローチを比較するために設定した比較を以下に示します。
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 + 3 < 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 + 2 < 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)
convertTo2DUsingGetRGB
とがありconvertTo2DWithoutUsingGetRGB
ます。最初のテストは平均で16秒かかります。2番目のテストの平均所要時間は1.5秒です。最初、「s」と「ms」は2つの異なる列だと思いました。@Mota、素晴らしいリファレンス。
BufferedImage
のtype
eg を確認するTYPE_INT_RGB
かTYPE_3BYTE_BGR
、適切に処理する必要があります。これはgetRGB()
あなたのために
|=
代わりに使用する方が効率的ではないでしょう+=
か?
このようなもの?
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 );
BufferedImage
とにかく、2Dのint配列を使用してピクセルを保存するのでしょうか?
Raster
のデータバッファーにアクセスするとは思いません。これは、加速パントを引き起こすため、間違いなく良いことです。
モタの答えは私に10倍の速度向上を与えてくれたので、モタに感謝します。
コンストラクターでBufferedImageを受け取り、BufferedImage.getRGB(x、y)を使用するコードの代わりにドロップする同等のgetRBG(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();
呼び出しは、各バイトに複数のピクセルが含まれるような方法で生のピクセル配列データを返します。
したがって、モノクロビットマップイメージを使用してBufferedImageオブジェクトを作成する場合、これは使用するアルゴリズムです。
/**
* 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);
ここに別のFastRGB実装があります:
public class FastRGB {
public int width;
public 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;
}
short[] getRGB(int x, int y) {
int pos = (y * pixelLength * width) + (x * pixelLength);
short rgb[] = new short[4];
if (hasAlphaChannel)
rgb[3] = (short) (pixels[pos++] & 0xFF); // Alpha
rgb[2] = (short) (pixels[pos++] & 0xFF); // Blue
rgb[1] = (short) (pixels[pos++] & 0xFF); // Green
rgb[0] = (short) (pixels[pos++] & 0xFF); // Red
return rgb;
}
}
これは何ですか?
BufferedImageのgetRGBメソッドを使用してピクセル単位で画像を読み取るのは非常に遅く、このクラスがこれを解決します。
アイデアは、BufferedImageインスタンスにオブジェクトを供給してオブジェクトを構築し、すべてのデータを一度に読み取って配列に格納するというものです。ピクセルを取得したい場合は、getRGBを呼び出します
依存関係
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
考慮事項
FastRGBはピクセルの読み取りを大幅に高速化しますが、画像のコピーを保存するだけなので、メモリ使用量が高くなる可能性があります。したがって、メモリに4MBのBufferedImageがある場合、FastRGBインスタンスを作成すると、メモリ使用量は8MBになります。ただし、FastRGBの作成後に、BufferedImageインスタンスをリサイクルできます。
RAMがボトルネックになっているAndroidフォンなどのデバイスでOutOfMemoryExceptionに陥らないように注意してください。
これは私のために働きました:
BufferedImage bufImgs = ImageIO.read(new File("c:\\adi.bmp"));
double[][] data = new double[][];
bufImgs.getData().getPixels(0,0,bufImgs.getWidth(),bufImgs.getHeight(),data[i]);
i
何ですか?
getRGB
、setRGB
直接および直接使用するのではなく、なぜ画像全体を配列にコピーしたいのですか?