2011-06-12 13 views
10

में आरजीबी डेटा की तेज़ लोडिंग और ड्राइंग विंडोज़ पर चल रहे कुछ जावा कोड में, मैं डिस्क से आरजीबी डेटा के कुछ बड़े ब्लॉक पढ़ रहा हूं और इसे जितनी जल्दी हो सके स्क्रीन पर प्रदर्शित करना चाहता हूं। आरजीबी डेटा बिना किसी अल्फा के 8 चैनल प्रति चैनल है। वर्तमान में मेरे पास BufferedImage बनाने के लिए निम्न जैसे कोड हैं।BufferedImage

BufferedImage getBufferedImage(File file, int width, int height) { 

    byte[] rgbData = readRGBFromFile(file); 

    WritableRaster raster = Raster.createInterleavedRaster(
     rgbData, width, height, 
     width * 3, // scanlineStride 
     3, // pixelStride 
     new int[]{0, 1, 2}, // bandOffsets 
     null); 

    ColorModel colorModel = new ComponentColorModel(
     ColorSpace.getInstance(ColorSpace.CS_sRGB), 
     new int[]{8, 8, 8}, // bits 
     false, // hasAlpha 
     false, // isPreMultiplied 
     ComponentColorModel.OPAQUE, 
     DataBuffer.TYPE_BYTE); 

    return new BufferedImage(colorModel, raster, false, null); 
} 

समस्या यह है कि स्क्रीन पर इसे प्रस्तुत करने का प्रदर्शन बहुत धीमा है। लगभग 250 - 300 एमएस। मैंने पढ़ा है कि सर्वश्रेष्ठ प्रदर्शन के लिए आपको स्क्रीन के साथ संगत BufferedImage में प्रदर्शित करने की आवश्यकता है। ऐसा करने के लिए, मैं उपर्युक्त विधि से इस तरह की विधि में लौटाई गई बफरर्ड छवि पास करता हूं।

BufferedImage createCompatibleImage(BufferedImage image) 
{ 
    GraphicsConfiguration gc = GraphicsEnvironment. 
     getLocalGraphicsEnvironment(). 
     getDefaultScreenDevice(). 
     getDefaultConfiguration(); 

    BufferedImage newImage = gc.createCompatibleImage(
     image.getWidth(), 
     image.getHeight(), 
     Transparency.TRANSLUCENT); 

    Graphics2D g = newImage.createGraphics(); 
    g.drawImage(image, 0, 0, null); 
    g.dispose(); 

    return newImage; 
} 

विधि अनिवार्य रूप से विंडोज पर ARGB लिए आरजीबी से बदल देता है और यह वास्तव में प्रदर्शित करने को गति है, लेकिन इस पद्धति एक 1600 x 1200 आरजीबी डेटा ब्लॉक के लिए ~ 300 एमएस लेता है यही कारण है कि। तो अब मैंने मूल रूप से ड्राइंग समस्या के प्रदर्शन हिट को एक कनवर्टिंग समस्या में व्यापार किया है।

300ms डिस्क के आरजीबी डेटा को लोड करने के लिए लगभग उसी समय होता है। मुझे लगता है कि मैं कुछ तेज कर सकता था।

क्या कोई बेहतर तरीका है कि मैं रूपांतरण कर सकता हूं? या अगर मैं आरजीबी डेटा संशोधित करता हूं और पहले से ही अल्फा चैनल जोड़ता हूं तो इससे मदद मिलेगी? यदि ऐसा है तो मेरा रास्टर और कलरमोडल कैसा दिखता है। साथ ही, चूंकि मेरे आरजीबी डेटा में पारदर्शिता नहीं है, क्या मुझे पूर्व गुणा अल्फा या कुछ का उपयोग करके कोई प्रदर्शन सुधार मिल सकता है?

क्षमा करें, थोड़ा मैं इस कलरमोडेल, रास्टर सामान पर थोड़ा खो गया हूं।

धन्यवाद!

उत्तर

11

इसके साथ खेलने के बाद मेरे पास एक सभ्य उत्तर है जो वर्तमान ग्राफिक्स कॉन्फ़िगरेशन एआरबीबी पूर्णांक पैक किए गए रास्टर्स का उपयोग कर रहा है।

