# JPEG, progressive, CMYK



## dadidom (14. Juni 2012)

Hallo Zusammen,

ich habe eine Webanwendung, in Thumbmails erzeuge. Hierbei habe ich bei einigen der hochgeladenen Bilder Probleme. Diese Bilder sind in meinem Bildbetracher als "JPEG, progressive, CMYK" markiert. Ich habe diverse Ansätze, die ich im Web gefunden habe ausprobiert, aber keine davon hat ein "sauberes" Bild erzeugt.

Jemand eine gute Idee, wie man JPEG, progressive, CMYK-Bilder in Java verkleinern/verarbeiten kann, ohne die Farbinformationen zu verlieren?

Danke schon mal
dadidom

PS: Ich habe mal ein Beispiel-Bild hinzugefügt, 
oder: http://dl.dropbox.com/u/39720523/image.zip


----------



## slowfly (18. Juni 2012)

Welche Ansätze hast du schon ausprobiert?

Gruss,
slowy


----------



## dadidom (25. Juni 2012)

Hi,

ich haben inzwischen, zumindest ein paar kleine Schritte vorwärts gemacht. Folgender Code erzeugt auf meiner Windows-Maschine eine farbliche korrekte, verkleiner Variante der Bilder...leider geht das Ganze innerhalb des *Servlet* nicht und erzeugt falsche Farben...

Ich verwende JAI um das Bild ein zu lesen, eine Verkleinerung durch zu führen und anschließend kommt eine manuelle Farb-Korrektur zum Einsatz die ich im Netz gefunden habe. Hier mal ein Beispiel Programm das ich auf die schnelle aus der Anwendung zusammen kopiert habe:


```
import com.sun.media.jai.codec.FileSeekableStream;
import com.sun.media.jai.codec.ImageCodec;
import com.sun.media.jai.codec.ImageDecoder;
import com.sun.media.jai.codec.SeekableStream;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.*;
import javax.imageio.ImageIO;
import javax.media.jai.NullOpImage;
import javax.media.jai.OpImage;

public class ImageResizer2 {

    public static void main(String[] args) throws Exception {
        File file = new File("inFile.jpg");
        FileInputStream fileInputStream = new FileInputStream(file);
        byte[] data = new byte[(int) file.length()];
        fileInputStream.read(data);
        fileInputStream.close();

        try {
            SeekableStream s = new FileSeekableStream(file);

            RenderedImage op = null;
            String codecName = "JPEG";
            try {

                ImageDecoder dec = ImageCodec.createImageDecoder(codecName, s, null);
                op = new NullOpImage(dec.decodeAsRenderedImage(0),
                        null,
                        OpImage.OP_IO_BOUND,
                        null);


            } catch (Exception io) {
                System.out.println("Error with: " + codecName);
            }


            BufferedImage bufferedImage = createJPEG4(op.getData(), 0);

            if (bufferedImage != null) {
                try {
                    int w = bufferedImage.getWidth();
                    int h = bufferedImage.getHeight();
                    BufferedImage dimg = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
                    Graphics2D g = dimg.createGraphics();
                    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
                    g.drawImage(bufferedImage, 0, 0, 100, 100, 0, 0, w, h, null);
                    g.dispose();

                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    ImageIO.write(dimg, "jpg", baos);
                    baos.flush();
                    data = baos.toByteArray();
                    baos.close();
                } catch (IOException ex) {
                    //can't write image - ignore 
                }
            }


            writeFile("SeekableStreamOutput.jpg", data);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    public static void writeFile(String name, byte[] data) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        File out = new File(name);
        FileOutputStream fOut = new FileOutputStream(out);
        fOut.write(data);
        fOut.close();
        baos.close();
    }

    /**
     * Java's ImageIO can't process 4-component images and Java2D can't apply
     * AffineTransformOp either, so convert raster data to RGB. Technique due to
     * MArk Stephens. Free for any use.
     */
    private static BufferedImage createJPEG4(Raster r, int xform) {
        int w = r.getWidth(), h = r.getHeight();
        byte[] rgb = new byte[w * h * 3];

        // if (Adobe_APP14 and transform==2) then YCCK else CMYK                                                                                    
        if (xform == 2) {    // YCCK -- Adobe                                                                                                         
            float[] Y = r.getSamples(0, 0, w, h, 0, (float[]) null), Cb =
                    r.getSamples(0, 0, w, h, 1, (float[]) null), Cr = r.getSamples(0, 0, w, h, 2, new float[0]);

            for (int i = 0, imax = Y.length, base = 0; i < imax; i++, base += 3) {
                float k = Y[i], y = Y[i], cb = Cb[i], cr = Cr[i];
                double val = y + 1.402 * (cr - 128) - k;
                rgb[base] = val < 0.0 ? (byte) 0 : val > 255.0 ? (byte) 0xff
                        : (byte) (val + 0.5);
                val = y - 0.34414 * (cb - 128) - 0.71414 * (cr - 128) - k;
                rgb[base + 1] = val < 0.0 ? (byte) 0 : val > 255.0 ? (byte) 0xff
                        : (byte) (val + 0.5);
                val = y + 1.772 * (cb - 128) - k;
                rgb[base + 2] = val < 0.0 ? (byte) 0 : val > 255.0 ? (byte) 0xff
                        : (byte) (val + 0.5);
            }

        } else {
            assert xform == 0 : xform;    // CMYK                                                                                                 
            int[] C = r.getSamples(0, 0, w, h, 0, (int[]) null), M =
                    r.getSamples(0, 0, w, h, 1, (int[]) null), Y = r.getSamples(0, 0, w, h, 2, (int[]) null);
            for (int i = 0, imax = C.length, base = 0; i < imax; i++, base += 3) {
                int k = C[i];
                rgb[base] = (byte) (255 - Math.min(255, C[i] + k));
                rgb[base + 1] = (byte) (255 - Math.min(255, M[i] + k));
                rgb[base + 2] = (byte) (255 - Math.min(255, Y[i] + k));
            }
        }

        r = Raster.createInterleavedRaster(new DataBufferByte(rgb, rgb.length), w, h, w * 3, 3, new int[]{0, 1, 2}, null);
        ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
        ColorModel cm = new ComponentColorModel(cs, false, true, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);


        return new BufferedImage(cm, (WritableRaster) r, true, null);
    }
}
```

