2012-02-18 11 views
31

Mam metodę, która odczytuje obrazy, konwertuje je (rozmiar, format) i zapisuje je z powrotem. To zawsze działało bardzo dobrze, ale teraz natknąłem się na obrazy JPEG (z agencji prasowej), które oczywiście zawierają pewne metadane (IPTC). Podczas konwersji tych obrazów kolory są nieprawidłowe. Moim pierwszym przypuszczeniem było to, że są to obrazy CMYK, ale nie są.Obraz JPEG z niewłaściwymi kolorami

Problem musi pochodzić z czytania, ponieważ nie ma znaczenia, czy obraz zostanie przekonwertowany na mniejszy plik JPEG czy plik PNG, zawsze wygląda tak samo.

Najpierw użyłem ImageIO.read() do odczytania obrazu. Dostaję teraz ImageReader przez ImageIO.getImageReadersByMIMEType() i próbuję powiedzieć czytelnikowi, aby zignorować dane meta poprzez ustawienie parametru ignoreMetadata z ImageReader#setInput(Object input, boolean seekForwardOnly, boolean ignoreMetadata), ale nie powiodło się.

Następnie stworzyłem wersję obrazu bez metadanych (przy użyciu programu Fireworks). Ten obraz jest przekonwertowany poprawnie.

Jedyną różnicą mogłem dowiedzieć się, jest to, że z obrazem nie-roboczej wartość czytelnika zmienna colorSpaceCode jest , whilest z obrazem pracy, wartość jest . Jest również outColorSpaceCode, który jest dla obu obrazów.

Jako source comment of the reader tylko mówi Ustaw przez wywołanie zwrotne kodu natywnego setImageData. Zmodyfikowany kod przestrzeni kolorów IJG + NIFTY Naprawdę utknąłem. Więc każda pomoc będzie bardzo ceniona.

Możesz uzyskać oryginalny obraz (~ 3 MB), przechodząc here i klikając przycisk Pobierz. Lewy obrazek poniżej pokazuje, co dostaję z oryginalnego obrazu, po prawej pokazuje, jak powinien wyglądać.

wrong colors correct colors (after removing metadata)

+0

Mam ten problem tak długo, jak pamiętam. Zdarza się to w około 0,1% plików jpg, które napotykam. Na przykład: http://chan.sankakustatic.com/data/cd/81/cd81a9fa1305b9c1887ab1ac4904d166.jpg Nie znalazłem jeszcze rozwiązania, które poprawnie wyświetliłoby je w panelu. Zgaduję, że to błąd w analizatorze Javy w Javie. –

+0

Możliwe nakładanie się: [Obraz zmienia kolor po zapisaniu w Javie] (http://stackoverflow.com/questions/20789043/image-changes-color-when-saved-with-java) –

Odpowiedz

8

znalazłem rozwiązanie teraz, że działa, przynajmniej jeśli obraz wynikowy jest także JPEG: Najpierw odczytaj obraz (z tablicy bajtów imageData), a co najważniejsze, czytam również metadane.

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

Teraz zrobiłbym jakiś konwersji (czyli kurczyć rozmiary) ... i wreszcie będę pisać wynik z powrotem jako obraz JPEG. Tutaj najważniejsze jest przekazanie metadanych, które otrzymaliśmy z oryginalnego obrazu, do nowego IIOImage.

Iterator<ImageWriter> iter = ImageIO.getImageWritersByMIMEType("image/jpeg"); 
ImageWriter writer = iter.next(); 
ImageWriteParam iwp = writer.getDefaultWriteParam(); 
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); 
iwp.setCompressionQuality(jpegQuality); 
ImageOutputStream imgOut = new MemoryCacheImageOutputStream(out); 
writer.setOutput(imgOut); 
IIOImage image = new IIOImage(destImage, null, imageMetadata); 
writer.write(null, image, iwp); 
writer.dispose(); 

Niestety, gdyby napisać PNG, ja wciąż niewłaściwych kolorów (nawet jeśli przekazując metadane), ale mogę z tym żyć.

0

wydaje się dobrze tutaj:

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

Cóż, zdjęcia są wynikami mojego Konwersje, ta właściwa jest tą, którą otrzymam po usunięciu metadanych. Powinienem opublikować oryginalny obraz niepracujący, ale jest bardzo duży (około 3mb). Prześlę go gdzieś i doda link do pytania. Dzięki za Twój kod, spróbuję go z oryginalnym obrazem. – Ridcully

+0

* "ale jest bardzo duży (około 3mb)." * Znajdź mniejszy, który się psuje. Nie stać mnie na pasmo eksperymentowania z kodem, który wymaga pobrania 3 megabajty (raz lub wielokrotnie). –

+0

Próbowałem przesłać oryginalny obraz do serwisu imgur.com, ale oczywiście usunięto metatagi, ponieważ z przesłanym obrazem obraz wygląda dobrze. Przesłałem już obrazek do google-docs, skąd w rzeczywistości można pobrać oryginalny obraz i za pomocą kodu, otrzymuję go ze złymi kolorami: -/(musiał zmienić kod na odczyt z lokalnego pliku zamiast adresu URL). – Ridcully

2

Oto algorytm przekształcania "złego" obrazu w dobry, jednak nie znalazłem żadnego sposobu automatycznego wykrycia, czy obraz będzie źle wyświetlany, więc nadal jest bezużyteczny.

Jeśli ktoś znajdzie sposób na sprawdzenie, czy obraz jest zły (poza gałkami ocznymi), prosimy o informację. (jak, gdzie mogę uzyskać tę tak zwaną wartość 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

Wierzę, że odpowiedź na pytanie o wykrywanie złych plików JPEG znajduje się [tutaj] (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4712797) i [tutaj] (http: // stackoverflow.com/questions/7676701/java-jpeg-converter-for-odd-image-types). To, co masz, to plik JPEG bez znacznika JFIF. Wszystkie inne programy ładujące obrazy przyjmują, że dane to YCbCr w tym przypadku, z wyjątkiem ImageIO, który zakłada, że ​​jest to RGB, gdy kanały 1 i 2 nie są podpróbkowane. Sprawdź więc, czy pierwsze 4 bajty to FF D8 FF E1, a jeśli tak, to czy kanały 1 i 2 są podpróbkowane. Tak jest w przypadku konwersji. – uckelman

+0

Możesz przystosować częściowy dekoder JPEG w [tej odpowiedzi] (http://stackoverflow.com/questions/672916/how-to-get-image-height-and-width-using-java#2911772), aby wykryć konwersję stan. – uckelman

4

miałem podobny problem.I musiał użyć:

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

zamiast

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

miałem podobne problemy, BufferedImage zwracany jest interpretacja opiera jeśli istnieje przezroczysty piksel, który zostanie ustawiony na true dla większości plików png/gif. Ale podczas konwersji do jpeg ta flaga powinna być ustawiona na false. Możesz napisać metodę, w której konwersja jest poprawnie obsługiwana. tj .:

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

W przeciwnym razie, że "marunish" wydźwięk zostaje zapisany wynik. :)


+13

Ta odpowiedź jest bezużyteczna bez kodu pokazującego sposób obsługi konwersji. –

3

Wpadłem na ten problem, a ja znalazłem bibliotekę osób trzecich, która sobie z tym poradziła. https://github.com/haraldk/TwelveMonkeys

Dosłownie wszystko, co musiałem zrobić, to uwzględnić to w moich uzależnieniach od mavenów, a jpegy, które pojawiały się w dziwnych kolorach, zaczęły być normalnie czytane. Nie musiałem nawet zmieniać linii kodu.