मैं जो करता हूं वह पहले संगत BufferedImage बनाता है, तो मैं मैन्युअल रूप से अपने आरजीबी बाइट्स सरणी को एक ARGB int सरणी में परिवर्तित करता हूं। तब मुझे संगत BufferedImage से रास्टर मिलता है और इसमें मेरी एआरजीबी इंट्स लिखते हैं। यह बहुत तेज है।

मेरे पास एक कक्षा भी है जो जांचती है कि संगत BufferedImage प्रारूप में है या नहीं, अगर यह पुराने धीमे दृष्टिकोण के लिए डिफ़ॉल्ट नहीं है।

यहां कक्षा है। उम्मीद है कि यह आपकी मदद करता है।

/** 
* This class can read chunks of RGB image data out of a file and return a BufferedImage. 
* It may use an optimized technique for loading images that relies on assumptions about the 
* default image format on Windows. 
*/ 
public class RGBImageLoader 
{ 
    private byte[] tempBuffer_; 
    private boolean fastLoading_; 

    public RGBImageLoader() 
    { 
     fastLoading_ = canUseFastLoadingTechnique(); 
    } 

    private boolean canUseFastLoadingTechnique() 
    { 
     // Create an image that's compatible with the screen 
     GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 
     BufferedImage image = gc.createCompatibleImage(100, 100, Transparency.TRANSLUCENT); 

     // On windows this should be an ARGB integer packed raster. If it is then we can 
     // use our optimization technique 

     if(image.getType() != BufferedImage.TYPE_INT_ARGB) 
      return false; 

     WritableRaster raster = image.getRaster(); 

     if(!(raster instanceof IntegerInterleavedRaster)) 
      return false; 

     if(!(raster.getDataBuffer() instanceof DataBufferInt)) 
      return false; 

     if(!(image.getColorModel() instanceof DirectColorModel)) 
      return false; 

     DirectColorModel colorModel = (DirectColorModel) image.getColorModel(); 

     if(!(colorModel.getColorSpace() instanceof ICC_ColorSpace) || 
      colorModel.getNumComponents() != 4 || 
      colorModel.getAlphaMask() != 0xff000000 || 
      colorModel.getRedMask() != 0xff0000 || 
      colorModel.getGreenMask() != 0xff00 || 
      colorModel.getBlueMask() != 0xff) 
      return false; 

     if(raster.getNumBands() != 4 || 
      raster.getNumDataElements() != 1 || 
      !(raster.getSampleModel() instanceof SinglePixelPackedSampleModel)) 
      return false; 

     return true; 
    } 

    public BufferedImage loadImage(File file, int width, int height, long imageOffset) throws IOException 
    { 
     if(fastLoading_) 
      return loadImageUsingFastTechnique(file, width, height, imageOffset); 
     else 
      return loadImageUsingCompatibleTechnique(file, width, height, imageOffset); 
    } 

    private BufferedImage loadImageUsingFastTechnique(File file, int width, int height, long imageOffset) throws IOException 
    { 
     int sizeBytes = width * height * 3; 

     // Make sure buffer is big enough 
     if(tempBuffer_ == null || tempBuffer_.length < sizeBytes) 
      tempBuffer_ = new byte[sizeBytes]; 

     RandomAccessFile raf = null; 
     try 
     { 
      raf = new RandomAccessFile(file, "r"); 

      raf.seek(imageOffset); 

      int bytesRead = raf.read(tempBuffer_, 0, sizeBytes); 
      if (bytesRead != sizeBytes) 
       throw new IOException("Invalid byte count. Should be " + sizeBytes + " not " + bytesRead); 

      GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 
      BufferedImage image = gc.createCompatibleImage(width, height, Transparency.TRANSLUCENT); 
      WritableRaster raster = image.getRaster(); 
      DataBufferInt dataBuffer = (DataBufferInt) raster.getDataBuffer(); 

      addAlphaChannel(tempBuffer_, sizeBytes, dataBuffer.getData()); 

      return image; 
     } 
     finally 
     { 
      try 
      { 
       if(raf != null) 
       raf.close(); 
      } 
      catch(Exception ex) 
      { 
      } 
     } 
    } 

