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