matplotlib / matplotlib

matplotlib: plotting with Python
https://matplotlib.org/stable/
19.6k stars 7.47k forks source link

Plot_surface add wrong value #28323

Closed leahwang1962 closed 1 day ago

leahwang1962 commented 3 weeks ago

Bug summary

I am using matplotlib.pyplot plot_surface to plot cone_like 3D surface from noisy array data, and I found there are some added error points if the cone's vertex center region is open, but these error points disappeared if the vertex center region filled. Does anyone know how to avoid these artificial noise points

Code for reproduction

from openpyxl import load_workbook
import numpy as np
import matplotlib.pyplot as plt
import sys
from sys import argv
import time

def iterating_over_values(path, sheet_name):
    # def iterating_over_values(path, xr, zr, sheet_name):
    sheet_name = "Sheet1"
    X1 = []
    Y1 = []
    workbook = load_workbook(filename=path)
    if sheet_name not in workbook.sheetnames:
        print(f"'{sheet_name}' not found. Quitting.")
        return
    sheet = workbook[sheet_name]

    xresolution =15
    zresolution =15

    NX = 1000
    NY = 1000

    xstart = -NX / 2 * xresolution
    xend = NX / 2 * xresolution + xresolution / 100
    ystart = -NY / 2 * zresolution
    yend = NY / 2 * zresolution + zresolution / 100

    ZList = []
    X = np.arange(xstart, xend, xresolution)
    Y = np.arange(ystart, yend, zresolution)
    X, Y = np.meshgrid(Y, X)
    for value in sheet.iter_rows(
            min_row=1, max_row=NX + 1, min_col=1, max_col=NY + 1,
            values_only=True):
        ZList.append(value)
        # print(value)
    Z = np.array(ZList)
    fig = plt.figure(figsize=(10, 4))

    titlestring = ""

    ax1 = fig.add_subplot(121, projection='3d')
    V = np.arange(Z.min(), Z.max(), 0.1)
    surf = ax1.plot_surface(Y, X, Z, cmap='jet',
                            linewidth=0, antialiased=False, cstride=10, rstride=10)
    ax1.grid(False)
    # Customize the z axis.
    ax1.set_zlim(Z.min(), Z.max())
    ax2 = fig.add_subplot(122)
    stepZ = (Z.max() - Z.min()) / 500
    clevZ = np.arange(Z.min(), Z.max() + Z.max() / 10, stepZ)

    cset = ax2.contourf(Y, X, Z, clevZ, cmap='jet')

    ax1.set_xlabel('X(um)', fontsize=6)
    ax1.set_xlim(xstart, xend)
    ax1.set_ylabel('Y(um)', fontsize=6)
    ax1.set_ylim(ystart, yend)
    ax1.set_zlabel('Z', fontsize=6)
    ax1.set_zlim(Z.min(), Z.max())

    ax2.set_xlabel('X(um)', fontsize=6)
    ax2.set_xlim(xstart, xend)
    ax2.set_ylabel('Y(um)', fontsize=6)
    ax2.set_ylim(ystart, yend)

    ax1.tick_params(axis='both', which='major', labelsize=6)
    ax1.tick_params(axis='both', which='minor', labelsize=6)
    ax2.tick_params(axis='both', which='major', labelsize=6)
    ax2.tick_params(axis='both', which='minor', labelsize=6)

    fig.colorbar(surf, shrink=0.5, aspect=20)
    fig.tight_layout()
    plt.show()

# def main(param1,param2,param3):
#  iterating_over_values(param1,param2,param3, sheet_name="Sheet1")

if __name__ == "__main__":
    #path = "Open vertex Cone.xlsx"
    path="closed vertex cone.xlsx"
    iterating_over_values(path, sheet_name="Sheet1")

Actual outcome

for open vertex cone, there are "added noise vertical red lines" in front of the cone in 3D plot.
image for closed vertex cone, the "added noise vertical red lines" in front of the cone in 3D plot disappeared. it is a correct 3D graph. image

Expected outcome

for open vertex cone, the 3D graph front surface should be smooth, similar as the closed vertex cone surface.

Additional information

No response

Operating system

windows11

Matplotlib Version

3.8.4

Matplotlib Backend

QtAgg

Python version

3.10.11

Jupyter version

Not installed

Installation

pip

leahwang1962 commented 3 weeks ago

here are the 2 excel files used for 3D plots Open vertex Cone.xlsx Closed vertex cone.xlsx

Thanks for any help

chaoyihu commented 2 days ago

Hi, thanks for raising this issue! I did a bit of of investigation myself, and would like to share some relevant findings.

Here is a simpler case for reproduction:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

x = 10
data= np.array([
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, x, x, x, x, 0, 0, 0],
    [0, 0, x, x, 0, 0, x, x, 0, 0],
    [0, 0, x, 0, 0, 0, 0, x, 0, 0],
    [0, 0, x, 0, 0, 0, 0, x, 0, 0],
    [0, 0, x, x, 0, 0, x, x, 0, 0],
    [0, 0, 0, x, x, x, x, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
    ])

X = np.arange(-5, 5, 1)
Y = np.arange(-5, 5, 1)
X, Y = np.meshgrid(Y, X)

fig = plt.figure()
ax1 = fig.add_subplot(111, projection='3d')
surf = ax1.plot_surface(X, Y, data, cmap='jet',
                        linewidth=0, antialiased=False)
plt.show()

In this SO discussion, one of the answers suggested setting antialiased=False, which I saw is also used in your example code. But I don't think that applies in your case, since the artifacts we see here are likely not aliasing artifacts [ref], but artifacts caused by intersecting bounding boxes (if I understood the discussion correctly). In the simpler example, the most visible artifact (the red one in the front) can be illustrated as below:

 0------------10------------10
 |  polygon1  |   polygon2   |
 |  (inside)  |   (outside)  |
10------------10------------ 0
# polygon1 should not be visible, but is still rendered.

And I found that adjusting rcount and ccount seem to produce a better result given your data:

image

Below is the code. Note here! Large rcount and ccount will be heavier computationally, and may take up memory resources, so I would suggest increasing the params in steps, experimenting along the way before jumping to a large value.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

open_cone_wb = pd.read_excel("open.xlsx")
open_cone_z = open_cone_wb.to_numpy()[:, :1000]

xresolution =1
zresolution =1
NX = 1000
NY = 1000

xstart = -NX / 2 * xresolution
xend = NX / 2 * xresolution
ystart = -NY / 2 * zresolution
yend = NY / 2 * zresolution

X = np.arange(xstart, xend, xresolution)
Y = np.arange(ystart, yend, zresolution)
X, Y = np.meshgrid(Y, X)
print(X.shape, Y.shape)

fig = plt.figure(figsize=(10, 4))

ax1 = fig.add_subplot(111, projection='3d')
open_surf = ax1.plot_surface(X, Y, open_cone_z, cmap='jet',
                             linewidth=0, rcount=1000, ccount=1000)
ax1.set_zlim(-100, 300)

plt.show()

Hope it helps.

leahwang1962 commented 1 day ago

Now the artifacts are gone. Thanks a lot.