Working with 2D Bitmap Data¶
Prerequisites¶
To gain any more functionality beyond simple read/write of DataTank images, you will need to install one or more of these modules.
GDAL¶
You may already have GDAL and its Python bindings installed – you can check by doing:
python -c 'import gdal'
For a binary install of GDAL, see this page which has links for various platforms. The Mac OS X framework package includes Python bindings.
PIL¶
The Python Imaging Library is also required for some operations, and is generally a useful thing to have. You can check for it with:
python -c 'import Image'
If it’s not installed, you can try:
sudo easy_install PIL
This may or may not give you a useful module, depending on what shared libraries you have available. Refer to the PIL documentation for more details.
Geospatial image example¶

This example shows different ways of passing a file to an external program. In one case, we pass a DataTank “File” object, which results in a hard link named “Image File” in the Python program’s working directory. This is most efficient in terms of disk space, but sometimes a geospatial raster includes a so-called “world file” which provides coordinate information, rather than embedding it in the TIFF tags. For example, “my_geotiff.tiff” could have a corresponding “my_geotiff.tfw” and GDAL knows to look for that file alongside the GeoTIFF. In that case, you need to pass the absolute path.
Clear as mud? Use The Source, Luke! Hopefully this example will clarify the words above.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This software is under a BSD license. See LICENSE.txt for details.
import os
import numpy as np
from datatank_py.DTDataFile import DTDataFile
from datatank_py.DTBitmap2D import DTBitmap2D
from time import time
if __name__ == '__main__':
#
# This program replaces a standard DataTank External Program module
# in C++. Note that it must be executable (chmod 755 in Terminal)
# and gdal and numpy are required.
#
# It takes as input a file path, then loads an image from it, using
# GDAL to determine the raster origin and pixel size. The image is
# then saved as a 2D Bitmap object, either 8 or 16 bits as needed.
#
# Note that this is not appropriate for elevation data, as DataTank
# will normalize the file range from [0, 1] if you extract the gray
# component.
#
input_file = DTDataFile("Input.dtbin")
# DT creates this hard link in the working directory, if passed a file.
# This is preferred, as it's fewer variables in DataTank, but if you
# have a world file, GDAL needs to be able to find it in the original
# directory.
image_path = "Image File"
# if no path set, then use the file itself (preferable)
if os.path.exists(image_path) == False:
image_path = input_file["Image Path"]
input_file.close()
start_time = time()
errors = []
if image_path:
image_path = os.path.expanduser(image_path)
if image_path is None or os.path.exists(image_path) is False:
errors.append("\"%s\" does not exist" % (image_path))
img = DTBitmap2D(image_path)
if img is None:
# set an error and bail out; DataTank doesn't appear to use this, but displays
# stderr output instead, so print them also
errors.append("Unable to open as an image file")
with DTDataFile("Output.dtbin", truncate=True) as output_file:
output_file["ExecutionErrors"] = errors
output_file.["ExecutionTime"] = time() - start_time
exit(1)
with DTDataFile("Output.dtbin", truncate=True) as output_file:
output_file.["ExecutionTime"] = time() - start_time
output_file["Var"] = img
# need to save a StringList of execution errors as Seq_ExecutionErrors
if len(errors):
output_file["ExecutionErrors"] = errors
DTBitmap2D¶
-
class
datatank_py.DTBitmap2D.
DTBitmap2D
(path_or_image=None, rgba_bands=None)¶ Base implementation for DTBitmap2D. This is a gray or color (RGB) image, with an optional alpha channel. It also has spatial data, so it can be displayed in DataTank on a physical grid, rather than the logical grid of pixels.
Provides implementation of
datatank_py.DTPyWrite.DTPyWrite
for all subclasses, and can be instantiated directly to use as a container.-
__init__
(path_or_image=None, rgba_bands=None)¶ Parameters: - path_or_image – a path to an image file or a PIL image object
- rgba_bands – a 1-4 element tuple mapping one-based band indexes to (r, g, b, a)
Returns: An object that implements
datatank_py.DTPyWrite.DTPyWrite
. Don’t rely on the class name for anything.If you pass the default argument, you’ll get back an object that implements
dt_write()
, and you are responsible for filling in its attributes. These are:Member grid: optional, of the form [x0, y0, dx, dy] Member red: required for RGB image only Member green: required for RGB image only Member blue: required for RGB image only Member gray: required for grayscale image only Member alpha: optional Member projection: optional WKT string Each must be a 2D numpy array, and you are responsible for ensuring a consistent shape and proper dimension. This is basically equivalent to the way DTSource constructs a C++
DTBitmap2D
. Note that DataTank only supports 8 bit and 16 bit images.If a PIL image is provided, it will be used as-is, and the grid will be a unit grid with origin at (0, 0). If a path is provided,
DTBitmap2D
will try to use GDAL to load the image and extract its components, as well as any spatial referencing included with the image. If GDAL fails for any reason, PIL will be used as a fallback.If you have an image with more than 4 bands, you can choose which to represent as red, green, blue, and alpha by passing :param rgba_bands:. For instance, if you have LANDSAT 8 multispectral imagery, you could pass (4, 3, 2) to get a true color image. By default, instantiating an image with more than 4 bands will give an error.
Note that
DTBitmap2D
does not attempt to be lazy at loading data; it will read the entire image into memory as soon as you instantiate it.
-
channel_count
()¶ Returns: number of channels, including data and alpha Compatible with multiband images.
-
dtype
()¶ Returns: a NumPy array datatype numpy.dtype
-
equalize_histogram
()¶ Alters the image by equalizing the histogram among any non-alpha bands.
-
classmethod
from_data_file
(datafile, name)¶ Create a new instance from a DTDataFile by name.
Parameters: - datafile – an open DTDataFile instance
- name – the name of the DTBitmap2D object in the file (including any time index)
Returns: a new DTBitmap2D instance
-
has_alpha
()¶ Returns boolean: True
if the image has an alpha channelOnly valid for grayscale or RGB image models, not arbitrary multiband images.
-
is_gray
()¶ Returns boolean: True
if the image is grayscale (not RBG or RBGA)Only valid for grayscale or RGB image models, not arbitrary multiband images.
-
mesh_from_channel
(channel='gray', alpha_as_mask=False)¶ Extract a given bitmap plane as a DTMesh2D object.
Parameters: channel – may be one of (red, green, blue, gray, alpha) Returns: a datatank_py.DTMesh2D.DTMesh2D
instanceRequires GDAL
This is how you would extract raw pixels and georegistration data from e.g., a 32-bit GeoTIFF or other image supported by GDAL, and bring it in to DataTank. This is particularly useful for elevation data.
The returned mesh will use the spatial grid (origin and pixel size) of the image. Note that the image’s
nodata
attribute will be used to create an appropriatedatatank_py.DTMask.DTMask
which will be applied to the mesh. The nodata value is obtained from GDAL.>>> from datatank_py.DTBitmap2D import DTBitmap2D >>> img = DTBitmap2D("int16.tiff") >>> img.mesh_from_channel() <datatank_py.DTMesh2D.DTMesh2D object at 0x101a7a1d0> >>> img = DTBitmap2D("rgb_geo.tiff") >>> img.mesh_from_channel(channel="red") <datatank_py.DTMesh2D.DTMesh2D object at 0x10049ab90>
The returned mesh will contain values in floating point
-
pil_image
()¶ Attempt to convert a raw image to a
PIL
Image object.Returns: a PIL
Image, orNone
ifPIL
can’t be loaded or if the conversion failed.Requires PIL
This allows you to run PIL image filters and operations. Only tested with 8-bit images, but gray/gray+alpha and RGB/RGBA have all been tested.
-
raster_size
()¶ Returns: size in pixels, 2-tuple ordered as (horizontal, vertical).
-
synthesize_alpha
()¶ Adds or overwrites an alpha channel corresponding to all zero-valued RGB pixels. This is not very smart, as it doesn’t check for neighboring pixels; you can end up turning actual black pixels transparent.
-
write_geotiff
(output_path, projection_name=None)¶ Save a DTBitmap2D as a GeoTIFF file.
Parameters: - output_path – a file path; all parent directories must exist
- projection_name – the spatial reference system to associate with the image
Requires GDAL
The spatial reference must be a string recognized by GDAL. For example, EPSG:4326 and WGS84 are both valid. See the documentation for OSRSpatialReference::SetFromUserInput at http://www.gdal.org/ogr/classOGRSpatialReference.html for more specific details.
Note that exceptions will be raised if the
DTBitmap2D
is not valid (has no data), or if any GDAL functions fail. This method has only been tested with 8-bit images, but gray/rgb/alpha images work as expected.
-
DTPyCoreImage¶
-
datatank_py.DTPyCoreImage.
ci_filter_named
(filter_name)¶ Returns: a Quartz:CIFilter
with the given nameRequires Mac OS X and PyObjC
These filters can be applied to a
Quartz.CIImage
using:ci_image = ci_image_from_planes(r, g, b) filt = ci_filter_named("CIExposureAdjust") filt.setValue_forKey_(ci_image, "inputImage") filt.setValue_forKey_(0.2, "inputEV") ci_image = filt.valueForKey_("outputImage")
From this point, you would apply another filter or use
dt_bitmap2d_from_ci_image()
to convert the image to adatatank_py.DTBitmap2D.DTBitmap2D
for DataTank.
-
datatank_py.DTPyCoreImage.
ci_image_from_planes
(red, green, blue)¶ Create a Quartz CIImage from bitmap data.
Parameters: - red – a NumPy 2D array of bitmap values
- green – a NumPy 2D array of bitmap values
- blue – a NumPy 2D array of bitmap values
Returns: a
Quartz.CIImage
instanceRequires Mac OS X and PyObjC
The
Quartz.CIImage
is only useful for applyingQuartz.CIFilter
or other transformations using PyObjC. Only 8-bit RGB images are supported at this time, and your image data will be truncated if it is not 8-bit.
-
datatank_py.DTPyCoreImage.
dt_bitmap2d_from_ci_image
(ci_image, width, height, grid)¶ Parameters: - ci_image – a
Quartz.CIImage
instance from PyObjC - width – desired width in pixels
- height – desired height in pixels
- grid – a four-tuple (x0, y0, dx, dy) spatial grid
Returns: a
datatank_py.DTBitmap2D.DTBitmap2D
instanceRequires Mac OS X and PyObjC
This function allows you to turn a
Quartz.CIImage
into an object that DataTank can use. Only 8-bit RGB images are supported at this time.- ci_image – a