1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.imaging.examples.tiff; 18 19 import java.awt.image.BufferedImage; 20 import java.io.File; 21 import java.io.IOException; 22 23 import javax.imageio.ImageIO; 24 25 import org.apache.commons.imaging.FormatCompliance; 26 import org.apache.commons.imaging.ImagingException; 27 import org.apache.commons.imaging.bytesource.ByteSource; 28 import org.apache.commons.imaging.formats.tiff.TiffContents; 29 import org.apache.commons.imaging.formats.tiff.TiffDirectory; 30 import org.apache.commons.imaging.formats.tiff.TiffImagingParameters; 31 import org.apache.commons.imaging.formats.tiff.TiffReader; 32 import org.apache.commons.imaging.formats.tiff.constants.TiffTagConstants; 33 import org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PhotometricInterpreterFloat; 34 35 /** 36 * A simple example application that reads the content of a TIFF file containing floating-point data and renders it using a gray-scale palette. TIFF files are 37 * sometimes used to store non-image information for scientific and geophysical data products, including terrestrial elevation and ocean depth data. 38 */ 39 public class ReadAndRenderFloatingPoint { 40 41 private static final String[] USAGE = { "Usage ReadAndRendserFloatingPoint <input file> [output file]", " input file: Mandatory file to be read", 42 " output file: Optional output path for file to be written.", " This name should not include a file extension.", 43 " Output data will be written using the JPEG format." }; 44 45 /** 46 * Reads the content of a TIFF file containing floating-point data and renders it using a gray-scale palette. 47 * 48 * @param args the command line arguments giving the path to an input TIFF file and an output JPEG. 49 * @throws org.apache.commons.imaging.ImagingException in the event of an internal data format or version compatibility error reading the image. 50 * @throws IOException in the event of an I/O error. 51 */ 52 public static void main(final String[] args) throws ImagingException, IOException { 53 if (args.length == 0) { 54 // Print usage and exit 55 for (final String s : USAGE) { 56 System.err.println(s); 57 } 58 System.exit(0); 59 } 60 61 final File target = new File(args[0]); 62 String outputPath = null; 63 if (args.length == 2) { 64 outputPath = args[1]; 65 } 66 final boolean optionalImageWritingEnabled = outputPath != null && !outputPath.isEmpty(); 67 68 final ByteSource byteSource = ByteSource.file(target); 69 70 // Establish a TiffReader. This is just a simple constructor that 71 // does not actually access the file. So the application cannot 72 // obtain the byteOrder, or other details, until the contents has 73 // been read. Then read the directories associated with the 74 // file by passing in the byte source and options. 75 final TiffReader tiffReader = new TiffReader(true); 76 77 // Read the directories in the TIFF file. Directories are the 78 // main data element of a TIFF file. They usually include an image 79 // element, but sometimes just carry metadata. This example 80 // reads all the directories in the file, but if we were interested 81 // in just the first element, Commons Imaging provides alternate API's 82 // that would be more efficient. 83 final TiffContents contents = tiffReader.readDirectories(byteSource, true, // indicates that application should read image data, if present 84 FormatCompliance.getDefault()); 85 86 // Render the first directory in the file. A practical implementation 87 // could use any of the directories in the file. This demo uses the 88 // first one just for simplicity. 89 final TiffDirectory directory = contents.directories.get(0); 90 // Render the first directory in the file 91 if (!directory.hasTiffImageData()) { 92 System.err.println("First directory in file does not have image"); 93 System.exit(-1); 94 } 95 96 // Obtain metadata about what kind of data is in the product. 97 // Because this demo is designed for floating-point data, 98 // it will expect the sample format to include only one element 99 // of type floating point. Beyond that, the TIFF specification 100 // allows a large number of variations in format (16, 24, or 32 byte 101 // floats; tiles versus strips; etc.). Unfortunately the test data 102 // for the less common variations was not available when Commons Imaging 103 // was implemented and so the API will not be able to handle all 104 // of them. This test will simply allow the API to throw an exception 105 // if an unsupported format is encountered. 106 // The getFieldValue call allows an application to provide a 107 // boolean indicating that the field must be present for processing 108 // to continue. If it does not, an exception is thrown. 109 final short[] sampleFormat = directory.getFieldValue(TiffTagConstants.TIFF_TAG_SAMPLE_FORMAT, true); 110 final short samplesPerPixel = directory.getFieldValue(TiffTagConstants.TIFF_TAG_SAMPLES_PER_PIXEL); 111 final short[] bitsPerPixel = directory.getFieldValue(TiffTagConstants.TIFF_TAG_BITS_PER_SAMPLE, true); 112 if (sampleFormat[0] != TiffTagConstants.SAMPLE_FORMAT_VALUE_IEEE_FLOATING_POINT) { 113 System.err.println("This example requires a data source with a floating-point format"); 114 System.exit(-1); 115 } 116 117 System.out.print("Bits per pixel: "); 118 for (int i = 0; i < samplesPerPixel; i++) { 119 System.out.format("%s%d", i > 0 ? ", " : "", bitsPerPixel[i]); 120 } 121 System.out.println(""); 122 123 // Create a TIFF Photometric Interpreter to perform a grayscale 124 // rendering. A Photometric Interpreter is a class that maps a 125 // floating-point value to a pixel (RGB) equivalent. 126 // Because there is no way to know the range of 127 // values in the TIFF before it is read. So the special-purpose 128 // interpreter permits an application to extract the 129 // min and maximum bounds of the data as an auxiliary function 130 // of interpreting the data. Unfortunately, if we wish for a 131 // good interpretation, we need to read the data twice. 132 // For this demo, we store the Photometric Interpreter instance 133 // as a option-parameter to be passed into the read-image method. 134 final PhotometricInterpreterFloat pi = new PhotometricInterpreterFloat(0.0f, 1.0f); 135 TiffImagingParameters params = new TiffImagingParameters(); 136 params.setCustomPhotometricInterpreter(pi); 137 BufferedImage bImage = directory.getTiffImage(params); 138 139 final float maxValue = pi.getMaxFound(); 140 final float minValue = pi.getMinFound(); 141 142 // System.out.format("Image size %dx%d%n", bImage.getWidth(), bImage.getHeight()); 143 // System.out.format("Range of values in TIFF: %f %f%n", minValue, maxValue); 144 145 if (optionalImageWritingEnabled) { 146 final File output = new File(outputPath); 147 // create a new photometric interpreter based on the range 148 // of values found above. 149 final PhotometricInterpreterFloat grayScale = new PhotometricInterpreterFloat(minValue, maxValue); 150 params = new TiffImagingParameters(); 151 params.setCustomPhotometricInterpreter(grayScale); 152 bImage = directory.getTiffImage(params); 153 ImageIO.write(bImage, "JPEG", output); 154 } 155 } 156 }