Ravi Ranjan Ravi Ranjan - 6 days ago 4
Java Question

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.

Answer

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);