জাভা - চিত্র থেকে পিক্সেল অ্যারে পান


118

আমি এ থেকে পিক্সেল ডেটা (ইন ফর্ম int[][]) পাওয়ার দ্রুততম উপায় খুঁজছি BufferedImage। আমার লক্ষ্যটি (x, y)ব্যবহার করে চিত্র থেকে পিক্সেলটি সম্বোধন করতে সক্ষম হব int[x][y]। আমি যে সমস্ত পদ্ধতি পেয়েছি সেগুলি এটি করে না (তাদের বেশিরভাগই ফিরে আসে int[])।


আপনি যদি গতি সম্পর্কে চিন্তিত হন তবে আপনি কেবল getRGBএবং setRGBসরাসরি ব্যবহারের পরিবর্তে কেন পুরো চিত্রটিকে একটি অ্যারে অনুলিপি করতে চান ?
ব্র্যাড ম্যাস

3
@ বিমেস: কারণ আমার প্রোফাইল অনুসারে এই পদ্ধতিগুলি কারও ভাবার চেয়ে বেশি কাজ করে বলে মনে হচ্ছে। কোনও অ্যারে অ্যাক্সেস করা দ্রুত গতিতে দেখায়।
রায়স্ট

15
@bemace: এটা আসলে সত্যিই তীব্র: একটি অ্যারে ব্যবহার করে চেয়ে বেশি 800% ব্যবহার চেয়ে দ্রুত getRGBএবং setRGBসরাসরি।
রায়স্ট

উত্তর:


179

আমি কেবল এই একই বিষয় নিয়ে ঘুরে দেখছিলাম, এটি পিক্সেল অ্যাক্সেসের দ্রুততম উপায়। আমি বর্তমানে এটি করার দুটি উপায় সম্পর্কে জানি:

  1. getRGB()@ Tskuzzy এর উত্তরে বর্ণিত বাফারডেমেজের পদ্ধতিটি ব্যবহার করা ।
  2. সরাসরি ব্যবহার করে পিক্সেল অ্যারে অ্যাক্সেস করে:

    byte[] pixels = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData();

আপনি যদি বড় ইমেজ নিয়ে কাজ করছেন এবং পারফরম্যান্স একটি সমস্যা হয়ে থাকে, প্রথম পদ্ধতিটি একেবারে যাওয়ার উপায় নয়। getRGB()পদ্ধতি এক int- এ মধ্যে আলফা, লাল, সবুজ এবং নীল মান সম্মিলন এবং তারপর ফলাফল, যা অধিকাংশ ক্ষেত্রে আপনি এই মান ফিরে পেতে বিপরীত চেষ্টা করবো ফেরৎ।

দ্বিতীয় পদ্ধতিটি প্রতিটি পিক্সেলের জন্য সরাসরি লাল, সবুজ এবং নীল মানগুলি ফিরিয়ে দেবে এবং যদি কোনও আলফা চ্যানেল থাকে তবে এটি আলফা মান যুক্ত করবে। সূচক গণনা করার ক্ষেত্রে এই পদ্ধতিটি ব্যবহার করা শক্ত, তবে প্রথম পদ্ধতির চেয়ে অনেক দ্রুত।

আমার অ্যাপ্লিকেশনটিতে আমি পিক্সেলগুলি প্রক্রিয়াকরণের সময়টি প্রথম পদ্ধতির থেকে দ্বিতীয়টিতে স্যুইচ করে 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 + 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)

10
কোডটি পড়তে খুব অলসদের জন্য, দুটি পরীক্ষা রয়েছে convertTo2DUsingGetRGBএবং convertTo2DWithoutUsingGetRGB। প্রথম পরীক্ষায় গড়ে 16 সেকেন্ড সময় লাগে। দ্বিতীয় টেস্টে গড়ে 1.5 সেকেন্ড সময় লাগে। প্রথমে আমি ভেবেছিলাম "এস" এবং "এমএস" দুটি পৃথক কলাম ছিল। @ মোটা, দুর্দান্ত রেফারেন্স।
জেসন