Jemand eine gute Idee?

Viele Grüße
Dom


----------



## Thomas Darimont (25. Juni 2012)

Hallo,

schau mal hier:
http://stackoverflow.com/questions/3123574/how-to-convert-from-cmyk-to-rgb-in-java-correctly
https://groups.google.com/forum/?fromgroups#!topic/de.comp.lang.java/1K9x3FzPJ5E

Gruß Tom


----------



## dadidom (25. Juni 2012)

Hi,



Thomas Darimont hat gesagt.:


> http://stackoverflow.com/questions/3123574/how-to-convert-from-cmyk-to-rgb-in-java-correctly
> https://groups.google.com/forum/?fromgroups#!topic/de.comp.lang.java/1K9x3FzPJ5E



das war leider einer meiner anderen (erfolglosen) versuche. Da ich das ICS Profil nicht aus dem Bild laden konnte, habe ich einige default ICS Profile probiert, mit dem Ergebnis das ich eine Art "negativ" Bild bekommen habe (leider kein "echtes" Negativ). Ausprobiert hatte ich das Ganze mit 20-30 verschiedenen ICS Profilen (ein Packet hatte ich von einer Adobe Photoshop Seite und eins von "der offiziellen?" Seite).


----------



## Thomas Darimont (25. Juni 2012)

Hallo,


also ich hatte es nun auch mal mit den folgenden ICC Profilen ausprobiert:
Adobe CMYK .icc - Color Profiles
http://www.adobe.com/support/downloads/iccprofiles/iccprofiles_win.html

http://www.eci.org/doku.php?id=en:downloads
...
The download archive ECI Offset 2009 contains the following ICC profiles:
ISO Coated v2 (ECI)
...
http://www.eci.org/_media/downloads...che&DokuWiki=d2fb4bb2c41073a23d314dd6e3e9cd65


... leider hatte ich damit auch keinen Erfolg...

```
package de.tutorials;

import java.awt.color.ColorSpace;
import java.awt.color.ICC_ColorSpace;
import java.awt.color.ICC_Profile;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;

import javax.imageio.ImageIO;

import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageDecoder;

public class CYMKImageScaler {

	public static void main(String[] args) throws Exception {
		JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new FileInputStream("c:/temp/image.jpg"));
		final Raster srcRaster = decoder.decodeAsRaster();

		final ICC_Profile rgbIccProfile = ICC_Profile.getInstance(ColorSpace.CS_sRGB);

		new File("C:/TEMP/ECI_Offset_2009/"
				//"C:/TEMP/ECI_Offset_cont_2004/"
		).listFiles(new FileFilter() {
			public boolean accept(File pathname) {
				if (!pathname.getName().endsWith(".icc")) {
					return false;
				}
				try {
					ICC_Profile currentIccProfile = ICC_Profile.getInstance(pathname.getAbsolutePath());

					ColorSpace cs = new ICC_ColorSpace(currentIccProfile);

					BufferedImage result = new BufferedImage(srcRaster.getWidth(), srcRaster.getHeight(),BufferedImage.TYPE_INT_RGB);
					WritableRaster dstRaster = result.getRaster();

					ColorConvertOp cmykToRgb = new ColorConvertOp(new ICC_Profile[] { currentIccProfile,rgbIccProfile }, null);
					cmykToRgb.filter(srcRaster, dstRaster);

					ImageIO.write(result, "jpeg", new File("c:/temp/" + pathname.getName() + "-out.jpg"));
				} catch (IOException e) {
					e.printStackTrace();
				}

				return true;
			}
		});

	}

}
```

Gruß Tom


----------



## Thomas Darimont (25. Juni 2012)

Hallo,

