Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commite8a3f57

Browse files
Specify the focal length for 3d plots
Create a gallery example showing the different proj_type optionsTypoTest fix, example fix, and lintingWhat's new, example fixMerge conflictOffset zooming of focal lengthCode review comments, consolidate projection functionsTry and fix zoomingTry to fix the focal length zoomingUpdate exampleCleanupexample cleanupmore example tweaksmore example tweakswap plot orderEnforce a positive focal lengthfocal lentgh testsflake8 lintingdocstring tweak
1 parent7457cb7 commite8a3f57

File tree

6 files changed

+145
-23
lines changed

6 files changed

+145
-23
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Give the 3D camera a custom focal length
2+
----------------------------------------
3+
4+
Users can now better mimic real-world cameras by specifying the focal length of
5+
the virtual camera in 3D plots. An increasing focal length between 1 and
6+
+infinity "flattens" the image, while a decreasing focal length between 1 and 0
7+
exaggerates the perspective and gives the image more apparent depth. The
8+
default focal length of 1 corresponds to a Field of View (FOV) of 90 deg, and
9+
is backwards-compatible with existing 3D plots.
10+
11+
The focal length can be calculated from a desired FOV via the equation:
12+
|``focal_length = 1/tan(FOV/2)``
13+
14+
..plot::
15+
:include-source: true
16+
17+
from mpl_toolkits.mplot3d import axes3d
18+
import matplotlib.pyplot as plt
19+
fig, axs = plt.subplots(1, 3, subplot_kw={'projection': '3d'})
20+
X, Y, Z = axes3d.get_test_data(0.05)
21+
focal_lengths = [0.25, 1, 4]
22+
for ax, fl in zip(axs, focal_lengths):
23+
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
24+
ax.set_proj_type('persp', focal_length=fl)
25+
ax.set_title(f"focal_length = {fl}")
26+
plt.tight_layout()
27+
plt.show()