1
@ রেডি আমি এটিকে চেষ্টা করে দেখলাম এবং আমি ফাইলের আকারের মধ্যে একটি পার্থক্য দেখতে পাচ্ছি, যা কেন আমি নিশ্চিত নই! আমি অবশ্য এই কোডটি ব্যবহার করে সঠিক পিক্সেল মানগুলি পুনরায় উত্পাদন করতে সক্ষম হয়েছি (আলফা চ্যানেল ব্যবহার করে): পেস্টবিন .com/zukCK2tu আপনার বাফার্ডআইমেজ কনস্ট্রাক্টরের তৃতীয় যুক্তিটি সংশোধন করার দরকার হতে পারে, আপনি যে চিত্রটি নিয়ে কাজ করছেন তার উপর নির্ভর করে । আশা করি এটা কিছুটা সাহায্য করবে!
মোতাসিম

4
@ মোটা রূপান্তরকালে 2 ডিভাইসিং গেটআরজিবিতে আপনি ফলাফল কেন নিচ্ছেন [সারি] [কল] = চিত্র.getRGB (কল, সারি); ফলাফলের পরিবর্তে [সারি] [কল] = চিত্র.getRGB (সারি, কল);
কৈলাশ

6
লোকেরা রঙের পার্থক্য এবং / অথবা ভুল বাইট ক্রম পর্যবেক্ষণ করছে: @ মোতার কোডটি একটি বিজিআর ক্রম ধরেছে । আপনি পরীক্ষা করা উচিত ইনকামিং BufferedImageএর typeউদাঃ TYPE_INT_RGBবা TYPE_3BYTE_BGRএবং হাতল উপযুক্তভাবে। এটি আপনার জন্য একটি কাজ যা getRGB()এটি ধীর করে তোলে :-(
মিলহাউস

2
আমি ভুল হলে আমাকে সংশোধন করুন, তবে পদ্ধতি 2 তে মানগুলি সংযুক্ত করার |=পরিবর্তে এটি ব্যবহার করা আরও দক্ষ হবে না +=?
অন্টনেটর

24

এটার মতো কিছু?

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 );

11
এটি কি অবিশ্বাস্যভাবে অক্ষম নয়? আমি যদিও BufferedImageপিক্সেলগুলিকে 2D ইন্ট অ্যারে ব্যবহার করে সঞ্চয় করতাম , যাইহোক?
রায়স্ট

1
আমি নিশ্চিত যে চিত্রটি একক মাত্রিক ডেটা স্ট্রাকচার হিসাবে অভ্যন্তরীণভাবে সঞ্চিত রয়েছে। সুতরাং অপারেশনটি ও (ডাব্লু * এইচ) গ্রহণ করবে আপনি তা যেভাবেই করেন না কেন। আপনি প্রথমে একক মাত্রিক অ্যারেতে স্টোর করে পদ্ধতি কল ওভারহেড এড়াতে এবং একক মাত্রিক অ্যারেটিকে 2 ডি-অ্যারে রূপান্তর করতে পারেন।
tskuzzy

4
@ রাইস্ট আপনি যদি একটি অ্যারেতে সমস্ত পিক্সেল চান, এটি এটি যতটা দক্ষ তা হিসাবে কার্যকর
সান প্যাট্রিক ফ্লয়েড

1
+1, আমি এটির Rasterডেটা বাফারটি অ্যাক্সেস করে না বলে মনে করি না যা ত্বরণ পন্টিংয়ের ফলস্বরূপ অবশ্যই একটি ভাল জিনিস।
মের

2
এই টুইটটি ধীর @ মোটা দ্বারা পদ্ধতিটি পরীক্ষা করুন, এটি এই প্রচলিত পদ্ধতির চেয়ে দ্রুত।
h4ck3d

20

আমি খুঁজে পেলাম যে মোতার উত্তরটি আমাকে 10 গুণ গতি বাড়িয়েছে - তাই মোতাকে ধন্যবাদ।

আমি কোডটি একটি সুবিধাজনক শ্রেণিতে গুটিয়ে রেখেছি যা কনস্ট্রাক্টারে বাফার্ডআইমেজ নেয় এবং এটি একটি সমতুল্য getRBG (x, y) পদ্ধতির বহিঃপ্রকাশ করে যা এটিকে বাফারডিমেজ.বেগআরজিবি (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;
    }
}

