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.Color;
20  import java.awt.image.BufferedImage;
21  import java.io.File;
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import javax.imageio.ImageIO;
27  
28  import org.apache.commons.imaging.FormatCompliance;
29  import org.apache.commons.imaging.ImagingException;
30  import org.apache.commons.imaging.bytesource.ByteSource;
31  import org.apache.commons.imaging.common.ImageBuilder;
32  import org.apache.commons.imaging.formats.tiff.TiffContents;
33  import org.apache.commons.imaging.formats.tiff.TiffDirectory;
34  import org.apache.commons.imaging.formats.tiff.TiffImagingParameters;
35  import org.apache.commons.imaging.formats.tiff.TiffRasterData;
36  import org.apache.commons.imaging.formats.tiff.TiffRasterStatistics;
37  import org.apache.commons.imaging.formats.tiff.TiffReader;
38  import org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PaletteEntry;
39  import org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PaletteEntryForRange;
40  import org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PaletteEntryForValue;
41  import org.apache.commons.imaging.formats.tiff.photometricinterpreters.floatingpoint.PhotometricInterpreterFloat;
42  
43  /**
44   * A simple example application that reads the content of a TIFF file containing floating-point data and extracts its content. TIFF files are * sometimes used
45   * to store non-image information for scientific and geophysical data products, including terrestrial elevation and ocean depth data.
46   */
47  public class ExampleReadFloatingPointData {
48  
49      private static final String[] USAGE = { "Usage ReadFloatingPointData <input file> [output file]", "   input file:  Mandatory file to be read",
50              "   output file: Optional output path for file to be written.", "                This name should not include a file extension.",
51              "                Output data will be written using the JPEG format." };
52  
53      /**
54       * Reads the content of a TIFF file containing floating-point data and obtains some simple statistics
55       *
56       * @param args the command line arguments giving the path to an input TIFF file
57       * @throws org.apache.commons.imaging.ImagingException in the event of an internal data format or version compatibility error reading the image.
58       * @throws IOException                                 in the event of an I/O error.
59       */
60      public static void main(final String[] args) throws ImagingException, IOException {
61          if (args.length == 0) {
62              // Print usage and exit
63              for (final String s : USAGE) {
64                  System.err.println(s);
65              }
66              System.exit(0);
67          }
68  
69          final File target = new File(args[0]);
70          String outputPath = null;
71          if (args.length >= 2) {
72              outputPath = args[1];
73          }
74          final boolean optionalImageWritingEnabled = outputPath != null && !outputPath.isEmpty();
75  
76          final ByteSource byteSource = ByteSource.file(target);
77  
78          // Establish a TiffReader. This is just a simple constructor that
79          // does not actually access the file until one of its methods such as
80          // readDirectories is called.
81          final TiffReader tiffReader = new TiffReader(true);
82  
83          // Read the directories in the TIFF file. Directories are the
84          // main data element of a TIFF file. They usually include an image
85          // element, but sometimes just carry metadata. This example
86          // reads all the directories in the file. Typically reading
87          // the directories is not a time-consuming operation.
88          final TiffContents contents = tiffReader.readDirectories(byteSource, true, // indicates that application should read image data, if present
89                  FormatCompliance.getDefault());
90  
91          // Read the first directory in the file. A practical implementation
92          // could use any of the directories in the file. This demo uses the
93          // first one just for simplicity.
94          final TiffDirectory directory = contents.directories.get(0);
95          // Render the first directory in the file
96          if (!directory.hasTiffFloatingPointRasterData()) {
97              System.err.println("Specified directory does not contain floating-point data");
98              System.exit(-1);
99          }
100 
101         // Construct a TiffImageParser instance and use it to read the data.
102         // If only a sub-image is desired, the params Mao can be used
103         // to specify what section of the data is to be extracted.
104         // See the Javadoc for readFloatingPointRasterData for more details.
105         final long time0Nanos = System.nanoTime();
106         final TiffImagingParameters params = new TiffImagingParameters();
107         final TiffRasterData rasterData = directory.getRasterData(params);
108         final long time1Nanos = System.nanoTime();
109         System.out.println("Data read in " + (time1Nanos - time0Nanos) / 1.0e+6 + " ms");
110 
111         // One of the test files in the Commons Imaging distribution uses
112         // the value 9999 as a special "No Data" indicator. In that case,
113         // we do not want to include 9999 in the simple-statistics survey.
114         final float excludedValue = Float.NaN;
115         TiffRasterStatistics simpleStats;
116         if ("Sample64BitFloatingPointPix451x337.tiff".equals(target.getName())) {
117             simpleStats = rasterData.getSimpleStatistics(9999);
118         } else {
119             // just gather the standard statistics
120             simpleStats = rasterData.getSimpleStatistics();
121         }
122 
123         final int w = rasterData.getWidth();
124         final int h = rasterData.getHeight();
125         final float minValue = simpleStats.getMinValue();
126         final float maxValue = simpleStats.getMaxValue();
127 
128         System.out.format("Image size %dx%d%n", w, h);
129         System.out.format("Range of values in TIFF: %f %f%n", minValue, maxValue);
130         System.out.format("Number of data values found %d%n", simpleStats.getCountOfSamples());
131 
132         if (optionalImageWritingEnabled) {
133             // The easiest way to get an image is by using the
134             // TiffDirectory class' getTiffImage() methods and passing in
135             // an optional photometric interpreter as shown in the
136             // ReadAndRenderFloatingPoint.java example, But in this case,
137             // we'll take the approach of building an image from the
138             // raster data that was obtained above.
139             final File output = new File(outputPath);
140             System.out.println("Writing image to " + output.getPath());
141             // create a new photometric interpreter based on the range
142             // of values found above.
143             final List<PaletteEntry> paletteList = new ArrayList<>();
144             if (!Float.isNaN(excludedValue)) {
145                 // draw the excluded value in red.
146                 paletteList.add(new PaletteEntryForValue(excludedValue, Color.red));
147             }
148             paletteList.add(new PaletteEntryForRange(minValue, maxValue, Color.black, Color.white));
149             // palette entries are defined for ranges minValue <= value < maxValue.
150             // Thus raster cells containing the maximum value would not be
151             // color-coded unless we add an additional palette entry to
152             // handle the single-value for the maximum.
153             paletteList.add(new PaletteEntryForValue(maxValue, Color.white));
154             final PhotometricInterpreterFloat photometricInterpreter = new PhotometricInterpreterFloat(paletteList);
155 
156             // Now construct an ImageBuilder to store the results
157             final ImageBuilder builder = new ImageBuilder(w, h, false);
158             for (int y = 0; y < h; y++) {
159                 for (int x = 0; x < w; x++) {
160                     final float f = rasterData.getValue(x, y);
161                     final int argb = photometricInterpreter.mapValueToArgb(f);
162                     builder.setRgb(x, y, argb);
163                 }
164             }
165 
166             final BufferedImage bImage = builder.getBufferedImage();
167             ImageIO.write(bImage, "JPEG", output);
168         }
169     }
170 }