kleiner Nachtrag - versuchs mal so:

Diesmal wird hier von dem Format YCCK nach CMYK Konvertiert anschließend wird das Ergebnis als RGB Image Interpretiert ... scheint zu funktionieren...


```
package de.tutorials;

import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FileInputStream;

import javax.imageio.ImageIO;

import com.sun.image.codec.jpeg.JPEGCodec;

public class CYMKImageScaler {

	public static void main(String[] args) throws Exception {

		final Raster srcRaster = JPEGCodec.createJPEGDecoder(
				new FileInputStream("c:/temp/image.jpg")).decodeAsRaster();

		BufferedImage dst = new BufferedImage(srcRaster.getWidth(),
				srcRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
		WritableRaster dstRaster = dst.getRaster();

		//Siehe: 
		//https://forums.oracle.com/forums/thread.jspa?messageID=10001648
		
			
		float[] srcPixel = new float[4];
		float[] destPixel = new float[3];
		for (int x = srcRaster.getMinX(); x < srcRaster.getWidth(); ++x)
			for (int y = srcRaster.getMinY(); y < srcRaster.getHeight(); ++y) {
				srcRaster.getPixel(x, y, srcPixel);

				float Y = srcPixel[0];
				float Cr = srcPixel[2];
				float Cb = srcPixel[1];
				float K = srcPixel[3];

				float c_ = 255 - (float) (Y + 1.402 * Cr - 179.456);
				float y_ = 255 - (float) (Y - 0.34414 * Cb - 0.71414 * Cr + 135.45984);
				float m_ = 255 - (float) (Y + 1.772 * Cb - 226.816);
				// float k_ = K;

				c_ = Math.min(255, Math.max(0, c_));
				m_ = Math.min(255, Math.max(0, m_));
				y_ = Math.min(255, Math.max(0, y_));

				destPixel[0] = c_;
				destPixel[1] = y_;
				destPixel[2] = m_;

				dstRaster.setPixel(x, y, destPixel);
			}

		BufferedImage scaled = new BufferedImage(100, 100,BufferedImage.TYPE_INT_RGB);
		scaled.createGraphics().drawImage(dst.getScaledInstance(100, 100, BufferedImage.SCALE_SMOOTH), 0, 0, 100, 100, null);

		ImageIO.write(scaled, "jpg", new File("c:/temp/foo-out.jpg"));
	}
}
```

Gruß Tom


----------



## dadidom (28. Juni 2012)

Du bist ein Gott  !
Funktioniert bisher zu 90%, bei einigen Bildern habe ich eine ganz leichte farbliche Verfälschung, die ich aber vertreten kann! SUPER!

Kleiner Schönheitsfehler dabei nur: com.sun.image.codec.jpeg.JPEGCodec; ist deprecated und das Programm lässt sich (zumindest unter NetBeans) mit einem 1.7erJDK auch nicht mehr compilieren.

Viele Grüße
Dominik


----------



## Thomas Darimont (28. Juni 2012)

Hallo,



> Kleiner Schönheitsfehler dabei nur: com.sun.image.codec.jpeg.JPEGCodec; ist deprecated


Ich weis, dass diese Klasse Deprecated ist, jedoch kann ImageIO keine CMYK Jpegs verarbeiten - ich gehe mal davon aus, da die Klasse noch lange leben wird ;-)



> und das Programm lässt sich (zumindest unter NetBeans) mit einem 1.7erJDK auch nicht mehr compilieren.


Ich habe das Beispiel mit Eclipse und JDK 1.7u_05 kompiliert / getestet. Ich denke die Fehler die du bekommst kommen von der Netbeans IDE. Afaik kann man in den Netbeans Java Compiler Optionen die Compiler Fehler / Warnings für Deprected / Restricted API (so ungefähr heißt es in Eclipse...) ausschalten.

Wenn du auf Nummer sicher gehen willst könntest du den Code in eine Klasse auslagern, die du dann in Abhängigkeit von der Existenz der Klasse JPEGCodec (etwa mit Test von getClass().getClassLoader().loadClass(....) auf CNFE prüfen) dynamisch (per Reflection) lädst.
Sollte die Klasse dann nicht verfügbar sein, kannst du einen dicken Error-Eintrag im Anwendungslog hinterlegen.

Gruß Tom


----------



## dadidom (28. Juni 2012)

Thomas Darimont hat gesagt.:


> Wenn du auf Nummer sicher gehen willst könntest du den Code in eine Klasse auslagern, die du dann in Abhängigkeit von der Existenz der Klasse JPEGCodec (etwa mit Test von getClass().getClassLoader().loadClass(....) auf CNFE prüfen) dynamisch (per Reflection) lädst.
> Sollte die Klasse dann nicht verfügbar sein, kannst du einen Dicken Error Eintrag im Anwendungslog hinterlegen.



Gute Idee, werde ich so machen. Noch mal danke, ich habe, weiß Gott wieviele Stunden, nach einer geeigneten Lösung gesucht 

Viele Grüße
Dom


----------