আমি জাভাতে ইমেজ ফাইলগুলি প্রসেসিংয়ে নতুন। আপনি কীভাবে রঙআরআইপি-র getRGB () এর চেয়ে getRGB () তৈরি করা দ্রুত / উন্নত / আরও অনুকূল তা ব্যাখ্যা করতে পারেন? প্রশংসা!
এম কে 7

@ mk7 দয়া করে এই উত্তর কটাক্ষপাত করা stackoverflow.com/a/12062932/363573 । আরও তথ্যের জন্য জাভা টাইপ করুন কেন getrgb আপনার প্রিয় অনুসন্ধান ইঞ্জিনে ধীর
স্টিফান

10

আপনার বাফার্ডআইমেজ কোনও মনোক্রোম বিটম্যাপ থেকে না আসা পর্যন্ত মোতার উত্তর দুর্দান্ত। একটি মনোক্রোম বিটম্যাপের এর পিক্সেলের জন্য কেবল 2 সম্ভাব্য মান রয়েছে (উদাহরণস্বরূপ 0 = কালো এবং 1 = সাদা)। যখন কোনও মনোক্রোম বিটম্যাপ ব্যবহৃত হয় তখন the

final byte[] pixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();

কলটি এমন কাঁচা পিক্সেলের অ্যারে ডেটা দেয় যাতে প্রতিটি বাইটে একাধিক পিক্সেল থাকে।

সুতরাং আপনি যখন নিজের বাফারডিম্যাজ অবজেক্ট তৈরি করতে মনোক্রোম বিটম্যাপ চিত্র ব্যবহার করেন তখন আপনি যে আলগোরিদিমটি ব্যবহার করতে চান তা এটি:

/**
 * 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;
}

4

যদি দরকারী হয় তবে এটি ব্যবহার করে দেখুন:

BufferedImage imgBuffer = ImageIO.read(new File("c:\\image.bmp"));

byte[] pixels = (byte[])imgBuffer.getRaster().getDataElements(0, 0, imgBuffer.getWidth(), imgBuffer.getHeight(), null);

14
একটি ব্যাখ্যা সহায়ক হবে
asheshr

1

এখানে আরও একটি ফাস্টআরজিবি বাস্তবায়ন পাওয়া গেছে :

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;
    }
}

এটা কি?

বাফারডিমেজের getRGB পদ্ধতির মাধ্যমে পিক্সেল দিয়ে একটি চিত্র পিক্সেল পড়া বেশ ধীর, এই শ্রেণি এটির জন্য সমাধান।

ধারণাটি হ'ল আপনি একটি বাফারডিমেজ উদাহরণটি খাওয়ানোর মাধ্যমে অবজেক্টটি তৈরি করুন এবং এটি একবারে সমস্ত ডেটা পড়ে এবং এগুলিতে অ্যারে সংরক্ষণ করে। আপনি একবার পিক্সেল পেতে চাইলে আপনি getRGB কল করুন

নির্ভরতা

import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;

বিবেচ্য বিষয়

যদিও ফাস্টআরজিবি পিক্সেল পড়া আরও দ্রুত করে তোলে তবে এটি উচ্চ মেমরির ব্যবহার করতে পারে, কারণ এটি কেবল চিত্রটির একটি অনুলিপি সঞ্চয় করে। সুতরাং আপনার যদি মেমরিতে 4MB বাফারডিম্যাজ থাকে তবে একবার আপনি ফাস্টআরজিবি ইনস্ট্যান্স তৈরি করলে মেমরির ব্যবহার 8MB হয়ে যায়। আপনি তবে, ফাস্টআরজিবি তৈরির পরে বাফারডেমেজ ইনস্ট্যান্সটি পুনর্ব্যবহার করতে পারেন।

মধ্যে পড়ে না সতর্কতা অবলম্বন করা আবশ্যক OutOfMemoryException যখন এটা যেমন Android ফোন, যেখানে র্যাম একটি বোতলের হিসাবে ডিভাইসের ব্যবহার


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.