View Javadoc
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 }