2010-03-09 34 views
37

Tôi gặp sự cố khi đọc tệp JPEG này bằng ImageIO.read (Tệp tệp) - nó ném ngoại lệ với thông báo "Loại hình ảnh không được hỗ trợ".Không thể đọc ảnh JPEG bằng ImageIO.read (Tệp tập tin)

Tôi đã thử các hình ảnh JPEG khác và chúng dường như hoạt động tốt.

Sự khác biệt duy nhất tôi có thể nhận ra là tệp này dường như bao gồm hình thu nhỏ - được biết là gây ra sự cố với ImageIO.read()?

Troublesome image

EDIT:

gia tăng hình ảnh kết quả:

Strange colors

+0

Sẽ hữu ích khi xem stacktrace từ ngoại lệ. – simonlord

+8

Vui lòng khôi phục lại hình ảnh! – math

Trả lời

34

Hình ảnh của bạn "Màu Model" là CMYK, JPEGImageReader (các lớp bên trong mà đọc tập tin của bạn) chỉ đọc Mô hình màu RGB.

Nếu bạn nhấn mạnh vào việc đọc hình ảnh CMYK, thì bạn sẽ cần phải chuyển đổi chúng, hãy thử mã này.

CẬP NHẬT

Đọc một hình ảnh CMYK vào RGB BufferedImage.

File f = new File("/path/imagefile.jpg"); 

    //Find a suitable ImageReader 
    Iterator readers = ImageIO.getImageReadersByFormatName("JPEG"); 
    ImageReader reader = null; 
    while(readers.hasNext()) { 
     reader = (ImageReader)readers.next(); 
     if(reader.canReadRaster()) { 
      break; 
     } 
    } 

    //Stream the image file (the original CMYK image) 
    ImageInputStream input = ImageIO.createImageInputStream(f); 
    reader.setInput(input); 

    //Read the image raster 
    Raster raster = reader.readRaster(0, null); 

    //Create a new RGB image 
    BufferedImage bi = new BufferedImage(raster.getWidth(), raster.getHeight(), 
    BufferedImage.TYPE_4BYTE_ABGR); 

    //Fill the new image with the old raster 
    bi.getRaster().setRect(raster); 

CẬP NHẬT - March 2015 - Thêm hình ảnh mô phỏng

hình ảnh gốc đã được gỡ bỏ từ dropbox OP của. Vì vậy, tôi đang thêm hình ảnh mới (không phải bản gốc) mô phỏng vấn đề đang xảy ra với họ.

Hình ảnh đầu tiên là hình ảnh RGB bình thường như thế nào.

Image RGB

Thứ hai hình ảnh là như thế nào cùng một hình ảnh sẽ trông giống như trong mô hình màu CMYK.

Bạn thực sự không thể thấy nó trông như thế nào trên web vì nó sẽ được chuyển đổi thành RGB bởi máy chủ. Để xem chính xác nó trông như thế nào, hãy lấy hình ảnh RGB và chạy nó qua bộ chuyển đổi RGB sang CMYK.

Hình ảnh thứ ba là hình ảnh CMYK sẽ trông như thế nào khi đọc sau đó được viết bằng Java ImageIO.

Image CMYK read through Java RGB

Vấn đề mà đã xảy ra với OP là họ có một cái gì đó giống như hình ảnh 2 mà ném một ngoại lệ khi bạn cố gắng đọc nó.

+0

Tuyệt vời, tôi sẽ thử. Nó sẽ làm việc cho hình ảnh RGB là tốt, hoặc tôi sẽ cần phải phát hiện các loại bằng cách nào đó? – Malakim

+0

Bạn sẽ tìm thấy nhiều cách để phát hiện mô hình màu, ưu tiên của tôi là sử dụng JPEGImageReader, nếu nó ném ngoại lệ 'Loại hình không được hỗ trợ 'thì có thể là CMYK nhất. – medopal

+1

Điều này hoạt động tốt đủ, tuy nhiên, các màu sắc được tất cả các sai lầm.Xem hình ảnh mới mà tôi đã đính kèm với câu hỏi. Bạn có lời khuyên nào về việc này không? Cảm ơn! – Malakim

5

tôi thấy https://stackoverflow.com/questions/22409... trên đây là tốt, cái này làm một sự chuyển đổi màu sắc lớn

Và kết hợp cả hai để có được điều này:

private BufferedImage convertCMYK2RGB(BufferedImage image) throws IOException{ 
    log.info("Converting a CYMK image to RGB"); 
    //Create a new RGB image 
    BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(), 
    BufferedImage.TYPE_3BYTE_BGR); 
    // then do a funky color convert 
    ColorConvertOp op = new ColorConvertOp(null); 
    op.filter(image, rgbImage); 
    return rgbImage; 
} 
+1

Đây là câu trả lời duy nhất tôi tìm thấy để khắc phục sự cố với các màu xanh lục trên các hình JPEG có một số câu hỏi về SO. – Phil

6

ImageIO.read() ->

