Search code examples
javatiffimaging

Multistrip to single strip tiff using Commons Imaging


I am trying to convert a multi strip Tiff image to a single strip tiff. I was able to do so using JAI APIs from Java. But these are not platform independent. I am now working with Commons Imaging to perform this task. Here i am not able to find how exactly i can write back the single strip image data.

Here is the code snippet that i have written so far. (Please note this is just a crude example and i will modify the code prior getting single strip)

//get the metadata out a tiff file
final IImageMetadata metadata = Imaging.getMetadata(file);
iffImageMetadata tiffMetadata = null;

if(metadata instanceof TiffImageMetadata){
    tiffMetadata = (TiffImageMetadata) metadata;
    TiffImageMetadata.Directory dir = (TiffImageMetadata.Directory)tiffMetadata.getDirectories().get(0);

TiffImageData imgData = dir.getTiffImageData();
long offset = 0;

//check number of strips(can be done by getting TIFF TAG 273
DataElement[] imgDataElements = imgData.getImageData();
int noOfSTrips = imgDataElements.length;

if(noOfSTrips == 1){
    return; //already a single strip
}

//merge all single strips to a single byte array 
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );

for(int i = 0; i< noOfSTrips; i++){
    ByteSourceData data = (ByteSourceData) imgDataElements[i];
    outputStream.write(data.getData());
}

List<TiffField> tiffFields = tiffMetadata.getAllFields();
TiffOutputField outputField = null;

for(TiffField tiffField : tiffFields){
    String type = tiffField.getFieldTypeName();
    switch(tiffField.getTag())  {
    case 273:
        if (type == FieldType.LONG.getName())
        {
            if (tiffField.getCount() > 1){
                //this will be writen back to the output set
                outputField = new TiffOutputField(tiffField.getTagInfo(), tiffField.getFieldType(), 1, outputStream.toByteArray());
                offset = tiffField.getByteArrayValue()[0];
            }
        }
        break;
    }
}

TiffOutputSet outputSet = tiffMetadata.getOutputSet();
List<TiffOutputDirectory> outputDirectories = outputSet.getDirectories();
TiffOutputDirectory outputDir = outputDirectories.get(0);
outputDir.removeField(273);

outputDir.add(outputField);

//NOW WHAT TO DO??
//I HAVE SEEN ExifRewritter CLASS THAT CAN UPDATE TIFF WITH EXIF, BUT HOW I CAN UPDATE THIS BACK

}

Any pointers will be helpful.


Solution

  • So i was finally able to convert a Multistrip tif image to single strip. Basically i had copied some code from TiffImageWriterBase class of Commons Imaging, thats due to the reason that rowsPerStrip was being calculated in such a way that the resulting image was always a Multistrip. . I had modified this code to set the rowsPerStrip = image.height. Here is the code that i have written:

    //source is any multistrip image
    File file = new File("D:\\Tiff\\image.tif");
    BufferedImage src = Imaging.getBufferedImage(file);
    OutputStream os = new FileOutputStream(new File("D:\\Tiff\\modified_image.tif"));
    os = new BufferedOutputStream(os);
    final int width = src.getWidth();
    final int height = src.getHeight();
    //These are set as per CCITT 4 compression, you can modify these as per requirement
    int samplesPerPixel = 1;
    int bitsPerSample = 1;
    int photometricInterpretation =0;
    //setting rowsPerStrip equal to heigh of the image, this did the trick for me
    int rowsPerStrip = height;//This code was present originally -> stripSizeInBits / (1 * bitsPerSample * samplesPerPixel);
    rowsPerStrip = Math.max(1, rowsPerStrip); // must have at least one.
    //you can copy getStrips method from TiffImageWriterBase class or work upon yours own
    final byte[][] strips = getStrips(src, samplesPerPixel, bitsPerSample, rowsPerStrip);
    for (int i = 0; i < strips.length; i++) {
        strips[i] = T4AndT6Compression.compressT6(strips[i], width,
                strips[i].length / ((width + 7) / 8));
    }
    final TiffElement.DataElement[] imageData = new TiffElement.DataElement[strips.length];
    for (int i = 0; i < strips.length; i++) {
        imageData[i] = new TiffImageData.Data(0, strips[i].length, strips[i]);
    }
    final TiffOutputSet outputSet = new TiffOutputSet(ByteOrder.LITTLE_ENDIAN);
    final TiffOutputDirectory directory = outputSet.addRootDirectory();
    directory.add(TiffTagConstants.TIFF_TAG_IMAGE_WIDTH, width);
    directory.add(TiffTagConstants.TIFF_TAG_IMAGE_LENGTH, height);
    directory.add(TiffTagConstants.TIFF_TAG_PHOTOMETRIC_INTERPRETATION,
            (short) photometricInterpretation);
    directory.add(TiffTagConstants.TIFF_TAG_COMPRESSION,
            (short) TiffConstants.TIFF_COMPRESSION_CCITT_GROUP_4);
    directory.add(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL,
            (short) samplesPerPixel);
    directory.add(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE,
            (short) bitsPerSample);
    directory.add(TiffTagConstants.TIFF_TAG_ROWS_PER_STRIP,
            rowsPerStrip);
    directory.add(TiffTagConstants.TIFF_TAG_RESOLUTION_UNIT,
            (short) 2);
    PixelDensity pixelDensity = PixelDensity.createFromPixelsPerInch(72, 72);
    directory.add(TiffTagConstants.TIFF_TAG_XRESOLUTION,
            RationalNumber.valueOf(pixelDensity.horizontalDensityInches()));
    directory.add(TiffTagConstants.TIFF_TAG_YRESOLUTION,
            RationalNumber.valueOf(pixelDensity.verticalDensityInches()));
    final TiffImageData tiffImageData = new TiffImageData.Strips(imageData,
            rowsPerStrip);
    directory.setTiffImageData(tiffImageData);
    //single strip image will be written to output stream
    new TiffImageWriterLossy().write(os, outputSet);