2

I want to write a binary raster to disk. I have the numpy array whose dtype is:

dtype('bool')

I try to open a GTiff for writing:

img_output = rasterio.open(
            "../../binary_output.tif",
            'w',
            driver='GTiff',
            nodata=nd,
            height=self.raster.height,
            width = self.raster.width,
            count=1,
            dtype = binary_raster.dtype,
            crs=self.raster.crs,
            transform=self.raster.transform,)  

But I end up with:

TypeError: invalid dtype: 'bool'

I have also tried things like:

dtype = bool
dtype = 'bool'
dtype = "bool"

What is the problem with this? According to the docs, dtype can be any numpy dtype. How do I write a binary raster?

4

Yes, you can write a one bit raster with rasterio*.

You need to:

  1. write to a format that supports a 1bit dataset, such as GeoTIFF;
  2. ensure your numpy array is np.uint8/ubyte so rasterio doesnt raise the TypeError: invalid dtype: 'bool' exception; and
  3. pass the NBITS=1 creation option to tell the underlying GDAL GeoTIFF driver to create a one bit file.

import numpy as np
import rasterio as rio

with rio.open('test_byte.tif') as src:
    data = src.read()
    profile = src.profile

with rio.open('test_bit.tif', 'w', nbits=1, **profile) as dst:
    dst.write(data)
    # If your array is not a byte dtype, you need to cast it as ubyte/uint8
    # dst.write(data.astype(np.uint8))

$ ls -sh test_bit.tif
228K test_bit.tif

$ ls -sh test_byte.tif
1.8M test_byte.tif

$ gdalinfo test_bit.tif
Driver: GTiff/GeoTIFF
Files: test_bit.tif
Size is 1588, 1167
<snip...>
Band 1 Block=1588x41 Type=Byte, ColorInterp=Palette
  Image Structure Metadata:
    NBITS=1

$ gdalinfo test_byte.tif
Driver: GTiff/GeoTIFF
Files: test_byte.tif
Size is 1588, 1167
<snip...>
Band 1 Block=1588x5 Type=Byte, ColorInterp=Gray

* whether or not any software other than GDAL based can read it, I don't know...

4

If you call rasterio.dtypes.check_dtype(np.bool_) you'll see that it's not a known dtype, because gdal doesn't support a true 1-bit dtype. GDT_Byte is the smallest. The list that rasterio is checking against is:

dtype_fwd = {
0: None,            # GDT_Unknown
1: ubyte,           # GDT_Byte
2: uint16,          # GDT_UInt16
3: int16,           # GDT_Int16
4: uint32,          # GDT_UInt32
5: int32,           # GDT_Int32
6: float32,         # GDT_Float32
7: float64,         # GDT_Float64
8: complex_,        # GDT_CInt16
9: complex_,        # GDT_CInt32
10: complex64,      # GDT_CFloat32
11: complex128}    # GDT_CFloat64

uint8 and int8 are mapped to the ubyte, as seen here:

https://github.com/mapbox/rasterio/blob/master/rasterio/dtypes.py#L29-L45

  • ah, okay, thanks. do you perhaps have any suggestion how to store a binary raster then? – Jan Pisl Oct 10 at 19:57
  • i might be missing something but it seems very strange to me that bool dtype is not an option. surely, storing a boolean raster must be a common task. – Jan Pisl Oct 10 at 20:05
  • 2
    I'd just save it as a byte raster only containing the values {0,1}. You can use your_array.astype(np.uint8) before saving it out, and then if you load it later you can do the reverse if necessary, your_array.astype(np.bool_). I'm not sure of the exact reasons behind gdal not having a 1-bit data type, but it potentially it has to do with the fact that a byte is the minimum addressable unit of data on most computer systems, so it's common to use a whole byte to store a boolean value. Doing so is less memory efficient but more computationally efficient. – mikewatt Oct 10 at 20:34
  • 2
    This is generally correct, except that the underlying GDAL GeoTIFF driver can write datasets with nbits < 8, see my answer for details. – user2856 Oct 10 at 23:18
  • Oh neat, I never noticed that creation option – mikewatt Oct 11 at 0:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.