    private BufferedImage loadImageUsingCompatibleTechnique(File file, int width, int height, long imageOffset) throws IOException 
    { 
     int sizeBytes = width * height * 3; 

     RandomAccessFile raf = null; 
     try 
     { 
      raf = new RandomAccessFile(file, "r"); 

      // Lets navigate to the offset 
      raf.seek(imageOffset); 

      DataBufferByte dataBuffer = new DataBufferByte(sizeBytes); 
      byte[] bytes = dataBuffer.getData(); 

      int bytesRead = raf.read(bytes, 0, sizeBytes); 
      if (bytesRead != sizeBytes) 
       throw new IOException("Invalid byte count. Should be " + sizeBytes + " not " + bytesRead); 

      WritableRaster raster = Raster.createInterleavedRaster(dataBuffer, // dataBuffer 
          width, // width 
          height, // height 
          width * 3, // scanlineStride 
          3, // pixelStride 
          new int[]{0, 1, 2}, // bandOffsets 
          null); // location 

      ColorModel colorModel = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), // ColorSpace 
          new int[]{8, 8, 8}, // bits 
          false, // hasAlpha 
          false, // isPreMultiplied 
          ComponentColorModel.OPAQUE, DataBuffer.TYPE_BYTE); 

      BufferedImage loadImage = new BufferedImage(colorModel, raster, false, null); 

      // Convert it into a buffered image that's compatible with the current screen. 
      // Not ideal creating this image twice.... 
      BufferedImage image = createCompatibleImage(loadImage); 

      return image; 
     } 
     finally 
     { 
      try 
      { 
       if(raf != null) 
       raf.close(); 
      } 
      catch(Exception ex) 
      { 
      } 
     } 
    } 

    private BufferedImage createCompatibleImage(BufferedImage image) 
    { 
     GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration(); 

     BufferedImage newImage = gc.createCompatibleImage(image.getWidth(), image.getHeight(), Transparency.TRANSLUCENT); 

     Graphics2D g = newImage.createGraphics(); 
     g.drawImage(image, 0, 0, null); 
     g.dispose(); 

     return newImage; 
    } 


    private void addAlphaChannel(byte[] rgbBytes, int bytesLen, int[] argbInts) 
    { 
     for(int i=0, j=0; i<bytesLen; i+=3, j++) 
     { 
      argbInts[j] = ((byte) 0xff) << 24 |     // Alpha 
         (rgbBytes[i] << 16) & (0xff0000) |  // Red 
         (rgbBytes[i+1] << 8) & (0xff00) |  // Green 
         (rgbBytes[i+2]) & (0xff);    // Blue 
     } 
    } 

} 
+0

2 सुझाव: 1) स्रोत में 'compatabile' ->' संगत 'बदलें। 2) अपने उत्तर को 'सही' के रूप में चिह्नित करें। –

+1

+1 ... मैं यह उल्लेख करने जा रहा था कि कलरमोडेल मंदता वास्तव में सबकुछ धीमा कर देती है (कुछ ओएस एक्स संस्करणों पर मैग्नीट्यूड के ** 3 ऑर्डर ** तक, हाँ, 1000 गुना धीमी ** ** ** प्रदान करता है और प्रदान करता है मूल रूप से कुछ नहीं (गंभीरता से, जावा में लिखी गई कितनी पेशेवर छवि/वीडियो हेरफेर संपादन वहाँ बाहर है? यह सही है, शून्य ...) * int [] * के साथ सीधे काम करने की तुलना में। यह एक और मामला है * "ज़ूम हमारे पास ऑब्जेक्ट्स हैं, चलो ओवरथिंक करें और किसी भी कारण से सब कुछ ओवरवॉप करें" *। * int [] * जीत के लिए आदिम हेरफेर: इस तरह गति को चिंता होने पर आपको ऐसा करना होगा। – SyntaxT3rr0r

+0

हां मेरी गति लगभग 300ms से 15ms से कम हो गई। – awinbra

19

मुझे पता है यह एक बहुत पुराना सवाल है, मैं सिर्फ किसी और ने इस प्रश्न और अधिक विकल्प की तलाश पर आते हैं हो सकता है के लिए इस पोस्टिंग कर रहा हूँ। मुझे हाल ही में एक मुद्दा था जहां मैं एक बड़ा (720 पी) आरजीबी बाइट [] लेने का प्रयास कर रहा था और इसे BufferedImage पर प्रस्तुत करता था। मूल कार्यान्वयन मैं उपयोग कर रहा था कुछ इस तरह (यहाँ सरलीकृत) देखा: यहां तक ​​कि BufferedImage और ComponentSampleModel बनाने एक बार और उसका पुनः उपयोग की तरह अनुकूलन के साथ

public void processFrame(byte[] frame, int width, int height) 
{ 
    DataBuffer videoBuffer = new DataBufferByte(frame,frame.length); 
    BufferedImage currentImage = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR); 
    ComponentSampleModel sampleModel = new ComponentSampleModel(DataBuffer.TYPE_BYTE,width,height,3,width*3,new int[] {2,1,0}); 
    Raster raster = Raster.createRaster(sampleModel,videoBuffer,null); 
    currentImage.setData(raster); 
} 

