2012-02-18 62 views
31

我有一個讀取圖像,轉換它們(大小,格式)並將它們寫回的方法。這一直運行得很好,但現在我遇到了一些顯然包含一些元數據(IPTC)的JPEG圖像(來自新聞社)。轉換這些圖像時,顏色都是錯誤的。我的第一個猜測是,那些是CMYK圖像,但它們不是。錯誤顏色的JPEG圖像

問題必須來自閱讀,因爲無論我將圖像轉換爲較小的JPEG還是PNG,都無關緊要,它看起來總是相同的。

起初,我用ImageIO.read()來讀取圖像。我現在通過ImageIO.getImageReadersByMIMEType()得到實際的ImageReader,並試圖告訴讀者通過設置ignoreMetadata參數ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata)忽略元數據,但沒有成功。

然後我創建了一個沒有元數據的圖像版本(使用Fireworks)。該圖像被正確轉換。

我能找到,唯一的區別是,與不工作的形象價值讀者的變量colorSpaceCode是,whilest與工作形象,價值。還有一個outColorSpaceCode這兩個圖像是。

由於source comment of the reader只說由setImageData本地代碼回調設置。修改後的IJG + NIFTY顏色空間代碼現在我真的被卡住了。所以任何幫助將不勝感激。

您可以通過here並點擊下載獲取原始圖像(〜3 MB)。下面的左圖顯示了我從原始圖像中獲得的內容,右圖顯示了它應該是什麼樣子。

wrong colors correct colors (after removing metadata)

+0

我已經有這個問題,只要我記得。它發生在我遇到的大約0.1%的jpg文件中。例如:http://chan.sankakustatic.com/data/cd/81/cd81a9fa1305b9c1887ab1ac4904d166.jpg我還沒有找到一個解決方案,但在面板中正確顯示它們。我的猜測是這是Java的JPEG解析器中的一個錯誤。 – 2012-02-22 09:57:24

+0