File filePath = new File("C:\\Users\\chang\\Desktop\\05036877.jpg"); 
com.sun.image.codec.jpeg.JPEGImageDecoder jpegDecoder = JPEGCodec.createJPEGDecoder (new FileInputStream(filePath)); 

BufferedImage image = jpegDecoder.decodeAsBufferedImage(); 
+4

Từ tài liệu API: 'Lưu ý rằng các lớp trong gói com.sun.image.codec.jpeg không phải là một phần của các API Java lõi. Chúng là một phần của bản phân phối JDK và JRE của Sun. Mặc dù những người được cấp phép khác có thể chọn phân phối các lớp này, các nhà phát triển không thể phụ thuộc vào sự sẵn có của họ trong các triển khai không phải của Sun. Chúng tôi hy vọng rằng chức năng tương đương cuối cùng sẽ có sẵn trong API lõi hoặc tiện ích mở rộng tiêu chuẩn.' – Omertron

18

Tôi một chút muộn cho bữa tiệc. Nhưng nó có lẽ vẫn còn giá trị mà tôi đăng câu trả lời của tôi là không ai trong số các câu trả lời thực sự giải quyết vấn đề.

Giải pháp yêu cầu Sanselan (hoặc Apache Commons Imaging như được gọi ngay bây giờ) và nó yêu cầu hồ sơ màu CMYK hợp lý (tệp .icc). Bạn có thể lấy phần sau từ Adobe hoặc từ eci.org.

Vấn đề cơ bản là Java - ngoài hộp - chỉ có thể đọc tệp JPEG trong RGB. Nếu bạn có tệp CMYK, bạn cần phân biệt giữa CMYK thông thường, Adobe CMYK (với giá trị đảo ngược, nghĩa là 255 không có mực và 0 cho mực tối đa) và Adobe CYYK (một số biến thể có màu ngược).

public class JpegReader { 

    public static final int COLOR_TYPE_RGB = 1; 
    public static final int COLOR_TYPE_CMYK = 2; 
    public static final int COLOR_TYPE_YCCK = 3; 

    private int colorType = COLOR_TYPE_RGB; 
    private boolean hasAdobeMarker = false; 

    public BufferedImage readImage(File file) throws IOException, ImageReadException { 
     colorType = COLOR_TYPE_RGB; 
     hasAdobeMarker = false; 

     ImageInputStream stream = ImageIO.createImageInputStream(file); 
     Iterator<ImageReader> iter = ImageIO.getImageReaders(stream); 
     while (iter.hasNext()) { 
      ImageReader reader = iter.next(); 
      reader.setInput(stream); 

      BufferedImage image; 
      ICC_Profile profile = null; 
      try { 
       image = reader.read(0); 
      } catch (IIOException e) { 
       colorType = COLOR_TYPE_CMYK; 
       checkAdobeMarker(file); 
       profile = Sanselan.getICCProfile(file); 
       WritableRaster raster = (WritableRaster) reader.readRaster(0, null); 
       if (colorType == COLOR_TYPE_YCCK) 
        convertYcckToCmyk(raster); 
       if (hasAdobeMarker) 
        convertInvertedColors(raster); 
       image = convertCmykToRgb(raster, profile); 
      } 

      return image; 
     } 

     return null; 
    } 

    public void checkAdobeMarker(File file) throws IOException, ImageReadException { 
     JpegImageParser parser = new JpegImageParser(); 
     ByteSource byteSource = new ByteSourceFile(file); 
     @SuppressWarnings("rawtypes") 
     ArrayList segments = parser.readSegments(byteSource, new int[] { 0xffee }, true); 
     if (segments != null && segments.size() >= 1) { 
      UnknownSegment app14Segment = (UnknownSegment) segments.get(0); 
      byte[] data = app14Segment.bytes; 
      if (data.length >= 12 && data[0] == 'A' && data[1] == 'd' && data[2] == 'o' && data[3] == 'b' && data[4] == 'e') 
      { 
       hasAdobeMarker = true; 
       int transform = app14Segment.bytes[11] & 0xff; 
       if (transform == 2) 
        colorType = COLOR_TYPE_YCCK; 
      } 
     } 
    } 

    public static void convertYcckToCmyk(WritableRaster raster) { 
     int height = raster.getHeight(); 
     int width = raster.getWidth(); 
     int stride = width * 4; 
     int[] pixelRow = new int[stride]; 
     for (int h = 0; h < height; h++) { 
      raster.getPixels(0, h, width, 1, pixelRow); 

      for (int x = 0; x < stride; x += 4) { 
       int y = pixelRow[x]; 
       int cb = pixelRow[x + 1]; 
       int cr = pixelRow[x + 2]; 

       int c = (int) (y + 1.402 * cr - 178.956); 
       int m = (int) (y - 0.34414 * cb - 0.71414 * cr + 135.95984); 
       y = (int) (y + 1.772 * cb - 226.316); 

       if (c < 0) c = 0; else if (c > 255) c = 255; 
       if (m < 0) m = 0; else if (m > 255) m = 255; 
       if (y < 0) y = 0; else if (y > 255) y = 255; 

       pixelRow[x] = 255 - c; 
       pixelRow[x + 1] = 255 - m; 
       pixelRow[x + 2] = 255 - y; 
      } 

      raster.setPixels(0, h, width, 1, pixelRow); 
     } 
    } 