‎examples/mplot3d/projections.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
========================
3+
3D plot projection types
4+
========================
5+
6+
Demonstrates the different camera projections for 3D plots, and the effects of
7+
changing the focal length for a perspective projection. Note that matplotlib
8+
corrects for the 'zoom' effect of changing the focal length.
9+
10+
An increasing focal length between 1 and +infinity "flattens" the image, while
11+
a decreasing focal length between 1 and 0 exaggerates the perspective and gives
12+
the image more apparent depth. The default focal length of 1 corresponds to a
13+
Field of View (FOV) of 90 deg. In the limiting case, a focal length of
14+
+infinity corresponds to an orthographic projection after correction of the
15+
zoom effect.
16+
17+
You can calculate focal length from a FOV via the equation:
18+
focal_length = 1/tan(FOV/2)
19+
20+
Or vice versa:
21+
FOV = 2*atan(1/focal_length)
22+
"""
23+
24+
frommpl_toolkits.mplot3dimportaxes3d
25+
importmatplotlib.pyplotasplt
26+
27+
28+
fig,axs=plt.subplots(1,3,subplot_kw={'projection':'3d'})
29+
30+
# Get the test data
31+
X,Y,Z=axes3d.get_test_data(0.05)
32+
33+
# Plot the data
34+
foraxinaxs:
35+
ax.plot_wireframe(X,Y,Z,rstride=10,cstride=10)
36+
37+
# Set the orthographic projection.
38+
axs[0].set_proj_type('ortho')# FOV = 0 deg
39+
axs[0].set_title("'ortho'\nfocal_length = +∞",fontsize=10)
40+
41+
# Set the perspective projections
42+
axs[1].set_proj_type('persp')# FOV = 90 deg
43+
axs[1].set_title("'persp'\nfocal_length = 1 (default)",fontsize=10)
44+
45+
axs[2].set_proj_type('persp',focal_length=0.2)# FOV = 157.4 deg
46+
axs[2].set_title("'persp'\nfocal_length = 0.2",fontsize=10)
47+
48+
plt.show()

‎lib/mpl_toolkits/mplot3d/axes3d.py

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class Axes3D(Axes):
5656
def__init__(
5757
self,fig,rect=None,*args,
5858
elev=30,azim=-60,roll=0,sharez=None,proj_type='persp',
59-
box_aspect=None,computed_zorder=True,
59+
box_aspect=None,computed_zorder=True,focal_length=None,
6060
**kwargs):
6161
"""
6262
Parameters
@@ -103,6 +103,11 @@ def __init__(
103103
This behavior is deprecated in 3.4, the default will
104104
change to False in 3.5. The keyword will be undocumented
105105
and a non-False value will be an error in 3.6.
106+
focal_length : float, default: None
107+
For a projection type of 'persp', the focal length of the virtual
108+
camera. Must be > 0. If None, defaults to 1.
109+
The focal length can be computed from a desired Field Of View via
110+
the equation: focal_length = 1/tan(FOV/2)
106111
107112
**kwargs
108113
Other optional keyword arguments:
@@ -116,7 +121,7 @@ def __init__(
116121
self.initial_azim=azim
117122
self.initial_elev=elev
118123
self.initial_roll=roll
119-
self.set_proj_type(proj_type)
124+
self.set_proj_type(proj_type,focal_length)
120125
self.computed_zorder=computed_zorder
121126

122127
self.xy_viewLim=Bbox.unit()
@@ -1027,18 +1032,36 @@ def view_init(self, elev=None, azim=None, roll=None, vertical_axis="z"):
10271032
dict(x=0,y=1,z=2),vertical_axis=vertical_axis
10281033
)
10291034

1030-
defset_proj_type(self,proj_type):
1035+
defset_proj_type(self,proj_type,focal_length=None):
10311036
"""
10321037
Set the projection type.
10331038
10341039
Parameters
10351040
----------
10361041
proj_type : {'persp', 'ortho'}
1037-
"""
1038-
self._projection=_api.check_getitem({
1039-
'persp':proj3d.persp_transformation,
1040-
'ortho':proj3d.ortho_transformation,
1041-
},proj_type=proj_type)
1042+
The projection type.
1043+
focal_length : float, default: None
1044+
For a projection type of 'persp', the focal length of the virtual
1045+
camera. Must be > 0. If None, defaults to 1.
1046+
The focal length can be computed from a desired Field Of View via
1047+
the equation: focal_length = 1/tan(FOV/2)
1048+
"""
1049+
ifproj_type=='persp':
1050+
iffocal_lengthisNone:
1051+
self.focal_length=1
1052+
else:
1053+
iffocal_length<=0:
1054+
raiseValueError(f"focal_length ={focal_length} must be"+
1055+
" greater than 0")
1056+
self.focal_length=focal_length
1057+
elifproj_type=='ortho':
1058+
iffocal_lengthnotin (None,np.inf):
1059+
raiseValueError(f"focal_length ={focal_length} must be"+
1060+
f"None for proj_type ={proj_type}")
1061+
self.focal_length=np.inf
1062+
else:
1063+
raiseValueError(f"proj_type ={proj_type} must be in"+
1064+
f"{'persp','ortho'}")
10421065

10431066
def_roll_to_vertical(self,arr):
10441067
"""Roll arrays to match the different vertical axis."""
@@ -1094,8 +1117,21 @@ def get_proj(self):
10941117
V=np.zeros(3)
10951118
V[self._vertical_axis]=-1ifabs(elev_rad)>0.5*np.pielse1
10961119

1097-
viewM=proj3d.view_transformation(eye,R,V,roll_rad)
1098-
projM=self._projection(-self.dist,self.dist)
1120+
# Generate the view and projection transformation matrices
1121+
ifself.focal_length==np.inf:
1122+
# Orthographic projection
1123+
viewM=proj3d.view_transformation(eye,R,V,roll_rad)
1124+
projM=proj3d.ortho_transformation(-self.dist,self.dist)
1125+
else:
1126+
# Perspective projection
1127+
# Scale the eye dist to compensate for the focal length zoom effect
1128+
eye_focal=R+self.dist*ps*self.focal_length
1129+
viewM=proj3d.view_transformation(eye_focal,R,V,roll_rad)
1130+
projM=proj3d.persp_transformation(-self.dist,
1131+
self.dist,
1132+
self.focal_length)
1133+
1134+
# Combine all the transformation matrices to get the final projection
10991135
M0=np.dot(viewM,worldM)
11001136
M=np.dot(projM,M0)
11011137
returnM
@@ -1158,7 +1194,7 @@ def cla(self):
11581194
pass
11591195

11601196
self._autoscaleZon=True
1161-
ifself._projectionisproj3d.ortho_transformation:
1197+
ifself.focal_length==np.inf:
11621198
self._zmargin=rcParams['axes.zmargin']
11631199
else:
11641200
self._zmargin=0.

‎lib/mpl_toolkits/mplot3d/proj3d.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,23 +90,27 @@ def view_transformation(E, R, V, roll):
9090
returnnp.dot(Mr,Mt)
9191

9292

93-
defpersp_transformation(zfront,zback):
94-
a= (zfront+zback)/(zfront-zback)
95-
b=-2*(zfront*zback)/(zfront-zback)
96-
returnnp.array([[1,0,0,0],
97-
[0,1,0,0],
98-
[0,0,a,b],
99-
[0,0,-1,0]])
93+
defpersp_transformation(zfront,zback,focal_length):
94+
e=focal_length
95+
a=1# aspect ratio
96+
b= (zfront+zback)/(zfront-zback)
97+
c=-2*(zfront*zback)/(zfront-zback)
98+
proj_matrix=np.array([[e,0,0,0],
99+
[0,e/a,0,0],
100+
[0,0,b,c],
101+
[0,0,-1,0]])
102+
returnproj_matrix
100103

101104

102105
defortho_transformation(zfront,zback):
103106
# note: w component in the resulting vector will be (zback-zfront), not 1
104107
a=-(zfront+zback)
105108
b=-(zfront-zback)
106-
returnnp.array([[2,0,0,0],
107-
[0,2,0,0],
108-
[0,0,-2,0],
109-
[0,0,a,b]])
109+
proj_matrix=np.array([[2,0,0,0],
110+
[0,2,0,0],
111+
[0,0,-2,0],
112+
[0,0,a,b]])
113+
returnproj_matrix
110114

111115

112116
def_proj_transform_vec(vec,M):

‎lib/mpl_toolkits/tests/test_mplot3d.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,7 @@ def _test_proj_make_M():
872872
V=np.array([0,0,1])
873873
roll=0
874874
viewM=proj3d.view_transformation(E,R,V,roll)
875-
perspM=proj3d.persp_transformation(100,-100)
875+
perspM=proj3d.persp_transformation(100,-100,1)
876876
M=np.dot(perspM,viewM)
877877
returnM
878878

@@ -1037,6 +1037,13 @@ def test_unautoscale(axis, auto):
10371037
np.testing.assert_array_equal(get_lim(), (-0.5,0.5))
10381038

10391039

1040+
@mpl3d_image_comparison(['axes3d_focal_length.png'],remove_text=False)
1041+
deftest_axes3d_focal_length():
1042+
fig,axs=plt.subplots(1,2,subplot_kw={'projection':'3d'})
1043+
axs[0].set_proj_type('persp',focal_length=np.inf)
1044+
axs[1].set_proj_type('persp',focal_length=0.15)
1045+
1046+
10401047
@mpl3d_image_comparison(['axes3d_ortho.png'],remove_text=False)
10411048
deftest_axes3d_ortho():
10421049
fig=plt.figure()

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp