Search code examples
jupyter-notebooktiffipywidgets

Displaying tif files in jupyter notebook vie ipywidgets


I am trying to display .tif images using the ipywidgets in jupyter-notebooks. The below code works for .png and .jpg files

from ipywidgets import Image
png_image_path = r"C:\Users\xxxxxxxx\Work\Exercises\images\000000000.png"
file = open(png_image_path, "rb")
im = file.read()

Image(
    value=im,
    width=300,
    height=400,
)

type(im) # <class 'bytes'>

The ipywidget from the above code renders the desired image. For reading a tif file I am using gdal.

img = gdal.Open(tif_img_path).ReadAsArray()
print(img.shape) # (3, 1024, 1024)
print(img.transpose(1,2, 0).shape) # (1024, 1024, 3)

type(img.transpose(1,2,0).tobytes()) # <class 'bytes'>

Image(
    value=img.transpose(1,2,0).tobytes(),
    width=300,
    height=400,
)

I get the following output, the image is not properly displayed in the ipywidget

I get the following output


Solution

  • The fact that you just do file.read() on the PNG image implies to me that Jupyter widgets expect a PNG or JPEG-encoded image, with a header and compressed pixel data.

    If you open your TIFF with GDAL you will have a Numpy array, so you will need to encode it into an "in-memory" PNG or JPEG before passing to Jupyter widgets. You can do that with OpenCV like this:

    import cv2
    
    # Open TIFF image into Numpy array
    img = gdal.Open(tif_img_path).ReadAsArray()
    
    # Encode into in-memory PNG for Jupyter
    _, PNG = cv2.imencode('.png', img)
    

    As you rightly note in the comments, OpenCV uses BGR ordering so you would need to reverse the order of the colour channels with:

    RGBimage =  cv2.cvtColor(BGRimage, cv2.COLOR_BGR2RGB)
    

    As an alternative to introducing the OpenCV dependency and its weird channel ordering, you could use PIL/Pillow which uses regular RGB ordering. In that case, you would convert a Numpy array you got from GDAL into a PNG with:

    from io import BytesIO
    
    im = ... read from GDAL ...
    
    # Write Numpy array to in-memory PNG
    membuf = BytesIO()
    Image.fromarray(im).save(membuf, format="png") 
    
    ... you can now use membuf.getvalue()
    

    Note also, that in general TIFFs may contain float or float64 values that cannot be expressed in an 8-bit JPEG, so you may need to re-scale your data to fit the smaller range.