2014-11-07 20 views
7

Có thể gợi ý rằng BufferedImage là tùy chọn tốt nhất để xử lý hình ảnh trong Java. Trong khi tiện lợi, khi đọc những hình ảnh khổng lồ, nó thường kết thúc bằng:Cách đọc hình ảnh từ luồng?

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 

Tăng kích thước VM không phải là giải pháp vì một số tệp đầu vào thực sự rất lớn trong trường hợp của tôi.

Vì vậy, tôi đang tìm cách (các) cách một hình ảnh có thể được đọc dần dần, từ một luồng.

Tôi nghi ngờ rằng ImageIO.createImageInputStream() từ ImageIO có thể phù hợp với hóa đơn, nhưng tôi không biết cách sử dụng nó để đọc số chunks dần dần. Ngoài ra, có các lớp học PNGMetadata & PNGImageReader có sẵn trên số rt.jar của JDK có vẻ hữu ích, nhưng tôi không tìm thấy ví dụ đơn giản về cách sử dụng của chúng.

Đây có phải là cách để đi, hoặc có lựa chọn thay thế tốt hơn?

+0

Hãy chú ý để lại nhận xét nếu bạn downvote hoặc bỏ phiếu để đóng. – blackSmith

+2

Không cần, chú giải công cụ cho mũi tên xuống đã cho bạn biết mọi thứ bạn cần biết – Clive

+0

@Clive: cảm ơn mẹo. Tuy nhiên nếu tôi đã có thể biết làm thế nào để truy cập vào tiêu đề bằng cách sử dụng java, tôi sẽ không có đăng nó. Để rõ ràng tôi có thể chỉnh sửa câu hỏi. Cuối cùng tôi không thể chấp nhận rằng nó 'không hữu ích' vì SO có quá nhiều 'java.lang.OutOfMemoryError: khoảng trống Java' khi sử dụng các câu hỏi' BufferedImage'. – blackSmith

Trả lời

11

Các API Java để đọc và thao tác hình ảnh không thực sự dựa trên luồng theo cách bạn nghĩ. Các ImageInputStream chỉ là một wrapper tiện lợi cho phép đọc byte s và các loại nguyên thủy khác từ đầu vào khác nhau (RandomAccessFile s, InputStream s vv).

Tôi đã suy nghĩ về việc tạo API để đọc "luồng pixel", để cho phép chuỗi các bộ lọc xử lý mà không cần sử dụng nhiều bộ nhớ. Nhưng nhu cầu chưa bao giờ đủ nghiêm túc để xứng đáng với nỗ lực. Cảm thấy tự do để thuê tôi, nếu bạn muốn nghe thêm ý tưởng hoặc có một thực hiện làm việc.;-)