可能的重疊:[用Java保存圖片顏色改變](http://stackoverflow.com/questions/20789043/image-changes-color-when-saved-with-java) – 2013-12-26 18:25:46

回答

8

我找到了一個解決方案,現在,這樣的作品,至少如果我得到的圖像也是一個JPEG: 首先我讀取圖像(從字節數組imageData),最重要的是,我還讀取了元數據。

InputStream is = new BufferedInputStream(new ByteArrayInputStream(imageData)); 
Image src = null; 
Iterator<ImageReader> it = ImageIO.getImageReadersByMIMEType("image/jpeg"); 
ImageReader reader = it.next(); 
ImageInputStream iis = ImageIO.createImageInputStream(is); 
reader.setInput(iis, false, false); 
src = reader.read(0); 
IIOMetadata imageMetadata = reader.getImageMetadata(0); 

現在,我會做一些轉換(即尺寸的縮小)......,最後我寫回結果爲JPEG圖像。這裏最重要的是將我們從原始圖像獲得的元數據傳遞給新的IIOImage。不幸的是,如果我寫一個PNG圖像,我仍然會得到錯誤的顏色(即使傳遞了元數據),但我可以忍受這一點。

0

似乎這裏罰款:

TestImage result

import java.awt.image.BufferedImage; 
import java.net.URL; 
import java.io.File; 
import javax.imageio.ImageIO; 

import javax.swing.*; 

class TestImage { 

    public static void main(String[] args) throws Exception { 
     URL url = new URL("http://i.stack.imgur.com/6vy74.jpg"); 
     BufferedImage origImg = ImageIO.read(url); 

     JOptionPane.showMessageDialog(null,new JLabel(new ImageIcon(origImg))); 

     File newFile = new File("new.png"); 
     ImageIO.write(origImg, "png", newFile); 
     BufferedImage newImg = ImageIO.read(newFile); 

     JOptionPane.showMessageDialog(null,new JLabel(
      "New", 
      new ImageIcon(newImg), 
      SwingConstants.LEFT)); 
    } 
} 
+0

那麼,圖片是我的結果轉換,正確的是我得到的,如果我刪除元數據。我應該發佈原始的非工作圖像,但它非常大(約3mb)。我會在某處上傳它並添加一個問題鏈接。儘管感謝你的代碼,我會用原始圖像來嘗試。 – Ridcully 2012-02-18 12:23:53

+0

*「但它非常大(約3mb)。」*請找到一個較小的休息時間。我無法承受使用需要3兆下載(一次或多次)代碼的實驗帶寬。 – 2012-02-18 12:37:16

+0

我嘗試將原始圖像上傳到imgur.com,但顯然他們刪除了元標籤,因爲在那裏上傳圖像時,圖像看起來沒問題。我現在已經把圖片上傳到google-docs,從那裏你可以下載原始圖片和你的代碼,我用錯誤的顏色獲取它: - /(必須改變代碼才能從本地文件讀取而不是URL )。 – Ridcully 2012-02-18 12:43:52

2

這裏有一個算法來將'壞'圖像轉換成好圖像,但是我還沒有找到任何方式來自動檢測圖像是否會被渲染得很糟糕,所以它仍然是無用的。

如果有人發現一種方法來檢測圖像是否會變壞(除眼球外),請告訴我們。 (比如,我在哪裏得到這個所謂的colorSpaceCode值?)

private static void fixBadJPEG(BufferedImage img) 
    { 
     int[] ary = new int[img.getWidth() * img.getHeight()]; 
     img.getRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth()); 
     for (int i = ary.length - 1; i >= 0; i--) 
     { 
      int y = ary[i] >> 16 & 0xFF; // Y 
      int b = (ary[i] >> 8 & 0xFF) - 128; // Pb 
      int r = (ary[i] & 0xFF) - 128; // Pr 

      int g = (y << 8) + -88 * b + -183 * r >> 8; // 
      b = (y << 8) + 454 * b >> 8; 
      r = (y << 8) + 359 * r >> 8; 

      if (r > 255) 
       r = 255; 
      else if (r < 0) r = 0; 
      if (g > 255) 
       g = 255; 
      else if (g < 0) g = 0; 
      if (b > 255) 
       b = 255; 
      else if (b < 0) b = 0; 

      ary[i] = 0xFF000000 | (r << 8 | g) << 8 | b; 
     } 
     img.setRGB(0, 0, img.getWidth(), img.getHeight(), ary, 0, img.getWidth()); 
    } 
+7

我相信你如何檢測不良JPEG的問題的答案可以在這裏找到[http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4712797)和[here](http:// stackoverflow.com/questions/7676701/java-jpeg-converter-for-odd-image-types)。你有什麼是沒有JFIF標記的JPEG。所有其他圖像加載器都假設在這種情況下數據是YCbCr,除了ImageIO,當通道1和2未被二次採樣時,ImageIO假定它是RGB。因此,檢查前4個字節是否爲FF D8 FF E1,如果是,則檢查通道1和2是否進行二次採樣。這是你需要轉換的情況。 – uckelman 2013-02-24 19:14:53

+0

您可以在[本答案](http://stackoverflow.com/questions/672916/how-to-get-image-height-and-width-using-java#2911772)中調整部分JPEG解碼器以檢測轉換條件。 – uckelman 2013-02-24 20:33:45

4

我也有類似的疑難問題不得不使用:

Image image = java.awt.Toolkit.getDefaultToolkit().getImage(path); 

代替

Image image = javax.imageio.ImageIO.read(new File(path)); 
5

我有類似的問題,BufferedImage返回基於的演繹,如果有透明像素,對於大多數png/gif類型的文件,該像素將設置爲true。但是,轉換爲JPEG時,此標誌應設置爲false。您可能需要編寫一個方法,在該方法中正確處理轉換。即:

public static BufferedImage toBufferedImage(Image image) { 
... 
} 

否則,「marunish」overtone成爲保存的結果。 :)


+13

如果沒有代碼顯示如何處理轉換,此答案是無用的。 – 2013-10-29 13:21:22

3

我遇到了這個問題,實際上我發現了一個第三方庫爲我處理這個問題。 https://github.com/haraldk/TwelveMonkeys

從字面上看,我所要做的就是將其包含在我的maven依賴項中,並且以怪異顏色出現的jpeg開始正常讀取。我甚至不必更改一行代碼。