[Java] JPEG's comprimeren met ImageWriteParam

Pagina: 1
Acties:

  • Johnny
  • Registratie: December 2001
  • Laatst online: 24-04 11:10

Johnny

ondergewaardeerde internetguru

Topicstarter
Ik probeer afbeeldingen op te slaan in het JPEG formaat. De standaard manier voor met ImageIO.write() werkt uitstekend, maar je kan er geen compressie mee instellen.

Na lang zoeken en van alles proberen lukt het me maar niet om die compressie werkend te krijgen.

In deze laatste poging worden afbeeldingen wel lelijker, met allerlei JPEG artifacts, maar de bestanden worden niet kleiner (ongeveer 250 KB voor een plaatje van 400x400 pixels).

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private void saveImage(BufferedImage image, File imageFile, float imageQuality, String imageFormat) {

try {
    
    // Find a writer
    ImageWriter writer = null;
    Iterator iter = ImageIO.getImageWritersByFormatName(imageFormat);
    if(iter.hasNext()) {
    writer = (ImageWriter)iter.next();
    }
    
    // Prepare output file
    ImageOutputStream ios = ImageIO.createImageOutputStream(imageFile);
    writer.setOutput(ios);
    
    System.out.println("Saving image with quality " + imageQuality);
    writeParam.setCompressionQuality(imageQuality);
    
    // Write the image
    writer.write(null, new IIOImage(image, null, null), writeParam);
    
    // Cleanup
    ios.flush();
    writer.dispose();
    ios.close();
    
} catch (IOException e) {
}

}

Aan de inhoud van de bovenstaande tekst kunnen geen rechten worden ontleend, tenzij dit expliciet in dit bericht is verwoord.


  • LAN
  • Registratie: Oktober 2000
  • Niet online

LAN

Misschien kun je dan wat met JPEGImageEncoder.

Java:
1
2
3
4
5
6
7
8
9
10
BufferedImage jpegBuf; // your BufferedImage
ByteArrayOutputStream baos = new ByteArrayOutputStream(); // create outputstream
JPEGImageEncoder jpegOut = JPEGCodec.createJPEGEncoder(baos); // create jpeg encoder
JPEGEncodeParam jpegEnc = jpegOut.getDefaultJPEGEncodeParam(jpegBuf); // get default encoding parameters
jpegEnc.setQuality(0.75f,true); // set quality to 75%
jpegOut.setJPEGEncodeParam(jpegEnc); // set encoding parameters
jpegOut.encode(jpegBuf); // encode image

OutputStream os = new FileOutputStream("bestand.jpg");
os.write(baos.toByteArray());

[ Voor 10% gewijzigd door LAN op 17-03-2005 19:54 ]


  • Johnny
  • Registratie: December 2001
  • Laatst online: 24-04 11:10

Johnny

ondergewaardeerde internetguru

Topicstarter
Daar ben ik ook al mee bezig geweest, maar daar zitten ook weer allerlei haken en ogen aan, zo is het opeens een stuk moeilijker om meerdere formaten te ondersteunen omdat ik dan voor ieder formaat aparte methode moet maken, terwijl ik juist ImageIO gebruik zodat dit niet meer hoeft. Nog belangrijker is dat het niet meer werkt onder Linux zoals hier te lezen is, en waar vervolgens weer wordt aangeraden om ImageIO te gebruiken... 8)7

Daarnaast vertrouw ik die JPEG encoder niet, ik heb er al eens eerder mee gewerkt, en toen bleek er (na dagen profile) dat het geheugenlek waar mijn programma last van had daar in zat.

Aan de inhoud van de bovenstaande tekst kunnen geen rechten worden ontleend, tenzij dit expliciet in dit bericht is verwoord.


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Hum ik heb het even aan de Internet God gevraagd en het lijkt een bekend probleem in J2SE: zie hier voor een oplossing.

PS: De oplossing lijkt trouwens veel op de door jouw geposte code, alleen de workaround voor de Java bug heb je natuurlijk vergeten over te nemen. ;)

PS2: De kwaliteit van de voorbeeldcode is natuurlijk verschrikkelijk dus helemaal letterlijk hoef je het ook niet over te nemen. ;)

  • Johnny
  • Registratie: December 2001
  • Laatst online: 24-04 11:10

Johnny

ondergewaardeerde internetguru

Topicstarter
misfire schreef op donderdag 17 maart 2005 @ 20:21:
Hum ik heb het even aan de Internet God gevraagd en het lijkt een bekend probleem in J2SE: zie hier voor een oplossing.

PS: De oplossing lijkt trouwens veel op de door jouw geposte code, alleen de workaround voor de Java bug heb je natuurlijk vergeten over te nemen. ;)

PS2: De kwaliteit van de voorbeeldcode is natuurlijk verschrikkelijk dus helemaal letterlijk hoef je het ook niet over te nemen. ;)
Ik heb dat stuk al eerder geprobeerd, en heb het nu weer opnieuw gedaan, maar het heeft geen effect, het probleem blijft bestaan.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    
    private void saveImage(BufferedImage image, File imageFile,  float imageQuality, String imageFormat) {
    
    try {
        
        // Find a writer
        ImageWriter writer = null;
        Iterator iter = ImageIO.getImageWritersByFormatName(imageFormat);
        if(iter.hasNext()) {
        writer = (ImageWriter)iter.next();
        }
        
        // Prepare output file
        ImageOutputStream ios = ImageIO.createImageOutputStream(imageFile);
        writer.setOutput(ios);
        
        System.out.println("Saving image with quality " + imageQuality);
        
        // Set the compression quality
        ImageWriteParam writeParam  = new MyImageWriteParam();
        //writeParam = writer.getDefaultWriteParam();
        writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        writeParam.setCompressionQuality(imageQuality);

        // Write the image
        writer.write(null, new IIOImage(image, null, null), writeParam);
        
        // Cleanup
        ios.flush();
        writer.dispose();
        ios.close();
        
    } catch (IOException e) {
    }
    
    }


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import javax.imageio.plugins.jpeg.JPEGImageWriteParam;
import java.util.Locale;

// This class overrides the setCompressionQuality() method to workaround
// a problem in compressing JPEG images using the javax.imageio package.
public class MyImageWriteParam extends JPEGImageWriteParam {
    
    public MyImageWriteParam() {
    
    super(Locale.getDefault());
    }
    
    // This method accepts quality levels between 0 (lowest) and 1 (highest) and simply converts
    // it to a range between 0 and 256; this is not a correct conversion algorithm.
    // However, a proper alternative is a lot more complicated.
    // This should do until the bug is fixed.
    public void setCompressionQuality(float quality) {
    
    if(quality < 0.0f || quality > 1.0f) {
        
        throw new IllegalArgumentException("Quality out-of-bounds!");
    }
    
    this.compressionQuality = 256 - (quality * 256);
    }
}


Ik heb net nog even getest of ik me het niet inbeeld, maar een afbeeldingen die ik op deze manie rop sla met een kwaliteit van 0.4f is, net als iedere andere hoeveelheid meer dan 200 KB, en in een ander programma maar 26 KB :(

Aan de inhoud van de bovenstaande tekst kunnen geen rechten worden ontleend, tenzij dit expliciet in dit bericht is verwoord.