Python可視化實戰(zhàn)從矩陣乘法到圖像仿射變換的線性代數(shù)之旅
前言
線性代數(shù)是理解空間變換和數(shù)據(jù)結(jié)構(gòu)的核心數(shù)學工具,其幾何意義往往隱藏在抽象的矩陣運算背后。隨著計算機圖形學和人工智能的發(fā)展,將線性代數(shù)概念轉(zhuǎn)化為可視化形式成為揭示其本質(zhì)的關(guān)鍵手段。本文通過Python代碼實現(xiàn)一系列可視化工具,涵蓋矩陣變換、特征向量分析和圖像仿射變換,旨在以直觀的方式展現(xiàn)線性代數(shù)在幾何變換、數(shù)據(jù)降維和計算機視覺中的應(yīng)用,幫助讀者建立“代數(shù)運算-幾何直觀-工程應(yīng)用”的完整認知鏈條。
一、基本概念回顧
1. 矩陣變換的幾何本質(zhì)
矩陣是線性變換的數(shù)值載體,每個2x2矩陣對應(yīng)二維平面上的一種線性變換:
旋轉(zhuǎn)矩陣:

將向量繞原點旋轉(zhuǎn)θ\thetaθ角度。
縮放矩陣:

沿x軸和y軸分別縮放Sx倍和Sy?倍。
剪切矩陣:

沿x軸方向剪切kkk個單位。這些變換可通過矩陣乘法復(fù)合,實現(xiàn)復(fù)雜的空間形變。
2. 特征向量與特征值
對于矩陣AAA,若存在非零向量v\mathbf{v}v和標量λ\lambdaλ使得Av=λvA\mathbf{v} = \lambda\mathbf{v}Av=λv,則v\mathbf{v}v稱為特征向量,λ\lambdaλ為特征值。幾何上,特征向量是變換中僅發(fā)生縮放而不改變方向的特殊向量,特征值表示縮放比例。對稱矩陣的特征向量互相正交,可張成整個空間(如主成分分析PCA的理論基礎(chǔ))。
3. 仿射變換與圖像映射
仿射變換是線性變換與平移的復(fù)合,表達式為y=Ax+b\mathbf{y} = A\mathbf{x} + \mathbfy=Ax+b。在計算機視覺中,圖像仿射變換通過矩陣AAA實現(xiàn)旋轉(zhuǎn)、縮放、剪切等操作,需通過反向映射(逆矩陣運算)將變換后的像素坐標映射回原始圖像采樣,避免像素丟失或重疊。
二、矩陣變換可視化工具
1. 向量與形狀變換演示
# 向量旋轉(zhuǎn)示例(90度旋轉(zhuǎn)矩陣) v = [1, 2] M_rotation = np.array([[0, -1], [1, 0]]) plot_vector_transformation(v, M_rotation, "vector_rotation.png")
可視化效果:藍色箭頭為原始向量,紅色箭頭為變換后向量,坐標系自動適配向量范圍,矩陣以文本形式顯示于右上角。
2. 多種變換效果對比
plot_matrix_effects("matrix_effects.png")
四宮格展示:
- 旋轉(zhuǎn):保持形狀不變,僅改變方向;
- 縮放:x軸拉長1.5倍,y軸壓縮0.7倍;
- 剪切:底邊固定,頂部沿x軸偏移0.8個單位;
- 投影:y坐標清零,形狀坍塌為x軸上的線段。
三、特征向量幾何意義演示
靜態(tài)可視化:單位圓到橢圓的變換
A = np.array([[3, 1], [1, 2]]) # 對稱矩陣,特征向量正交 plot_eigenvectors(A, "eigenvectors_symmetric.png")
關(guān)鍵元素:
- 藍色單位圓經(jīng)矩陣AAA變換為紅色橢圓;
- 綠色和品紅色箭頭為特征向量,變換后方向不變,長度由特征值(λ1=3.30\lambda_1=3.30λ1?=3.30, λ2=1.70\lambda_2=1.70λ2?=1.70)決定;
- 特征向量方向?qū)?yīng)橢圓的長軸和短軸。
動態(tài)演示:插值變換過程
create_eigenvector_animation(A, "eigenvector_animation.gif")
動畫邏輯:從單位矩陣(無變換)逐漸插值到目標矩陣AAA,觀察單位圓如何逐步變形為橢圓,特征向量始終保持方向不變,直觀展現(xiàn)“特征向量是變換的主方向”這一本質(zhì)。
四、計算機視覺案例:圖像仿射變換
1. 靜態(tài)變換:縮放與旋轉(zhuǎn)復(fù)合
# 30度旋轉(zhuǎn)+0.8倍縮放矩陣
theta = np.pi/6
M_scale_rotate = np.array([
[0.8*np.cos(theta), -0.8*np.sin(theta)],
[0.8*np.sin(theta), 0.8*np.cos(theta)]
])
plot_image_affine_transform("sample_image.jpg", M_scale_rotate, "image_affine_transform.png")
實現(xiàn)細節(jié):
- 反向映射:通過逆矩陣將變換后坐標映射回原始圖像采樣;
- 網(wǎng)格線對比:紅色網(wǎng)格為原始圖像坐標,藍色網(wǎng)格為變換后坐標,展示空間形變。
2. 動態(tài)旋轉(zhuǎn)動畫
create_image_rotation_animation("sample_image.jpg", "image_rotation.gif")
技術(shù)要點:
- 每幀計算旋轉(zhuǎn)矩陣并更新圖像坐標;
- 自動計算旋轉(zhuǎn)后圖像邊界,避免黑邊拉伸;
- 近鄰插值保持像素完整性(可擴展為雙線性插值提升畫質(zhì))。
五、生成圖像說明
向量變換圖 (vector_rotation.png):
展示向量在矩陣變換前后的變化
藍色箭頭表示原始向量
紅色箭頭表示變換后向量
包含變換矩陣的數(shù)學表示

