J o
J o

Reputation: 3

T6 Compressed Tiff image

I am trying to read an image file in JPG format, write a text into the image, and save it into a file in a single strip, compressed TIFF image format. I used Apache Commons library to do the compression and the writing of the output image file. I have no idea why the output image is drawn like it was split in two parts and the second part drawn first. If anyone can help me resolve this or point out what am I doing wrong here. Thanks..

This is the sample input and output image from the code below...

sample image

FileOutputStream      fos  = null;
BufferedOutputStream  os   = null;
ByteArrayOutputStream baos = null;

try {

    String          inputPath    = "D:\\Test\\input.jpg";
    File            inputFile    = new File(inputPath);

    BufferedImage   inputImage   = ImageIO.read(inputFile);

    int             width        = inputImage.getWidth();
    int             height       = inputImage.getHeight();

    BufferedImage   outputImage  = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);

    Font font = new Font("Arial", Font.BOLD, 40 );
    java.awt.Graphics2D g2 = outputImage.createGraphics();
    g2.setFont( font );
    g2.setColor ( Color.BLACK );
    g2.drawImage ( inputImage, 0, 0, width, height, Color.WHITE, null);
    g2.drawString("TEST TEXT", 20, 40);
    g2.dispose();

    PixelDensity        resolution   = PixelDensity.createFromPixelsPerInch ( 200, 200);
    double              resolutionX  = resolution.horizontalDensityInches();
    double              resolutionY  = resolution.verticalDensityInches();
    RationalNumber      rationalNumX = RationalNumber.valueOf ( resolutionX );
    RationalNumber      rationalNumY = RationalNumber.valueOf ( resolutionY );

    TiffOutputSet       outputSet    = new TiffOutputSet(ByteOrder.LITTLE_ENDIAN);
    TiffOutputDirectory directory    = outputSet.addRootDirectory();

    baos = new ByteArrayOutputStream(); 
    ImageIO.write(outputImage, "tif", baos); 
    byte[] bytes = baos.toByteArray();

    byte[] compressedBytes = T4AndT6Compression.compressT6(bytes, width, height);

    TiffImageData.Data data = new TiffImageData.Data(0, compressedBytes.length, compressedBytes);

    TiffElement.DataElement[] dataElements = new TiffElement.DataElement[]{ data };

    TiffImageData tiffImageData = new TiffImageData.Strips(dataElements, height);

    directory.add ( TiffTagConstants.TIFF_TAG_NEW_SUBFILE_TYPE,                   1    );
    directory.add ( TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION, (short) 1    );
    directory.add ( TiffTagConstants.TIFF_TAG_COMPRESSION,                (short) 4    );
    directory.add ( TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE,            (short) 1    );
    directory.add ( TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT,            (short) 2    );
    directory.add ( TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP,             height       );
    directory.add ( TiffTagConstants.TIFF_TAG_IMAGE_WIDTH,                width        );
    directory.add ( TiffTagConstants.TIFF_TAG_IMAGE_LENGTH,               height       );
    directory.add ( TiffTagConstants.TIFF_TAG_XRESOLUTION,                rationalNumX );
    directory.add ( TiffTagConstants.TIFF_TAG_YRESOLUTION,                rationalNumY );

    directory.setTiffImageData( tiffImageData );

    String outputPath = "D:\\Test\\output.tif";
    File outputFile = new File(outputPath);

    fos = new FileOutputStream(outputFile);
    os   = new BufferedOutputStream(fos);

    new TiffImageWriterLossy().write( os, outputSet );


} catch (IOException ex) {

    Logger.getLogger(JavaApplication7.class.getName()).log(Level.SEVERE, null, ex);

}   catch (ImageWriteException ex) {

    Logger.getLogger(JavaApplication7.class.getName()).log(Level.SEVERE, null, ex);

} finally {

    if ( baos != null ) {
        try { 
            baos.flush();   
            baos.close();   
        } catch ( IOException ex ) { }
    }
    if ( os != null ) {
        try { 
            os.flush();   
            os.close();   
        } catch ( IOException ex ) { }
    }
    if ( fos != null ) {
        try { 
            fos.flush();   
            fos.close();   
        } catch ( IOException ex ) { }
    }

}

Upvotes: 0

Views: 2348

Answers (1)

Harald K
Harald K

Reputation: 27064

The problem with your code is that you first store the BufferedImage as a TIFF file using ImageIO, then compress the entire file along with headers as the pixel data of the image you pass to commons imaging:

baos = new ByteArrayOutputStream(); 
ImageIO.write(outputImage, "tif", baos); 
byte[] bytes = baos.toByteArray(); // <-- This is not pixel data, but a complete TIFF file

byte[] compressedBytes = T4AndT6Compression.compressT6(bytes, width, height);

The reason this actually resembles the image you expect, is that ImageIO writes the image data uncompressed. The garbage lines and offset before your image, is the TIFF header and tags displayed as pixels...

Instead, you probably meant to do something like:

// Cast is safe here, as you know outputImage is TYPE_BYTE_BINARY
byte[] bytes = ((DataBufferByte) outputImage.getRaster().getDataBuffer()).getData();
byte[] compressedBytes = T4AndT6Compression.compressT6(bytes, width, height);

This will compress just the pixels, and your image should be fine.


You could also do everything using just ImageIO, and avoid the extra dependency on commons imaging, by doing something like this:

try (ImageOutputStream stream = ImageIO.createImageOutputStream(outputFile)) {
    ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromRenderedImage(outputImage);
    ImageWriter writer = ImageIO.getImageWriters(imageTypeSpecifier, "TIFF").next();
    writer.setOutput(stream);

    ImageWriteParam param = writer.getDefaultWriteParam();
    param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
    param.setCompressionType("CCITT T.6");

    IIOMetadata metadata = writer.getDefaultImageMetadata(imageTypeSpecifier, param);
    // TODO: Set 200 DPI, default is likely 72, and perhaps subfile type if needed, 
    // other tags will be set correctly for you

    writer.write(null, new IIOImage(outputImage, null, metadata), param);
}

Upvotes: 3

Related Questions