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