Java, jpeg, exception

Az egyik publikus képfeltöltő szervlet kapcsán jeleztek egy hibát, hogy valamiféle csodaképeket egyszerűen nem lehet feltölteni. A naplófájlok elemzése után látszott is a hiba:

java.lang.IllegalArgumentException: Numbers of source Raster bands and source color space components do not match
 at java.awt.image.ColorConvertOp.filter(ColorConvertOp.java:460)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.acceptPixels(JPEGImageReader.java:1114)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.readImage(Native Method)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.readInternal(JPEGImageReader.java:1082)
 at com.sun.imageio.plugins.jpeg.JPEGImageReader.read(JPEGImageReader.java:897)
 at javax.imageio.ImageIO.read(ImageIO.java:1422)
 at javax.imageio.ImageIO.read(ImageIO.java:1282)

A hiba viszonylag könnyen reprodukálható volt, tehát volt egy jó képem, és egy rossz. A gimp semmi extrát nem mondott a képről, és a rossz kép az ismételt elmentés után is rossz maradt.

Kis keresgélés után rátaláltam az exiftool nevű alkalmazásra:

exiftool -a -G ./JUT430-1.jpg

Látszott is, hogy a kép adatai között mindenféle furcsaság található:

[JFIF] JFIF Version : 1.01
 [JFIF] Resolution Unit : inches
 [JFIF] X Resolution : 300
 [JFIF] Y Resolution : 300
 [ICC_Profile] Profile CMM Type : HDM
 [ICC_Profile] Profile Version : 2.4.0
 [ICC_Profile] Profile Class : Output Device Profile
 [ICC_Profile] Color Space Data : CMYK
 [ICC_Profile] Profile Connection Space : Lab
 ...
[ICC_Profile] Profile Description : ISO Coated v2 (ECI)
 [ICC_Profile] Char Target : (Binary data 126677 bytes, use -b option to extract)
 [EXIF] Modify Date : 2012:08:21 09:56:35
 [EXIF] Compression : JPEG (old-style)
 [EXIF] Thumbnail Offset : 1829717
 [EXIF] Thumbnail Length : 5947

Ekkor gyanús lett a dolog, az én képeimen nem volt ennyi szemét. Letöröltem az összes exif adatot:

exiftool -all= ./JUT430-1.jpg

A dolog működött, a kép megnyitható java-ban. Innen már azt hittem egyszerű a dolog, már csak meg kellene oldani, hogy az exif adatokat a java is le tudja kapni feldolgozás előtt.

Erre találtam is egy ígéretes projektet az Apache common’s Sanselan-t. Az mondjuk furcsa volt, hogy még incubator-os, béta, és 3 éve senki nem nyúlt hozzá, de abból indultam ki, hogy biztosan azért mert annyira jó… 🙂

Természetesen a dolog nem működött. A forráskód tanulmányozása során feltűnt, hogy az általam használt kódból mintha hiányzott volna az érdemi rész…

&lt;/pre&gt;<br />
public void removeExifMetadata(ByteSource byteSource, OutputStream os)<br />
 throws ImageReadException, IOException, ImageWriteException<br />
 {<br />
 JFIFPieces jfifPieces = analyzeJFIF(byteSource);<br />
 List pieces = jfifPieces.pieces;</p>
<p>//Debug.debug(&quot;pieces&quot;, pieces);</p>
<p>//pieces.removeAll(jfifPieces.exifPieces);</p>
<p>//Debug.debug(&quot;pieces&quot;, pieces);</p>
<p>writeSegmentsReplacingExif(os, pieces, null);<br />
 }<br />
&lt;pre&gt;

Gondoltam ez így nem a legjobb, ezért visszacommenteztem (nyílt forráskód rulz) a removeAll-t. Sajnos ez még önmagában nem jelentett megoldást, ugyanis nem minden addicionális adat EXIF adat, és pont azok az adatok maradtak a képen, amik a problémát okozták.

Minden JFIFPieceSegment eltávolítása nem minősült kimondottan jó taktikának, de egy kis wikipedia-s utánaolvasás után megvolt, hogy mik az egyéb adatok marker bit-jei, amikre nekem semmi szükségem, innen már csak pár sor választott el a sikertől…

</p>
<p>public void removeExifAndAllMetadata(ByteSource byteSource, OutputStream os)<br />
 throws ImageReadException, IOException, ImageWriteException<br />
 {<br />
 JFIFPieces jfifPieces = analyzeJFIF(byteSource);<br />
 List pieces = jfifPieces.pieces;</p>
<p>//Debug.debug(&quot;pieces&quot;, pieces);</p>
<p> Iterator it = pieces.iterator();</p>
<p> while (it.hasNext()) {<br />
   Object object = (Object) it.next();<br />
   if (object instanceof JFIFPieceSegment ) {<br />
     JFIFPieceSegment iffdat=(JFIFPieceSegment)object;<br />
     if (iffdat.marker&gt;= 0xFFE0 &amp;&amp; iffdat.marker&lt;=0xFFEF) {<br />
        it.remove();<br />
     }<br />
   }<br />
 }</p>
<p> pieces.removeAll(jfifPieces.exifPieces);</p>
<p>//Debug.debug(&quot;pieces&quot;, pieces);</p>
<p>writeSegmentsReplacingExif(os, pieces, null);<br />
 }</p>
<p>

A végeredmény valami ilyesmi lett:

<br />
public static void main(String[] args) {<br />
 try {<br />
String fileName=&quot;c:/JUT430-1.jpg&quot;;<br />
File f=new File(fileName);<br />
ExifRewriterAdv eRewriter=new ExifRewriterAdv();<br />
FileInputStream fis=new FileInputStream(f);<br />
ByteSourceInputStream bis=new ByteSourceInputStream(fis, null);<br />
ByteArrayOutputStream bout=new ByteArrayOutputStream ();<br />
eRewriter.removeExifAndAllMetadata(bis, bout);<br />
ByteArrayInputStream iobis=new ByteArrayInputStream(bout.toByteArray());<br />
ImageIO.read(iobis);<br />
 } catch (Exception e) {<br />
e.printStackTrace();<br />
 }<br />
 }<br />

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.