形狀變換圖 (triangle_shear.png):
展示三角形在剪切變換下的形變
藍色輪廓表示原始形狀
紅色虛線輪廓表示變換后形狀
直觀展示矩陣變換對幾何形狀的影響

矩陣變換效果圖 (matrix_effects.png):
四宮格對比不同矩陣變換效果
包含旋轉(zhuǎn)、縮放、剪切和投影
每個子圖顯示對應(yīng)的變換矩陣

特征向量圖 (eigenvectors_symmetric.png):
藍色圓表示單位圓
紅色橢圓表示矩陣變換后的結(jié)果
綠色和品紅色箭頭表示特征向量
展示特征向量在變換后保持方向不變

特征向量動畫 (eigenvector_animation.gif):
動態(tài)展示單位圓到橢圓的變換過程
特征向量在變換過程中保持方向
特征值表示特征向量的縮放比例

圖像仿射變換圖 (image_affine_transform.png):
并排顯示原始圖像和變換后圖像
網(wǎng)格線展示變換的空間映射關(guān)系
包含變換矩陣的數(shù)學表示

圖像旋轉(zhuǎn)動畫 (image_rotation.gif):
展示圖像在二維平面中的旋轉(zhuǎn)過程
演示仿射變換在計算機視覺中的應(yīng)用
展示線性變換對像素位置的映射

