I need to use Tensorflow's .map() function to read in a 32-bit TIFF images (really DEM rasters). So far I've tried several approaches with no luck. The images are all in completely accessible folders that are readable and writeable by any and all users. Because we need to read in thousands of 32-bit TIFF rasters for training a TF model, the solution needs to be able to be mapped to all entries in a tf.data.Dataset.from_tensor_slices() object.
For testing purposes I am using a single band 32-bit floating point raster for a very small portion of the area we're actually interested in.
img_path = 'depth.tif'
Attempt 1: Use tfio.experimental.image.decode_tiff()
Even when not using with TF Datatet objects this approach is failing. Here is a very simple code that doesn't seem to work.
import tensorflow_io as tfio
img = tf.io.read_file(img_path)
img_decoded = tfio.experimental.image.decode_tiff(img, index=1)
But I keep receiving the following error:
Traceback (most recent call last):
File "C:\Users\Public\load_tif_dataset_from_dir.py", line 141, in <module>
img_read = tfio.experimental.image.decode_tiff(img, index=0)
File "C:\ProgramData\Anaconda3\envs\substrate\lib\site-packages\tensorflow_io\python\experimental\image_ops.py", line 87, in decode_tiff
return core_ops.io_decode_tiff(contents, index, name=name)
File "<string>", line 6306, in io_decode_tiff
File "C:\ProgramData\Anaconda3\envs\substrate\lib\site-packages\tensorflow\python\framework\ops.py", line 7209, in raise_from_not_ok_status
raise core._status_to_exception(e) from None # pylint: disable=protected-access
tensorflow.python.framework.errors_impl.InvalidArgumentError: {{function_node __wrapped__IO>DecodeTiff_device_/job:localhost/replica:0/task:0/device:CPU:0}} unable to read directory: 0 [Op:IO>DecodeTiff]
This approach also fails whenever trying to read in TIFF images into TF Dataset objects.
Attempt 2: Use rasterio package with TF .map() function
I've used rasterio several times in the past to read and write 32-bit TIFF images without any issue, so I would assume this method should work. To begin I wrote a simple function to parse the images:
import rasterio as rio
def parse_with_rasterio(image_path):
img = rio.open(image_path)
dat = img.read(1)
return dat
This function works as expected when run stand-alone:
foo = parse_with_rasterio(img_path)
print(foo)
[[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
...
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]]
However, when attempting to use it within the .map() function of Tensorflow I receive the following error:
foo = tf.data.Dataset.from_tensor_slices([img_path])
foo_ds = foo.map(
lambda x: tf.py_function(parse_with_rasterio, [x], Tout=tf.float32)
)
print(foo_ds.element_spec)
TensorSpec(shape=<unknown>, dtype=tf.float32, name=None)
Although I'm not getting any error with using rasterio, the Tensor shape is <unknown> which means I cannot use it as a model input (requires defined dimensions).
Attempt 3: Use skimage package with TF .map() function
Creating a parse function that uses skimage functions to parse the image, which can then be called by TF .map() function.
from skimage import io as skio
def parse_with_skimage(image_path):
skimage = skio.imread(image_path)
return skimage
Again, this standalone function works as expected.
[[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
...
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]]
But there is an issue when running it within the TF .map() function:
foo = tf.data.Dataset.from_tensor_slices([img_path])
foo_ds = foo.map(
lambda x: tf.py_function(parse_with_skimage, [x], Tout=tf.float32)
)
print(foo_ds.element_spec)
TensorSpec(shape=<unknown>, dtype=tf.float32, name=None)
Attempt 4: Use cv2 (OpenCV) package with TF .map() function
Created another parse function, this time using cv2 (OpenCV) package to parse the image. This function should be callable by TF .map() function.
def parse_with_opencv(image_path):
img = cv2.imread(image_path, flags=cv2.IMREAD_ANYDEPTH)
return img
This function works fine when run standalone.
img = cv2.imread(img_path, flags=cv2.IMREAD_ANYDEPTH)
print(img)
[[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
...
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]
[-3.4e+38 -3.4e+38 -3.4e+38 ... -3.4e+38 -3.4e+38 -3.4e+38]]
However, similar to the other attempts, it fails to read the image in correctly when used in the TF .map() function.
ds = tf.data.Dataset.list_files([img_path])
img_ds = ds.map(
lambda filename: tf.py_function(cv2.imread, [filename], Tout=tf.float32)
)
print(img_ds.element_spec)
TensorSpec(shape=<unknown>, dtype=tf.float32, name=None)
I'm using the following configuration:
Python version: 3.10.6
Tensorflow version: 2.10
As stated earlier, we need to be able to read a 32-bit floating point TIFF image into TF Dataset object for ML model training. Unfortunately the data cannot be simplified to lower precision. Does anybody have a solution to reading 32-bit TIFF images into TF Dataset objects?