Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly display an image as a 3D plot with step transitions in Python?

I am trying to visualize differences between images in 3D, in order to more easily differentiate between positive and negative differences.

I have succeeded with a basic plot of an image, however, between the values matplotlib is interpolating values. I need these to be step changes between pixels.

I am often testing with very low-resolution images, for example, 16 by 16, so the interpolation has a large effect.

Numpy file of 16 by 16 image: https://wetransfer.com/downloads/c916f76e0d86a61c00c2ed4cfe4ae97520190210192200/60d87c

One way to solve this would be to repeat the values however, this seems very inefficient and requires cleaning up the ticks after.

enter image description here

Code to generate above image:

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

SubIm = np.load("Subtract_Image.npy")

def ImPlot2D3D(img, cmap=plt.cm.jet):

    Z = img[::1, ::1]

    fig = plt.figure(figsize=(14, 7))

    # 2D Plot
    ax1 = fig.add_subplot(1, 2, 1)
    im = ax1.imshow(Z, cmap=cmap)
    ax1.set_title('2D')
    ax1.grid(False)

    # 3D Plot
    ax2 = fig.add_subplot(1, 2, 2, projection='3d')
    X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
    ax2.plot_surface(X, Y, Z, cmap=cmap)
    ax2.set_title('3D')

    plt.show()


ImPlot2D3D(SubIm)

I've looked into 3D bar charts but they all use binning schemes and I can't make it work for an image.

like image 729
James Carron Avatar asked Jan 24 '26 03:01

James Carron


1 Answers

Eventually managed to answer my own question.

A brute force method to solve this is to repeat the values in the array, hence making the interpolation between values that 'matplotlib' does, less impactful and better approximating a step change. This can be achieved using numpy.repeat. As this is a 3D array we must iterate over one axis than the other. Otherwise, the array will be flattened repeated and this flat array returned.

Result: Results:

def ImPlot2D3D(img, cmap=plt.cm.jet, step=False, ratio=10):

    if step:
        img = (img.repeat(ratio, axis=0)).repeat(ratio, axis=1)

    Z = img[::1, ::1]

    fig = plt.figure(figsize=(14, 7))

    # 2D Plot
    ax1 = fig.add_subplot(1, 2, 1)
    im = ax1.imshow(Z, cmap=cmap)
    ax1.set_title('2D')
    ax1.grid(False)

    # 3D Plot
    ax2 = fig.add_subplot(1, 2, 2, projection='3d')
    X, Y = np.mgrid[:Z.shape[0], :Z.shape[1]]
    ax2.plot_surface(X, Y, Z, cmap=cmap)
    ax2.set_title('3D')

    # Scale the ticks back down to original values
    if step:
        ticks_x = ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x / ratio))
        ticks_y = ticker.FuncFormatter(lambda y, pos: '{0:g}'.format(y / ratio))
        ax1.xaxis.set_major_formatter(ticks_x)
        ax1.yaxis.set_major_formatter(ticks_y)
        ax2.xaxis.set_major_formatter(ticks_x)
        ax2.yaxis.set_major_formatter(ticks_y)

    plt.show()

import matplotlib.ticker as ticker
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D

SubIm = np.load("Subtract_Image.npy")
ImPlot2D3D(SubIm, step=True)
like image 193
James Carron Avatar answered Jan 26 '26 16:01

James Carron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!