, BufferedImage पर setData बुला के अंतिम चरण पर अभी भी ले रहा था 50-60 मिलीसेकंड का आदेश, जो अस्वीकार्य है।

क्या मैं साकार समाप्त हो गया है कि, कम से कम मेरी परिदृश्य के लिए, आप वास्तव में सीधे BufferedImage और बाईपास मध्यवर्ती प्रसंस्करण के सबसे का समर्थन बाइट सरणी के लिए लिख सकते हैं (पहले से ही सही मानते हुए छवि के लिए समर्थन मेटाडाटा है) ।तो मैं इस तरह देखने के लिए मेरी कोड बदल: बस ऐसा करने से

public void processFrame(byte[] frame, int width, int height) 
{ 
    BufferedImage currentImage = new BufferedImage(width,height,BufferedImage.TYPE_3BYTE_BGR); 
    byte[] imgData = ((DataBufferByte)currentImage.getRaster().getDataBuffer()).getData(); 
    System.arraycopy(frame,0,imgData,0,frame.length); 
} 

, मेरे प्रदर्शन 20 वर्ष की कारक के बारे में द्वारा सुधार किया मैं अब के बजाय 3-5 मिलीसेकेंड में एक ही फ्रेम 50-60 मिलीसेकेंड की प्रक्रिया।

यह सभी मामलों के लिए लागू नहीं हो सकता है, लेकिन मैंने सोचा कि अगर किसी और को यह उपयोगी लगे तो मैं साझा करूंगा।

+0

बढ़िया! यह मेरे लिए ठीक काम करता है, मैं इस बेवकूफ 'setData()' को तेजी से काम करने के लिए 'बुफर्ड इमेज' को उपclass करने के लिए तैयार था। आपने मुझे बचाया, धन्यवाद! :) – Matthieu

+0

I मैं उलझन में हूँ ... यहाँ क्या चल रहा है? कि 'System.arraycopy() '' imgData' को ओवरराइट कर रहा है। क्या यह दूसरी तरफ नहीं होना चाहिए? –

+0

यह पोस्ट करने के बाद से कुछ समय हो गया है, इसलिए अगर इसकी कमी हो रही है तो क्षमा करें ... 'imgData' वास्तविक' बाइट [] '(BufferedImage के WritableRaster के सदस्य चर के रूप में संग्रहीत) का संदर्भ है, जिसका उपयोग वास्तव में किया जाता है 'BufferedImage' कक्षा द्वारा छवि प्रस्तुत करें। 'फ्रेम' एक बाइट [] 'है जिसे मैंने बनाया है जिसे मैं बुफर्ड इमेज के माध्यम से प्रस्तुत करना चाहता हूं। तो System.arraycopy (...) 'BufferedImage' के आंतों में बैकिंग 'बाइट []' पर निर्मित छवि (' frame' में निहित) की प्रतिलिपि बनाता है। अब जब 'बुफर्ड इमेज' प्रदान किया जाता है, तो यह 'फ्रेम' से डेटा का उपयोग करके किया जाता है जिसे मैंने 'बुफर्ड इमेज' में कॉपी किया था –

संबंधित मुद्दे