六、教學要點
矩陣變換幾何解釋:
- 矩陣作為線性變換的數(shù)學表示
- 旋轉(zhuǎn)、縮放、剪切和投影的矩陣形式
- 矩陣乘法與向量變換的關(guān)系
特征值與特征向量:
- 特征向量在變換中保持方向不變
- 特征值表示特征向量的縮放因子
- 特征分解的幾何意義
計算機視覺應(yīng)用:
- 仿射變換的圖像處理應(yīng)用
- 圖像旋轉(zhuǎn)、縮放和剪切的實現(xiàn)
- 反向映射與圖像重采樣技術(shù)
編程實現(xiàn)技巧:
- 使用NumPy進行矩陣運算
- Matplotlib幾何形狀繪制
- 圖像處理與坐標映射
- 動態(tài)變換的動畫實現(xiàn)
本節(jié)代碼提供了強大的線性代數(shù)可視化工具,從基礎(chǔ)的向量變換到復(fù)雜的圖像仿射變換,直觀展示了線性代數(shù)的核心概念。特征向量動畫和圖像變換案例揭示了數(shù)學在計算機視覺中的實際應(yīng)用價值。
總結(jié)
本文通過可視化工具將線性代數(shù)的抽象概念轉(zhuǎn)化為直觀圖像,從向量變換的幾何直觀,到特征向量的物理意義,再到圖像仿射變換的工程實現(xiàn),構(gòu)建了“理論-代碼-應(yīng)用”的完整學習路徑。讀者可通過調(diào)整代碼中的矩陣參數(shù),觀察不同變換效果,深入理解線性代數(shù)在計算機圖形學、機器學習等領(lǐng)域的基礎(chǔ)作用。
完整代碼
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Polygon
from matplotlib.animation import FuncAnimation
from matplotlib import patches
from PIL import Image
import os
# ---------------------- 全局配置 ----------------------
plt.rcParams["font.sans-serif"] = ["SimHei"] # 設(shè)置中文字體為黑體
plt.rcParams["axes.unicode_minus"] = False # 正確顯示負號
plt.rcParams["mathtext.fontset"] = "cm" # 設(shè)置數(shù)學字體為Computer Modern
# ====================== 矩陣變換可視化工具 ======================
def plot_vector_transformation(v, M, output_path="vector_transformation.png"):
"""
可視化向量經(jīng)過矩陣變換后的效果
參數(shù):
v: 原始向量 [x, y]
M: 2x2變換矩陣
output_path: 輸出文件路徑
"""
plt.figure(figsize=(10, 8), dpi=130)
ax = plt.gca()
# 原始向量
origin = np.array([0, 0])
v = np.array(v)
ax.quiver(
*origin,
*v,
scale=1,
scale_units="xy",
angles="xy",
color="b",
width=0.01,
label=f"原始向量: ({v[0]}, {v[1]})",
)
# 變換后的向量
v_transformed = M @ v
ax.quiver(
*origin,
*v_transformed,
scale=1,
scale_units="xy",
angles="xy",
color="r",
width=0.01,
label=f"變換后: ({v_transformed[0]:.2f}, {v_transformed[1]:.2f})",
)
# 設(shè)置坐標系
max_val = max(np.abs(v).max(), np.abs(v_transformed).max()) * 1.2
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)
# 添加網(wǎng)格和樣式
ax.grid(True, linestyle="--", alpha=0.6)
ax.set_aspect("equal")
ax.spines["left"].set_position("zero")
ax.spines["bottom"].set_position("zero")
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# 使用簡單的文本表示矩陣
matrix_text = (
f"變換矩陣:\n" f"[{M[0,0]:.2f}, {M[0,1]:.2f}]\n" f"[{M[1,0]:.2f}, {M[1,1]:.2f}]"
)
plt.text(
0.05,
0.95,
matrix_text,
transform=ax.transAxes,
fontsize=14,
bbox=dict(facecolor="white", alpha=0.8),
)
# 設(shè)置標題和圖例
plt.title("向量矩陣變換", fontsize=16, pad=20)
plt.legend(loc="best", fontsize=12)
# 保存圖像
plt.savefig(output_path, bbox_inches="tight")
plt.close()
print(f"向量變換圖已保存至: {output_path}")
def plot_shape_transformation(vertices, M, output_path="shape_transformation.png"):
"""
可視化形狀經(jīng)過矩陣變換后的效果
參數(shù):
vertices: 形狀頂點列表 [[x1,y1], [x2,y2], ...]
M: 2x2變換矩陣
output_path: 輸出文件路徑
"""
plt.figure(figsize=(10, 8), dpi=130)
ax = plt.gca()
# 原始形狀
shape_original = Polygon(
vertices,
closed=True,
fill=False,
edgecolor="b",
linewidth=2.5,
label="原始形狀",
)
ax.add_patch(shape_original)
# 變換后的形狀
vertices_transformed = np.array(vertices) @ M.T
shape_transformed = Polygon(
vertices_transformed,
closed=True,
fill=False,
edgecolor="r",
linewidth=2.5,
linestyle="--",
label="變換后形狀",
)
ax.add_patch(shape_transformed)
# 設(shè)置坐標系
all_points = np.vstack([vertices, vertices_transformed])
max_val = np.abs(all_points).max() * 1.2
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)
# 添加網(wǎng)格和樣式
ax.grid(True, linestyle="--", alpha=0.6)
ax.set_aspect("equal")
ax.spines["left"].set_position("zero")
ax.spines["bottom"].set_position("zero")
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# 使用簡單的文本表示矩陣
matrix_text = (
f"變換矩陣:\n" f"[{M[0,0]:.2f}, {M[0,1]:.2f}]\n" f"[{M[1,0]:.2f}, {M[1,1]:.2f}]"
)
plt.text(
0.05,
0.95,
matrix_text,
transform=ax.transAxes,
fontsize=14,
bbox=dict(facecolor="white", alpha=0.8),
)
# 設(shè)置標題和圖例
plt.title("形狀矩陣變換", fontsize=16, pad=20)
plt.legend(loc="best", fontsize=12)
# 保存圖像
plt.savefig(output_path, bbox_inches="tight")
plt.close()
print(f"形狀變換圖已保存至: {output_path}")
def plot_matrix_effects(output_path="matrix_effects.png"):
"""
可視化不同矩陣變換效果:旋轉(zhuǎn)、縮放、剪切、投影
"""
# 創(chuàng)建基本形狀 (三角形)
vertices = np.array([[0, 0], [1, 0], [0.5, 1], [0, 0]])
# 定義變換矩陣
transformations = {
"旋轉(zhuǎn)": np.array(
[
[np.cos(np.pi / 4), -np.sin(np.pi / 4)],
[np.sin(np.pi / 4), np.cos(np.pi / 4)],
]
),
"縮放": np.array([[1.5, 0], [0, 0.7]]),
"剪切": np.array([[1, 0.8], [0, 1]]),
"投影": np.array([[1, 0], [0, 0]]), # 投影到x軸
}
fig, axs = plt.subplots(2, 2, figsize=(14, 12), dpi=130)
fig.suptitle("基本矩陣變換效果", fontsize=20, y=0.95)
for ax, (title, M) in zip(axs.flat, transformations.items()):
# 原始形狀
shape_original = Polygon(
vertices, closed=True, fill=False, edgecolor="b", linewidth=2, alpha=0.7
)
ax.add_patch(shape_original)
# 變換后的形狀
vertices_transformed = vertices @ M.T
shape_transformed = Polygon(
vertices_transformed, closed=True, fill=False, edgecolor="r", linewidth=2.5
)
ax.add_patch(shape_transformed)
# 設(shè)置坐標系
all_points = np.vstack([vertices, vertices_transformed])
max_val = np.abs(all_points).max() * 1.2
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)
# 添加網(wǎng)格和樣式
ax.grid(True, linestyle="--", alpha=0.5)
ax.set_aspect("equal")
ax.spines["left"].set_position("zero")
ax.spines["bottom"].set_position("zero")
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# 使用簡單的文本表示矩陣
matrix_text = f"[{M[0,0]:.2f}, {M[0,1]:.2f}]\n[{M[1,0]:.2f}, {M[1,1]:.2f}]"
ax.set_title(f"{title}\n{matrix_text}", fontsize=14)
plt.tight_layout(pad=3.0)
plt.savefig(output_path, bbox_inches="tight")
plt.close()
print(f"矩陣變換效果圖已保存至: {output_path}")
# ====================== 特征向量幾何意義演示 ======================
def plot_eigenvectors(A, output_path="eigenvectors.png"):
"""
可視化矩陣的特征向量和特征值
參數(shù):
A: 2x2矩陣
output_path: 輸出文件路徑
"""
# 計算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
plt.figure(figsize=(10, 8), dpi=130)
ax = plt.gca()
# 創(chuàng)建單位圓
theta = np.linspace(0, 2 * np.pi, 100)
circle_x = np.cos(theta)
circle_y = np.sin(theta)
ax.plot(circle_x, circle_y, "b-", alpha=0.5, label="單位圓")
# 變換后的橢圓
ellipse_points = np.column_stack([circle_x, circle_y]) @ A.T
ax.plot(ellipse_points[:, 0], ellipse_points[:, 1], "r-", label="變換后橢圓")
# 繪制特征向量
colors = ["g", "m"]
for i, (eigenvalue, eigenvector) in enumerate(zip(eigenvalues, eigenvectors.T)):
# 原始特征向量
ax.quiver(
0,
0,
*eigenvector,
scale=1,
scale_units="xy",
angles="xy",
color=colors[i],
width=0.015,
label=f"特征向量 {i+1}: ({eigenvector[0]:.2f}, {eigenvector[1]:.2f})",
)
# 變換后的特征向量
transformed_vector = A @ eigenvector
ax.quiver(
0,
0,
*transformed_vector,
scale=1,
scale_units="xy",
angles="xy",
color=colors[i],
width=0.015,
alpha=0.5,
linestyle="--",
)
# 標記特征值
ax.text(
transformed_vector[0] * 1.1,
transformed_vector[1] * 1.1,
f"λ={eigenvalue:.2f}",
color=colors[i],
fontsize=12,
)
# 設(shè)置坐標系
max_val = max(np.abs(ellipse_points).max(), 1.5)
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)
# 添加網(wǎng)格和樣式
ax.grid(True, linestyle="--", alpha=0.6)
ax.set_aspect("equal")
ax.spines["left"].set_position("zero")
ax.spines["bottom"].set_position("zero")
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# 使用簡單的文本表示矩陣
matrix_text = (
f"矩陣:\n"
f"[{A[0,0]:.2f}, {A[0,1]:.2f}]\n"
f"[{A[1,0]:.2f}, {A[1,1]:.2f}]\n\n"
f"特征值: {eigenvalues[0]:.2f}, {eigenvalues[1]:.2f}"
)
plt.text(
0.05,
0.95,
matrix_text,
transform=ax.transAxes,
fontsize=14,
bbox=dict(facecolor="white", alpha=0.8),
)
# 設(shè)置標題和圖例
plt.title("特征向量與特征值幾何意義", fontsize=16, pad=20)
plt.legend(loc="best", fontsize=11)
# 保存圖像
plt.savefig(output_path, bbox_inches="tight")
plt.close()
print(f"特征向量圖已保存至: {output_path}")
def create_eigenvector_animation(A, output_path="eigenvector_animation.gif"):
"""
創(chuàng)建特征向量動畫:展示單位圓到橢圓的變換過程
"""
fig, ax = plt.subplots(figsize=(8, 8), dpi=120)
# 計算特征值和特征向量
eigenvalues, eigenvectors = np.linalg.eig(A)
# 單位圓
theta = np.linspace(0, 2 * np.pi, 100)
circle_points = np.column_stack([np.cos(theta), np.sin(theta)])
# 創(chuàng)建初始圖形
(circle,) = ax.plot(circle_points[:, 0], circle_points[:, 1], "b-", alpha=0.7)
(ellipse,) = ax.plot([], [], "r-", linewidth=2)
# 特征向量線
vec_lines = []
for i, eigenvector in enumerate(eigenvectors.T):
color = "g" if i == 0 else "m"
(line,) = ax.plot([], [], color=color, linewidth=2.5, label=f"特征向量 {i+1}")
vec_lines.append(line)
# 設(shè)置坐標系
max_val = max(np.abs(circle_points).max(), np.abs(eigenvectors).max()) * 1.5
ax.set_xlim(-max_val, max_val)
ax.set_ylim(-max_val, max_val)
# 添加網(wǎng)格和樣式
ax.grid(True, linestyle="--", alpha=0.5)
ax.set_aspect("equal")
ax.spines["left"].set_position("zero")
ax.spines["bottom"].set_position("zero")
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
# 使用簡單的文本表示矩陣
matrix_text = (
f"矩陣 A:\n" f"[{A[0,0]:.2f}, {A[0,1]:.2f}]\n" f"[{A[1,0]:.2f}, {A[1,1]:.2f}]"
)
text = ax.text(
0.05,
0.95,
matrix_text,
transform=ax.transAxes,
fontsize=12,
bbox=dict(facecolor="white", alpha=0.8),
)
# 設(shè)置標題和圖例
plt.title("特征向量變換動畫", fontsize=14, pad=15)
plt.legend(loc="best")
# 動畫更新函數(shù)
def update(t):
# 插值矩陣: 從單位矩陣到目標矩陣
M = np.eye(2) * (1 - t) + A * t
# 變換圓點
transformed_points = circle_points @ M.T
ellipse.set_data(transformed_points[:, 0], transformed_points[:, 1])
# 更新特征向量線
for i, eigenvector in enumerate(eigenvectors.T):
vec_transformed = M @ eigenvector
vec_lines[i].set_data([0, vec_transformed[0]], [0, vec_transformed[1]])
# 更新插值值
text.set_text(f"{matrix_text}\n\n插值: {t:.2f}")
return ellipse, *vec_lines, text
# 創(chuàng)建動畫
anim = FuncAnimation(
fig, update, frames=np.linspace(0, 1, 50), interval=100, blit=True
)
# 保存GIF
anim.save(output_path, writer="pillow", fps=10)
plt.close()
print(f"特征向量動畫已保存至: {output_path}")
# ====================== 計算機視覺案例 ======================
def plot_image_affine_transform(
image_path, transform_matrix, output_path="image_transform.png"
):
"""
應(yīng)用仿射變換到圖像并可視化結(jié)果
參數(shù):
image_path: 圖像文件路徑
transform_matrix: 2x2變換矩陣
output_path: 輸出文件路徑
"""
# 加載圖像
img = Image.open(image_path).convert("RGB")
img_array = np.array(img)
# 創(chuàng)建圖像網(wǎng)格
height, width = img_array.shape[:2]
x = np.linspace(0, width, 10)
y = np.linspace(0, height, 10)
X, Y = np.meshgrid(x, y)
# 創(chuàng)建繪圖
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 8), dpi=130)
fig.suptitle("圖像仿射變換", fontsize=20, y=0.98)
# 原始圖像
ax1.imshow(img_array)
ax1.set_title("原始圖像", fontsize=16)
ax1.axis("off")
# 創(chuàng)建網(wǎng)格點
points = np.column_stack([X.ravel(), Y.ravel()])
# 應(yīng)用變換
transformed_points = points @ transform_matrix.T
# 變換后圖像
# 由于變換可能改變圖像范圍,我們需要調(diào)整坐標
min_x, min_y = transformed_points.min(axis=0)
max_x, max_y = transformed_points.max(axis=0)
# 創(chuàng)建新圖像網(wǎng)格
new_width = int(max_x - min_x)
new_height = int(max_y - min_y)
# 創(chuàng)建新圖像數(shù)組
new_img = np.zeros((new_height, new_width, 3), dtype=np.uint8)
# 反向映射: 對于新圖像的每個點,找到原始圖像中的對應(yīng)點
inv_matrix = np.linalg.inv(transform_matrix)
for y_new in range(new_height):
for x_new in range(new_width):
# 將新圖像坐標轉(zhuǎn)換回原始圖像坐標
orig_x, orig_y = np.array([x_new + min_x, y_new + min_y]) @ inv_matrix.T
# 如果坐標在原始圖像范圍內(nèi),則采樣顏色
if 0 <= orig_x < width and 0 <= orig_y < height:
# 簡單最近鄰插值
orig_x_int = int(orig_x)
orig_y_int = int(orig_y)
new_img[y_new, x_new] = img_array[orig_y_int, orig_x_int]
# 顯示變換后圖像
ax2.imshow(new_img, extent=[min_x, max_x, max_y, min_y])
ax2.set_title("變換后圖像", fontsize=16)
# 添加變換網(wǎng)格
for i in range(len(y)):
ax1.plot(x, [y[i]] * len(x), "r-", alpha=0.3)
for j in range(len(x)):
ax1.plot([x[j]] * len(y), y, "r-", alpha=0.3)
# 繪制變換后的網(wǎng)格
T_grid = transformed_points.reshape(X.shape[0], X.shape[1], 2)
for i in range(len(y)):
ax2.plot(T_grid[i, :, 0], T_grid[i, :, 1], "b-", alpha=0.5)
for j in range(len(x)):
ax2.plot(T_grid[:, j, 0], T_grid[:, j, 1], "b-", alpha=0.5)
ax2.axis("off")
# 使用簡單的文本表示矩陣
matrix_text = (
f"變換矩陣:\n"
f"[{transform_matrix[0,0]:.2f}, {transform_matrix[0,1]:.2f}]\n"
f"[{transform_matrix[1,0]:.2f}, {transform_matrix[1,1]:.2f}]"
)
plt.figtext(
0.5,
0.02,
matrix_text,
ha="center",
fontsize=14,
bbox=dict(facecolor="white", alpha=0.8),
)
plt.tight_layout(pad=3.0)
plt.savefig(output_path, bbox_inches="tight")
plt.close()
print(f"圖像變換圖已保存至: {output_path}")
def create_image_rotation_animation(image_path, output_path="image_rotation.gif"):
"""
創(chuàng)建圖像旋轉(zhuǎn)動畫
"""
# 加載圖像
img = Image.open(image_path).convert("RGB")
img_array = np.array(img)
height, width = img_array.shape[:2]
# 創(chuàng)建圖形
fig = plt.figure(figsize=(8, 8), dpi=120)
ax = fig.add_subplot(111)
ax.set_title("圖像旋轉(zhuǎn)動畫", fontsize=14)
ax.axis("off")
# 顯示原始圖像
im = ax.imshow(img_array)
# 動畫更新函數(shù)
def update(frame):
# 計算旋轉(zhuǎn)角度 (0到360度)
angle = np.radians(frame * 7.2) # 每幀旋轉(zhuǎn)7.2度,50幀完成360度
# 創(chuàng)建旋轉(zhuǎn)矩陣
rotation_matrix = np.array(
[[np.cos(angle), -np.sin(angle)], [np.sin(angle), np.cos(angle)]]
)
# 計算旋轉(zhuǎn)后的圖像尺寸
corners = np.array([[0, 0], [width, 0], [width, height], [0, height]])
rotated_corners = corners @ rotation_matrix.T
min_x, min_y = rotated_corners.min(axis=0)
max_x, max_y = rotated_corners.max(axis=0)
new_width = int(max_x - min_x)
new_height = int(max_y - min_y)
# 創(chuàng)建新圖像數(shù)組
new_img = np.zeros((new_height, new_width, 3), dtype=np.uint8)
# 反向映射
inv_matrix = np.linalg.inv(rotation_matrix)
for y_new in range(new_height):
for x_new in range(new_width):
# 將新圖像坐標轉(zhuǎn)換回原始圖像坐標
orig_x, orig_y = np.array([x_new + min_x, y_new + min_y]) @ inv_matrix.T
# 如果坐標在原始圖像范圍內(nèi),則采樣顏色
if 0 <= orig_x < width and 0 <= orig_y < height:
orig_x_int = int(orig_x)
orig_y_int = int(orig_y)
new_img[y_new, x_new] = img_array[orig_y_int, orig_x_int]
im.set_array(new_img)
im.set_extent([min_x, max_x, max_y, min_y])
return (im,)
# 創(chuàng)建動畫
anim = FuncAnimation(fig, update, frames=50, interval=100, blit=True)
# 保存GIF
anim.save(output_path, writer="pillow", fps=10)
plt.close()
print(f"圖像旋轉(zhuǎn)動畫已保存至: {output_path}")
# ====================== 使用示例 ======================
if __name__ == "__main__":
# 清理舊文件
for f in [
"vector_rotation.png",
"triangle_shear.png",
"matrix_effects.png",
"eigenvectors_symmetric.png",
"eigenvector_animation.gif",
"image_affine_transform.png",
"image_rotation.gif",
"temp_image.jpg",
]:
if os.path.exists(f):
os.remove(f)
# 1. 向量變換示例(90度旋轉(zhuǎn))
v = [1, 2]
M_rotation = np.array([[0, -1], [1, 0]])
plot_vector_transformation(v, M_rotation, "vector_rotation.png")
# 2. 形狀變換示例(剪切變換)
triangle = [[0, 0], [1, 0], [0.5, 1], [0, 0]]
M_shear = np.array([[1, 0.5], [0, 1]])
plot_shape_transformation(triangle, M_shear, "triangle_shear.png")
# 3. 矩陣變換效果對比
plot_matrix_effects("matrix_effects.png")
# 4. 特征向量可視化(對稱矩陣)
A = np.array([[3, 1], [1, 2]])
plot_eigenvectors(A, "eigenvectors_symmetric.png")
create_eigenvector_animation(A, "eigenvector_animation.gif")
# 5. 圖像仿射變換(自動生成臨時圖像)
image_path = "sample_image.jpg"
if not os.path.exists(image_path):
# 創(chuàng)建100x100藍色方塊圖像
img_array = np.zeros((100, 100, 3), dtype=np.uint8)
img_array[20:80, 20:80] = np.array([0, 128, 255], dtype=np.uint8)
Image.fromarray(img_array).save("temp_image.jpg")
image_path = "temp_image.jpg"
# 縮放+旋轉(zhuǎn)矩陣(30度縮放0.8倍)
theta = np.pi / 6
M_scale_rotate = np.array(
[
[0.8 * np.cos(theta), -0.8 * np.sin(theta)],
[0.8 * np.sin(theta), 0.8 * np.cos(theta)],
]
)
plot_image_affine_transform(
image_path, M_scale_rotate, "image_affine_transform.png"
)
create_image_rotation_animation(image_path, "image_rotation.gif")
print("\n所有可視化結(jié)果已生成,文件列表:")
for f in os.listdir():
if any(f.endswith(ext) for ext in [".png", ".gif"]):
print(f"- {f}")以上就是Python可視化實戰(zhàn)從矩陣乘法到圖像仿射變換的線性代數(shù)之旅的詳細內(nèi)容,更多關(guān)于Python線性代數(shù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
對python .txt文件讀取及數(shù)據(jù)處理方法總結(jié)
下面小編就為大家分享一篇對python .txt文件讀取及數(shù)據(jù)處理方法總結(jié),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-04-04
Python快速開發(fā)一個MCP服務(wù)器的實現(xiàn)示例
MCP是一個開源標準,用于將AI應(yīng)用連接到外部系統(tǒng),本文就來詳細的介紹一下Python快速開發(fā)一個MCP服務(wù)器的實現(xiàn)示例,感興趣的可以了解一下2025-12-12
python爬蟲 線程池創(chuàng)建并獲取文件代碼實例
這篇文章主要介紹了python爬蟲 線程池創(chuàng)建并獲取文件代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-09-09
一文詳解Python中的Map,Filter和Reduce函數(shù)
這篇文章主要介紹了一文詳解Python中的Map,Filter和Reduce函數(shù),本文重點介紹Python中的三個特殊函數(shù)Map,Filter和Reduce,以及如何使用它們進行代碼編程2022-08-08
Python實現(xiàn)監(jiān)控遠程主機實時數(shù)據(jù)的示例詳解
這篇文章主要為大家詳細介紹了Python如何使用Socket庫和相應(yīng)的第三方庫來監(jiān)控遠程主機的實時數(shù)據(jù),比如CPU使用率、內(nèi)存使用率、網(wǎng)絡(luò)帶寬等,感興趣的可以了解一下2023-04-04
Python3開發(fā)監(jiān)控自動化觸發(fā)聲光報警
使用python制作一個自動監(jiān)控并觸發(fā)聲光報警是不是感覺很高端,很多人都會認為只是一件很難的事情,但實際很簡單就能實現(xiàn)。2023-07-07
Python3.x+迅雷x 自動下載高分電影的實現(xiàn)方法
這篇文章主要介紹了Python3.x+迅雷x 自動下載高分電影的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-01-01