    public static void convertInvertedColors(WritableRaster raster) { 
     int height = raster.getHeight(); 
     int width = raster.getWidth(); 
     int stride = width * 4; 
     int[] pixelRow = new int[stride]; 
     for (int h = 0; h < height; h++) { 
      raster.getPixels(0, h, width, 1, pixelRow); 
      for (int x = 0; x < stride; x++) 
       pixelRow[x] = 255 - pixelRow[x]; 
      raster.setPixels(0, h, width, 1, pixelRow); 
     } 
    } 

    public static BufferedImage convertCmykToRgb(Raster cmykRaster, ICC_Profile cmykProfile) throws IOException { 
     if (cmykProfile == null) 
      cmykProfile = ICC_Profile.getInstance(JpegReader.class.getResourceAsStream("/ISOcoated_v2_300_eci.icc")); 
     ICC_ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile); 
     BufferedImage rgbImage = new BufferedImage(cmykRaster.getWidth(), cmykRaster.getHeight(), BufferedImage.TYPE_INT_RGB); 
     WritableRaster rgbRaster = rgbImage.getRaster(); 
     ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace(); 
     ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null); 
     cmykToRgb.filter(cmykRaster, rgbRaster); 
     return rgbImage; 
    } 
} 

Trước tiên, mã sẽ đọc tệp bằng phương pháp thông thường, hoạt động với tệp RGB. Nếu nó không thành công, nó sẽ đọc chi tiết của mô hình màu (profile, Adobe marker, Adobe variant). Sau đó, nó đọc dữ liệu điểm ảnh thô (raster) và thực hiện tất cả các chuyển đổi cần thiết (YCCK thành CMYK, ngược màu, CMYK thành RGB).

Tôi không hoàn toàn hài lòng với giải pháp của mình. Trong khi màu sắc chủ yếu là tốt, các khu vực tối hơi quá sáng, đặc biệt là màu đen không hoàn toàn đen. Nếu bất cứ ai biết những gì tôi có thể cải thiện, tôi rất vui khi nghe nó.

+0

Đây là câu trả lời hay nhất mà tôi đã tìm thấy, nhưng bạn có muốn đóng ImageInputStream trong một khối cuối cùng không? – Amalgovinus

+0

Cảm ơn đoạn mã, hoạt động tốt. (ISOcoated_v2_300_eci.icc có thể tìm thấy tại đây: http://www.humburg.de/?page=4) – user2198875

40

Cũ bưu điện, nhưng để tham khảo trong tương lai:

Lấy cảm hứng từ câu hỏi này và liên kết tìm thấy ở đây, tôi đã viết một plugin JPEGImageReader cho ImageIO hỗ trợ mô hình màu CMYK (cả với mô hình màu sắc ban đầu, hoặc ngầm chuyển đổi sang RGB khi đọc). Đầu đọc cũng thực hiện chuyển đổi màu phù hợp, sử dụng cấu hình ICC được nhúng trong luồng JPEG, trái ngược với các giải pháp khác được đề cập ở đây.

Java đơn giản và không yêu cầu JAI. Mã nguồn và bản phân phối nhị phân có sẵn miễn phí tại github.com/haraldk/TwelveMonkeys và được bao phủ bởi giấy phép kiểu BSD.

Một khi bạn có nó được cài đặt, nó cho phép bạn đọc hình ảnh JPEG CMYK sử dụng ImageIO.read(...) như thế này:

File cmykJPEGFile = new File(/*path*/); 
BufferedImage image = ImageIO.read(cmykJPEGFile); 

tức là .: Trong hầu hết trường hợp, nó không cần thiết phải sửa đổi mã của bạn.

+0

Cảm ơn! Tôi đã ước gì đó như thế này. Bạn có một readme/docs? :) hay tôi nên kiểm tra các bài kiểm tra? Cảm ơn bạn lần nữa –

+4

Rất tiếc, tài liệu đang thưa thớt tại thời điểm này. Tuy nhiên, đó là một plugin ImageIO, vì vậy nếu bạn chỉ muốn đọc một CMYK JPEG làm như sau: Xây dựng các JAR bằng cách sử dụng Maven, đặt trong classpath và ImageIO.read (cmykJPEGFile) chỉ nên làm việc. Hãy hỏi, nếu có bất cứ điều gì cụ thể bạn muốn làm. :-) – haraldK

+1

Plugin tuyệt vời, tuyệt vời mà bạn có. – medopal

0

tôi sửa lỗi này bằng cách này. chỉ cần thêm phụ thuộc này. tôi có thể đọc hình ảnh CMYK của ImageIO. TwelveMonkeys

ImageIO.read(new URL("http://img3.tianyancha.com/api/9b80a61183787909e719c77fd0f78103.png")) 
Các vấn đề liên quan