Tuy nhiên, như tôi nhìn thấy nó, bạn có nhiều lựa chọn để đạt được mục tiêu cuối cùng của bạn, để có thể xử lý hình ảnh lớn:

  1. Sử dụng BufferedImage như là, và ImageIO API để đọc một hình ảnh trong các phần nhỏ hơn để bảo tồn bộ nhớ. Điều này sẽ khá hiệu quả đối với một số định dạng, ít hiệu quả hơn đối với các định dạng khác, do thực hiện (ví dụ: JPEGImageReader mặc định sẽ đọc toàn bộ hình ảnh trong bộ nhớ riêng trước khi giao vùng nhỏ hơn cho vùng heap Java, nhưng PNGImageReader có thể không sao).

    cái gì đó dọc theo dòng:

    ImageInputStream stream = ImageIO.createImageInputStream(input); 
    ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() 
    reader.setInput(stream); 
    
    int width = reader.getWidth(0); 
    int height = reader.getHeight(0); 
    
    ImageReadParam param = reader.getDefaultReadParam(); 
    
    for (int y = 0; y < height; y += 100) { 
        for (int x = 0; x < width; x += 100) { 
          param.setSourceRegion(new Rectangle(x, y, 100, 100)); // TODO: Bounds check 
    
          // Read a 100 x 100 tile from the image 
          BufferedImage region = reader.read(0, param); 
    
          // ...process region as needed... 
        } 
    } 
    
  2. Đọc toàn bộ hình ảnh cùng một lúc, vào một bộ nhớ ánh xạ đệm. Hãy thử một số experimental classes Tôi đã thực hiện cho mục đích này (sử dụng nio). Việc đọc sẽ chậm hơn việc đọc sang một hình ảnh bộ nhớ thuần túy và việc xử lý cũng sẽ chậm hơn. Nhưng nếu bạn đang tính toán trên các vùng nhỏ hơn của hình ảnh tại một thời điểm, nó có thể nhanh như trong bộ nhớ với một số tối ưu hóa. Tôi đã đọc> 1 GB hình ảnh vào một JVM 32 MB sử dụng các lớp này (tiêu thụ bộ nhớ thực là tất nhiên lớn hơn nhiều).

    Một lần nữa, đây là một ví dụ:

    ImageInputStream stream = ImageIO.createImageInputStream(input); 
    ImageReader reader = ImageIO.getImageReaders(stream).next(); // TODO: Test hasNext() 
    reader.setInput(stream); 
    
    int width = reader.getWidth(0); 
    int height = reader.getHeight(0); 
    ImageTypeSpecifier spec = reader.getImageTypes(0).next(); // TODO: Test hasNext(); 
    
    BufferedImage image = MappedImageFactory.createCompatibleMappedImage(width, height, spec) 
    
    ImageReadParam param = reader.getDefaultReadParam(); 
    param.setDestination(image); 
    
    image = reader.read(0, param); // Will return same image as created above 
    
    // ...process image as needed... 
    
  3. Một số định dạng, như TIFF không nén, BMP, PPM vv, giữ pixel trong file trong một cách mà nó sẽ có thể để bộ nhớ bản đồ trực tiếp để thao túng chúng. Yêu cầu một số công việc, nhưng nên có thể. TIFF cũng hỗ trợ các ô có thể trợ giúp. Tôi sẽ để tùy chọn này như là một bài tập, cảm thấy tự do để sử dụng các lớp tôi liên kết ở trên như là một điểm khởi đầu hoặc cảm hứng. ;-)

  4. JAI có thể có thứ gì đó có thể giúp bạn. Tôi không phải là một fan hâm mộ lớn, do có nhiều lỗi chưa được giải quyết và thiếu tình yêu và sự phát triển của Oracle. Nhưng đáng để kiểm tra. Tôi nghĩ rằng họ cũng có hỗ trợ cho các thiết bị dựa trên teceable và dựa trên đĩa RenderedImage. Một lần nữa, tôi sẽ để lại tùy chọn này để bạn khám phá thêm.

+0

Dường như quản lý được, tôi thực sự không ở đâu trước cả hai câu trả lời. Cho phép xem cách xa tôi có thể đi với khẩu phần '(inspi | explo) 'của tôi. Chắc chắn sẽ tìm kiếm sự hỗ trợ từ bạn. Bằng cách +1 cho ưu đãi tuyển dụng, mặc dù tôi chỉ là – blackSmith

4

Vấn đề bộ nhớ chắc chắn liên quan không phải với quá trình giải mã mà còn lưu trữ toàn bộ hình ảnh trong bộ nhớ dưới dạng BufferedImage. Có thể đọc một hình ảnh PNG dần, nhưng:

  • này chỉ được nhẹ liên quan đến tổ chức trong "khối", hơn thế nữa với thực tế rằng các file PNG được mã hóa line-by-line, và do đó chúng có thể được đọc theo từng dòng.

  • Các vi phạm giả định nêu trên trong trường hợp của PNG chằng chịt - nhưng ta không nên mong đợi để có hình ảnh PNG khổng lồ được lưu trữ ở định dạng interlaced

  • Trong khi một số thư viện PNG cho phép tiến (line-by-line) giải mã (ví dụ: libpng), API Java chuẩn không cung cấp cho bạn điều đó.

Tôi đã gặp phải sự cố đó và tôi đã lập mã hóa thư viện Java của riêng mình: PNGJ. Nó khá trưởng thành, nó cho phép đọc hình ảnh PNG theo từng dòng, giảm thiểu mức tiêu thụ bộ nhớ, và ghi chúng theo cách tương tự (thậm chí có thể đọc cả PNG có thể đọc được, nhưng trong trường hợp này vấn đề bộ nhớ sẽ không biến mất.) Nếu bạn chỉ cần để làm một số xử lý hình ảnh "cục bộ" (sửa đổi mỗi giá trị pixel tùy thuộc vào giá trị hiện tại và hàng xóm, và viết lại) điều này sẽ giúp ích.

Các vấn đề liên quan