The image_tool
program serves as a utility for the user to interact with the
ENVI images that are output by many DIRSIG simulations.
Overview
The DIRSIG generated ENVI image files do not normally
have native support in most operating systems' default image viewers,
so a tool to interact with them can be useful. These images can
also be viewed via QGIS or interacted
with programmatically in Python via the
spectral
package or via
GDAL. However, DIRSIG ships image_tool
with
the standard distribution for simplicity and to support some common
operations.
image_tool
is a tool-based application and operates on subcommands,
similar to many other well-known CLI applications, such as git
or aws
. The list of available tools can be obtained via:
help
tool.$ image_tool -h
Usage: image_tool [options] <command> ...
DIRSIG Image manipulation and interaction tool
Options:
-h/--help
Display this help and exit.
-v/--version
Display build and version info and exit.
--log_level string
Sets the minimum logging level (debug, info, warning, error, critical, off). Defaults to info.
Commands:
analyze
convert
envi
test
For command help: image_tool <command> -h
The Convert Tool
The convert
tool is used to convert ENVI images into other common image
formats that can be viewed in common image viewers. This program uses
QImageWriter to write output images
and thus the
supported output formats
are determined by the abilities of that library. The reason ENVI images are
output by DIRSIG is their support for arbitrary spectral bands and lossless
floating point output. Since these are not features of many common image
formats, this conversion will be lossy and thus has a number of options to
control the conversion. For the current full list of options, use the help
command, however an overview will be given here.
convert
tool.$ image_tool convert -h
Usage: image_tool ... convert [options] filenames+
Converts ENVI format images to 8-bit RGB images for easy viewing.
NOTE:
Radiance or non-8-bit DN (digital number) images must be scaled into
the 0-255 range for viewing. This tool supports three general scaling
modes for this reason:
* Manually gain/bias, called "manual scaling".
* Automatically gain/bias the range [N%, (100-N)%] into 0-255, called "percent scaling".
* Automatically gain/bias the range [μ-Nσ, μ+Nσ] into 0-255, called "sigma scaling".
NOTE:
The default behavior is '--percent 2' if no scaling options are present.
Positional Arguments:
filenames
The input filename(s)
Options:
-h/--help
Display this help and exit.
-f/--format string
The output file format. Defaults to png.
-o/--output string
The output filename. Defaults to input filename plus new extension.
--stdout
Write to stdout.
[Scaling]
--gain float
Use manual scaling, set gain.
--gains float float float
Use manual scaling, set gain for the R, G, and B channels separately.
--bias float
Use manual scaling, set bias.
--biases float float float
Use manual scaling, set bias for the R, G, and B channels separately.
--minmax
Alias for --percent 0.
-p/--percent float
Use percent scaling, set percentage.
-s/--sigma float
Use sigma scaling, set standard deviation.
--per_band
Apply sigma or percent scaling per-band.
--all_same
Apply the same scaling to all images, determined by the first image. Useful for making GIFs.
--output_scaling
Print scaling gains/biases to stderr.
--autoscale string
Supports old autoscale mode (none, percent, minmax, bandminmax, twosigma, gamma), but issues a deprecation warning. For backward compatibility only!
[Processing]
-b/--bands int int int
The bands to use for RGB output. Defaults to 0, 1, 2.
-B/--band int
The band to use for Grayscale output.
-x/--xyztorgb
Convert XYZ to RGB?
-y/--gamma float
Set the gamma. Note: This is a gamma correction, so each pixel value is raised to 1/γ. Defaults to 1.
-t/--tonemap string
Set the tonemap operator: none, srgb, exp, reinhard.
The conversion can be thought of as a multi-step process:
-
Determine which band(s) from the input will be converted,
-
Determine the min and max values in the input data to be used with a linear scaling approach (discussed below in the scaling options section), and
-
Optionally modify the default linear scaling to a non-linear scaling approach (discussed below in the processing options section).
Band and Pixel Selection
If the --band
option is provided, then a grayscale image will be
produced. If the input only has a single band, then this band will
be used to produce the grayscale image (e.g., the same as explicitly
using --band=0
).
The --bands
options are used to specify which band or bands are to be
mapped to the red, green and blue channels in the output image. The
values provided to this option are the 0-based indices of the bands
mapped to the red, green and blue channels, respectively. If the input
image only has 3 bands, then the default is to assume they are in R,
G, B order (e.g., the same as explicitly using --bands=0,1,2
).
If the default bands variable is set in an ENVI image header
file, these bands will be used as default if the --bands option
is not provided.
|
The --pixel_stride
option is used to sample which pixels will be
extracted. This option can be used to downsample the image by
skipping over pixels. The default stride is 1
, which is to extract
a pixel, move forward 1
pixel, extract a pixel, etc. Using a
stride of 2
effectively downsamples the image by a factor of 2.
Note that this sampling is performed in both the X and Y (row and
column) dimensions of the image.
Scaling Options
The primary operation in the conversion is the scaling of floating-point
data into 8-bit, integer values. Although many scaling options and
combinations are available, under the hood the convert
tool supports
three, primary scaling methods:
-
Linear scaling to the range [N% → (100-N)%] into 0 → 255 using automatically computed gain and bias values, referred to as percent scaling.
-
This method is available via the
--percent
option, where the user provides the value ofN
(e.g.,--percent=5
will set the min and max to 5% and 95%, respectively).
-
-
Linear scaling to the range [μ-Nσ → μ+Nσ] into 0 → 255 using automatically computed gain and bias values, referred to as sigma scaling.
-
This method is available via the
--sigma
option, where the user provides the value ofN
(e.g.,--sigma=2
will set the min and max to -2σ and +2σ, respectively).
-
-
Linear scaling via user-supplied gain and bias values, referred to as manual scaling.
-
This method is available by providing the
--gains
and--biases
(or--gain
and--bias
for a single-band image) options.
-
Processing Options
Gamma correction
The --gamma
option allows the user to apply a standard, non-linear
Gamma Correction.
Each value is raised to 1/γ, where γ is the the gamma value provided.
This is the default processing option, with the gamma value set to 1
(e.g., the same as explicitly using --gamma=1
).
Tristimulous mapping
If the input image was created with a sensor using the
CIE RGB Color Matching Responses
(also referred to as the "CIE tristimulous curves"), then this
mapping (requested using the --xyztorgb
option) will appropriately map
the channel values to RGB values in the output.
Tone mapping
The --tonemap
option applies a tone-mapping operation after the
image has been scaled to the 8-bit range (0-255). The initial scaling
is done in floating point space and this tone mapping is applied
in a 0-1 floating point space and then scaled back to 0-255, so no
quantization concerns should be present. See the help
output for
the various supported tone mappings. This option is intended primarily
for making linear response images, such as radiance images, more
natural-looking for visual consumption.
The following options are currently available:
none
-
A pass-through (linear) mapping.
srgb
-
Applies the standard sRGB mappings. This assumes the input image was produced using the CIE RGB Color Matching Responses.
exp
-
Applies an exponential mapping.
reinhard
-
Applies the standard Reinhard inverse-square falloff mapping (Vout = Vin / (1 + Vin))
Output Options
Output format
The --format
or -f
option can be used to specify the output format
(png
and jpg
are the most commonly used). The default format is
8-bit PNG (e.g., the same as explicitly providing --format=png
).
Output filename
By default, the output filename is the input filename with the file
format extension (e.g., .png
, .jpeg
, etc.) appended to it (e.g.,
demo.img.png
is produced when demo.img
is the input). The
--output_filename
option allows the user to specify the name of
the output image file.
The --output_filename option cannot be used when converting
multiple images or with the --stdout option.
|
Output to stdout
If --stdout
is specified, which will write the output file to
stdout
, which allows the use of shell pipes to redirect the output
to another program.
Helpful Options
Scaling bands independently
The default for the automatic scaling methods (percent and sigma)
is to compute a single gain and bias across all the bands, as if
they were part of the same dataset. In some cases, you might want
these scaling methods to compute a specific gain and bias for each
band. For that use case, the --per_band
option can be used.
Scaling multiple images at once
When automatically scaling a set of images in a single execution,
it is useful to "lock" the scaling across all the images to avoid
any image to image flicker that will arise from differences in the
values (e.g., minimum, maximum, mean, etc.) in each image. The
--all_same
option will compute the gain and bias for the first
image in the set, and then apply that same gain and bias to all the
images. A common use case is making a diurnal video, where the
magnitude of the images is changing during the day. If you want
each frame in the video to be independently scaled, then use the
automatic methods as previously described. However, if you want
the scaling to be constant all day, then the --all_same
option
should be added. If you want to have the scaling based on the
brightest image (presumably a midday image in the sequence), then
provide that image as the first in the list of images.
Outputting the scaling
If you want to reveal the computed gain and bias values determined
by the automatic scaling methods (e.g., percent and gamma) then specify
the --output_scaling
option. This is handy if you want to store the
gain and bias values to apply at a later time.
Example Usage
Converting to a color PNG image
To convert bands 16
, 11
and 6
in a DIRSIG (ENVI) .img
file to
color PNG using a 2.5 gamma scaling, use the following example syntax:
$ image_tool convert --gamma=2.5 --bands=16,11,6 test.img
This will produce test.img.png
since PNG is the default format.
Converting to a grayscale JPEG image
To make a grayscale JPEG file using the 1% scaling, specify the same band for all three bands:
$ image_tool convert --percent=1 --format=jpeg --band=3,3,3 --output_filename=gray.jpeg test.img
or use the --band
option:
$ image_tool convert --percent=1 --format=jpeg --band=3 --output_filename=gray.jpeg test.img
This will produce gray.jpeg
.
Converting multiple images to color PNGs
To bulk convert a series of images, you can use standard shell wildcards on LINUX and macOS:
$ image_tool convert --sigma=2 --format=png --bands=2,1,0 demo-t0000-c*.img
This works because LINUX and macOS shells expand demo-t0000-c*.img
into a list of all matching files, which is supplied to image_tool
and it then iterates through that list. However on Windows,
neither CMD
or PowerShell
expands wildcards. Instead, they
simply pass them to programs and expects the program to expand them
(which image_tool
does not support). Hence, we need to generate
the list of matches outside of image_tool
and pass that list to
the program. Here is an example of how to do that in PowerShell
using the build-in Get-Item
cmdlet:
PS C:\Users\dirsig\demos\PlatformJitter1> $list = Get-Item demo-t0000-c*.img
PS C:\Users\dirsig\demos\PlatformJitter1> image_tool convert --gamma=3 --format=png $list
If you want to scale all your images the same (using the --all_same
option) but the Nth image in the sequence is the one you want
used to establish the scaling, then provide this image first and
then use wildcards for the entire sequence. For example, supply
demo-t0000-c0010.img demo-t0000-c*.img . The scaling will be
established using demo-t0000-c0010.img and then applied to
demo-t0000-c0000.img , demo-t0000-c0001.img …
demo-t0000-c0010.img , etc. Yes, the demo-t0000-c0010.img file
will be scaled twice in the process, but the simple command-line
syntax is convenient.
|
The Analyze Tool
The analyze
tool is intended to help with basic statistical
analysis on images. Its full options can be seen with the help
output:
$ image_tool analyze -h
Usage: image_tool ... analyze [options] operator filename
Positional Arguments:
operator (required!)
The analysis operator to perform.
* extract
Output each band for every pixel
* band_min_max
Output min/max in each band across all the pixels
* band_mean_stddev
Output mean/stddev in each band across all the pixels
* band_covariance
Output covariance statistics between bands
filename (required!)
The input filename.
Options:
-h/--help
Display this help and exit.
--bands string
The test bands. The supported formats are:
* [band=]B
An individual band.
* [bandlist=]B1,B2,...
An enumerated list of bands.
* bandrange=B1,B2
The range of bands from B1 to B2, inclusive.
--roi string
The test ROI. The supported formats are:
* pixel=X,Y
An individual pixel at X,Y.
* xline=X
A 1D profile in X (constant X, varying Y).
* yline=Y
A 1D profile in Y (constant Y, varying X).
* rect=X1,Y1,X2,Y2
The rectangle from (X1,Y1) to (X2,Y2), inclusive.
- Band Sampling
-
The
--bands
option is used to select which bands will be analyzed.-
To select all the bands, do not include this option.
-
To select a single band, use
--bands='band=0'
. -
To select a list of bands, use
--bands='bandlist=0,3,4,7'
-
To select a range of bands, use
--bands='bandrange=3,7'
-
- Spatial Sampling
-
The
--roi
option is used select which pixels will be analyzed.-
To select all the pixels, do not include this option.
-
To select a single pixel, use
--roi='pixel=X,Y'
, whereX
andY
are the 0-based indexes (coordinates) of the pixel. -
To select a vertical column of pixels, use
--roi='xline=X
', whereX
is the 0-based index (coordinate) of the column. -
To select a horizontal row of pixels, use
--roi='yline=Y
', whereY
is the 0-based index (coordinate) of the row. -
To select an axis-aligned rectangular region of pixels, use
--roi='rect=X1,Y1,X2,Y2'
, whereX1
,Y1
,X2
andY2
form the region from (X1,Y1) to (X2,Y2), inclusively.
-
- Analysis Operators
-
The positional "operator" options define what analysis will be performed on the selected data:
-
To get the minimum and maximum value across all the selected pixels by band, use the
band_min_max
operator. -
To get the mean and standard deviation across all the selected pixels by band, use the
band_mean_stddev
operator. -
To get the covariance between bands across all the selected pixels, use the
band_covariance
operator. -
To extract the data to an ASCII/Text, comma separated value (CSV) output, use the
extract
operator.
-
To extract the data as binary data rather than ASCII/Text, use the data tool. |
Example Usage
To compute the min/max for a specific set of bands, use the following syntax:
$ image_tool analyze band_min_max --bands='bandlist=0,2,11' test.img
To compute the mean and stddev for a single band (band index = 2
) within
a rectangular ROI (with lower-left and upper-right image coordinates
of 10,8
and 32,21
, respectively), use the following syntax:
$ image_tool analyze band_mean_stddev --bands='band=2' --roi='rect=10,8,32,21' test.img
The Data Tool
The data
tool is intended for extracting binary data from the the
ENVI image file and sending it to the standard output (console,
etc.). It’s primary role is as the backend for the new image viewer
introduced with DIRSIG5. Its full options can be seen with the
help
output:
$ image_tool data -h
Usage: image_tool ... data [options] filename
Positional Arguments:
filename (required!)
The input filename.
Options:
-h/--help
Display this help and exit.
--bands string
The test bands. The supported formats are:
* [band=]B
An individual band.
* [bandlist=]B1,B2,...
An enumerated list of bands.
* bandrange=B1,B2
The range of bands from B1 to B2, inclusive.
--pixel_stride uint
The pixel stride, to only load every nth pixel. Default is 1.
- Band Sampling
-
The
--bands
option is used to select which bands will be extracted.-
To select a single band, use
--bands='band=0'
. -
To select a list of bands, use
--bands='bandlist=0,3,4,7'
-
To select a range of bands, use
--bands='bandrange=3,7'
-
- Pixel Sampling
-
The
--pixel_stride
option is used to sample which pixels will be extracted. This option can be used to downsample the image by skipping over pixels. The default stride is1
, which is to extract a pixel, move forward1
pixel, extract a pixel, etc. Using a stride of2
effectively downsamples the image by a factor of 2. Note that this sampling is performed in both the X and Y (row and column) dimensions of the image.
Example Usage
The following example extracts a single band (band index = 2
) and every
3rd pixel from the input and sends the binary data to the output.
$ image_tool --bands='band=2' --pixel_stride=3 example.img
[binary data is displayable in a document]
The ENVI Tool
The envi
tool is intended to provide some functionality specifically
targeted to local (on disk) ENVI images. Currently, only one operation
is implemented, which is a scanning of the ENVI header file to
extract metadata about the image. The tool will print the requested
field in the requested header file to standard output. Fields that
have multiple values (curly-brace lists) will be printed with one
value per line. This tool is intended for script usage to avoid
potentially complicated parsing of an ENVI file with tools that are
not readily available on every computing platform.
envi
tool.$ image_tool envi -h
Usage: image_tool ... envi [options] field filename
Positional Arguments:
field (required!)
The header field to scan.
filename (required!)
The input filename (.hdr).
Options:
-h/--help
Display this help and exit.
Example Usage
To fetch a given ENVI header variable, you supply the field (tag) name. For example, to get the number of lines in the image file, use the following syntax:
$ image_tool envi 'lines' test.img.hdr
240
To get the radiometric units of the image file, use the following syntax:
$ image_tool envi 'data units' test.img.hdr
watts/(cm^2 sr um)
The quotes (i.e., '' ) around the field (tag) name is important
when the field has a multi word name (e.g., data type , header
offset , data units , etc.).
|
The Test Tool
The test
tool is used to make and perform tests on images. This
can be useful for making assertions about DIRSIG output. Tests can
be specified in a JSON format either via a file or through stdin
with the --stdin
option. An example test input is:
Test Definition
[
{
"filename": "rgb.img",
"tests": [
{
"id": "band_mean",
"name": "Spatially-averaged band values for the image",
"description": "Verifies the spatially-averaged band values of the image.",
"history": "Test automatically generated with 'image_tool' 2021.30 (04678d1)",
"metric": "band_mean",
"expected": [0.01513, 0.01513, 0.01513],
"tolerance": [1.513e-05, 1.513e-05, 1.513e-05]
}
]
}
]
The structure of the JSON document is an array containing objects
for each image file to be processed. Each image file object contains
the filename
variable with the name of the image file and a tests
object that is an array of tests to be performed. Each test object
contains the following:
Metadata
Each test contains a set of variables describing the test:
-
The
id
is a unique string for each test that is used to reduce duplication when running multiple tests in a single execution of the tool. These IDs must be unique within a set of tests run in a given execution. -
The
name
is the short name for the test that is displayed when the test is run. It should contain a unique and brief but descriptive string. -
The
description
is available to capture a more detailed description of the test. It might provide important details about the test and what it attempts to achieve. -
The
history
is available to document the history of the test and will typically contain who made the test and when.
Metrics
The supported values for metric
are:
-
"band_mean"
→ The mean of the ROI for each band -
"band_min"
→ The minimum of each band in the ROI -
"band_max"
→ The maximum of each band in the ROI -
"band_min2"
→ The mean of the bottom 2% of values in the ROI for each band -
"band_max2"
→ The mean of the top 2% of values in the ROI for each band
The expected value for the metric
is supplied via the expected
array.
The dimension of this array is based on the number of bands (channels)
in the image or the band selection defined in the test (see below).
Tolerance
The tolerance
defines the allowable deviation of the computed values
from the expected
values. Like the expected
values, this is an
array sized to match the number of bands (channels) used in the test.
The tolerance is in the same units as the data. Alternatively, the
tolerance can be specified as percentage (via the percentage
array
rather than the tolerance
array), where each array element is
a percentage (0-100) error (from expected
) that can be tolerated.
Band Subsets
By default, the metric is computed for all bands in the image.
The bandSubset
can be used to confine the bands over which the
statistics are calculated. This field is an object of the form:
"bandSubset" : {
"type": "band",
"value": [0]
}
The supported values for type
are band
, list
, and range
.
-
For
band
, thevalues
array contains a single band index. -
For
list
, thevalues
array contains arbitrary number of band indices. -
For
range
, thevalues
array contains a band index pair that defines the start and index (inclusive) of the band range.
Any of these band indices can also be substituted with a string of
the form $<BAND_NAME:name>
, where name
is the name of the band
in the image file. This can be especially useful when defining tests
for the classic DIRSIG truth image data cube. Using the band name
makes it more robust if more truth is added to the image and the band
indexes change.
BANDNAME
option. "bandSubset": {
"type": "band",
"value": [ "$<BAND_NAME:Dominant Material Index>" ]
},
Spatial Subsets
By default, the metric is computed for all pixels in the image.
The roi
allows the test to restrict the set of pixels that the
metric
is computed for. This field is an object of the form:
"roi" : {
"type": "rect",
"value": [0, 5, 10, 15]
}
The supported values for type
are pixel
, xline
, yline
and
rect
.
-
For
pixel
, thevalue
array should contain 2 elements indicating theX,Y
coordinate of the pixel. -
For
xline
andyline
, thevalue
array should contain a single element indicating thex
ory
index at which to take a line profile. -
For
rect
, thevalue
array should contain 4 elements indicating themin_x
,min_y
,max_x
andmax_y
coordinates of the rectangular region.
Making a Min/Mean/Max Test
There is an option to the test
tool to generate a simple image statistics
"fingerprint" test:
$ image_tool test make demo.img > stats.json
This will compute the image min 2%, max 2% and mean values and automatically create a test to confirm them.
Using the test make combination is also a good way to generate the
basic JSON document for a test that can be manually customized.
|
Updating an Existing Test
If you have an existing test JSON file and need to update the expected
values to match an existing image, then the test update
option can be
used:
$ image_tool test update truth_tests.json
Running a Tests
To run a test, the test run
option should be used:
$ image_tool test run truth_tests.json
The output of the tool is a JSON document containing an object for each
file processed, and each test on that file. Each test object contains the
id
and name
for the test as well as a success
variable:
[
{
"filename": "demo_truth.img",
"tests": [
{
"id": "outside_plane1a.test",
"name": "Outside Plane, position #1, time #1",
"success": true
},
{
"id": "outside_plane1b.test",
"name": "Outside Plane, position #2, time #1",
"success": true
},
{
"id": "inside_plane1.test",
"name": "Inside Plane, position #2, time #1",
"success": true
}
]
},
...
]
If the test fails, the success
variable will be set to false
and the
error
variable will contain a string describing which band(s) failed the
test, the computed image value, the expected value and the difference.
[
{
"filename": "demo_truth-t0000-c0000.img",
"tests": [
{
"error": "Band Image Expected Abs Diff % Diff\n0 3.102620e+02 3.122620e+02 2.000000e+00 0.640488",
"id": "tests_d5/outside_plane1a.test",
"name": "Outside Plane, position #1, time #1",
"success": false
},
...
]
}
]
Using Python-based Tests
As an alternative to the statistical tests given above, image_tool
also
supports running user-defined tests using Python. This can be done with slight
modifications to the test JSON:
[
{
"filename": "rgb.img",
"tests": [
{
""
"description": "Runs a user-defined Python Test",
"history": "Developed by <name>",
"id": "user_defined_test_0"
"name": "MyTest",
"pyModule": "image_test",
"arguments": ["15"]
}
]
}
]
This example would look for the "image_test"
module in your PYTHONPATH, which
would include the current working directory. Normally, this would correspond to
a file called image_test.py
. Additional paths may be added via the
--pythonpath
argument on image_tool test run
. In this file, there should be
a global function called test
, for example:
import numpy as np
# args is present only if "arguments" is in the JSON
def test(image, args):
exp = float(args[0])
sum = np.sum(image)
return abs(sum - exp) < 0.01, f"Expected sum to be {exp}, was {sum}"
The first value of the return tuple should be a boolean indicating the success
of the test and the second should be an error string that will be shown upon
failure. Note that "arguments"
is an optional field in the JSON and should
be omitted from test()
's argument list if it is not provided.