小陈博客-个人分享 https://reinness.com/ zh-CN 理想成为大牛的小陈同学 Wed, 29 Oct 2025 14:20:00 +0800 Wed, 29 Oct 2025 14:20:00 +0800 从 requirements.txt 到 pyproject.toml:现代 Python 项目升级实战指南(含依赖分析与 Docker) https://reinness.com/posts/313 https://reinness.com/posts/313 Wed, 29 Oct 2025 14:20:00 +0800 Caleb pyproject.png

🧱 一、现实问题:requirements.txt 的“依赖泥潭”

在老项目中,常见的现象是这样的:

$ pip list
Package         Version
--------------- --------
numpy           1.24.3
pandas          2.0.1
requests        2.31.0
urllib3         2.0.4
charset-normalizer 3.2.0
idna            3.4
fastapi         0.95.2
uvicorn         0.22.0
pydantic        1.10.11
...

看起来一切正常,其实乱得很。
因为 requirements.txt 里可能只有 3~5 个包:

fastapi==0.95.2
pandas==2.0.1
uvicorn==0.22.0

pip list 却列出了几十个依赖。这时有两个关键问题:

  1. 哪些是项目自己需要的?
  2. 哪些是​依赖的依赖(transitive dependencies)

我们要在迁移前先把这些关系理清,否则迁移到 pyproject.toml 之后仍旧是混乱的。

🧩 二、精准识别“主依赖” vs “传递依赖”

1️⃣ 使用 pipdeptree 分析依赖树

安装:

pip install pipdeptree

查看依赖层级:

pipdeptree --warn silence

示例输出:

fastapi==0.95.2
  - pydantic [required: <2.0.0, installed: 1.10.11]
  - starlette [required: <1.0.0, installed: 0.27.0]
pandas==2.0.1
  - numpy [required: >=1.20.3, installed: 1.24.3]
  - python-dateutil [installed: 2.8.2]
  - pytz [installed: 2023.3]
uvicorn==0.22.0
  - click [installed: 8.1.3]
  - h11 [installed: 0.14.0]

👉 很明显,fastapipandasuvicorn 是你主动安装的;
其余如 pydanticnumpystarlette 等都是“传递依赖”。

Tip: 你可以用 pipdeptree --reverse 反查:哪个包依赖了这个库。

🧮 三、清理并锁定顶级依赖

1️⃣ 提取顶级包

pip freeze > all.txt
pipdeptree --warn silence --freeze > tree.txt

打开 tree.txt,手动(或脚本)过滤掉那些被其他包引用的依赖。

最终保留的核心依赖(即​顶级包​)写入新的 requirements.txt

fastapi==0.95.2
pandas==2.0.1
uvicorn==0.22.0

2️⃣ 验证清理是否正确

先创建个全新虚拟环境:

python -m venv cleanenv
source cleanenv/bin/activate
pip install -r requirements.txt
pipdeptree

如果依赖树和原项目行为一致,你的“核心依赖集”就整理对了。

🧭 四、迁移到 pyproject.toml + uv

安装 uv(强烈推荐):

curl -LsSf https://astral.sh/uv/install.sh | sh

初始化:

uv init

导入依赖:

uv add --from-requirements requirements.txt

查看结果(生成的 pyproject.toml):

[project]
name = "movie-analyzer"
version = "0.1.0"
dependencies = [
    "fastapi==0.95.2",
    "pandas==2.0.1",
    "uvicorn==0.22.0",
]
requires-python = ">=3.10"

🧰 五、验证和同步依赖

锁定依赖版本:

uv lock

安装依赖:

uv sync

验证运行:

uv run python main.py
uv 会自动生成 .venvuv.lock,确保依赖一致性。
即使协作者在另一台机器,也能完美复现同样的环境。

🧱 六、用 Docker 打包部署

这是最通用、最干净的构建方式。

FROM python:3.11-slim AS base

# 安装 uv
RUN pip install uv

WORKDIR /app

# 拷贝依赖文件
COPY pyproject.toml uv.lock ./

# 安装依赖
RUN uv sync --frozen --no-cache

# 拷贝源码
COPY src ./src

# 入口命令
CMD ["uv", "run", "fastapi", "run", "--host", "0.0.0.0", "--port", "8000"]

构建镜像:

docker build -t docker-test .

运行容器:

docker run -p 8000:8000 docker-test

🔍 七、附:自动识别“顶级依赖”的脚本

如果你懒得手动筛选,可以用这个 Python 脚本快速过滤:

import subprocess

def get_top_level_packages():
    output = subprocess.check_output(["pipdeptree", "--warn", "silence"]).decode()
    top_level = []
    for line in output.splitlines():
        if not line.startswith(" "):  # 顶级包
            pkg = line.split("==")[0]
            top_level.append(pkg)
    return top_level

if __name__ == "__main__":
    for pkg in get_top_level_packages():
        print(pkg)

执行:

python find_top_deps.py > clean_requirements.txt

它会自动生成仅包含“主依赖”的 requirements.txt。

⚡ 八、实战总结

步骤工具作用
依赖分析pipdeptree查出依赖关系树
顶级提取脚本 or 手动确定哪些是核心依赖
环境验证venv+pip install确认依赖可重现
现代化迁移uv init + add生成 pyproject.toml
锁定同步uv lock + sync可重复依赖管理
部署打包Docker + uv一致性环境交付

🧠 九、总结

“requirements.txt 是笔记本,而 pyproject.toml 是配置中心。”

通过 pipdeptree 清理依赖、再用 uv 统一管理,
你能把老项目彻底从“依赖泥潭”里拔出来,
同时获得更快的安装、更好的团队协作与更稳定的部署。

]]>
0 https://reinness.com/posts/313#comments https://reinness.com/feed/
用 NumPy 玩转电影评分系统:从随机数据到洞察分析 https://reinness.com/posts/302 https://reinness.com/posts/302 Fri, 24 Oct 2025 15:29:00 +0800 Caleb numpy_3.png

🎬 一、灵感来源:这片我给满分!

如果你经常在豆瓣、IMDb 上冲浪,你会发现有的人动不动就打 10 分,
也有的人连《阿凡达》都只给个 6 分。
那问题来了:

如果我们用 NumPy 来分析一群用户的打分,会发现怎样的规律?

今天,我们就做一个小实验,用 NumPy 还原一个「影评网站」的世界。


🍿 二、项目目标

我们要模拟一份影评数据表,里面包含:

  • 100 个用户
  • 20 部电影
  • 每个用户对每部电影的评分(1~10 分)

目标:用 NumPy 模拟一个 100 个用户、20 部电影的评分矩阵,
并完成平均分、最受欢迎电影、两极分化电影等分析。


🧮 三、用 NumPy 模拟一份评分矩阵

准备工作

pip install numpy matplotlib

别忘了导入:

import numpy as np
import matplotlib.pyplot as plt

展开查看代码

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(42)

num_users = 100
num_movies = 20

# 用户打分偏好:有人偏高、有人偏低
user_bias = np.random.normal(0, 1, num_users)

# 电影总体质量
movie_quality = np.random.normal(0, 1, num_movies)

# 评分生成公式 = 用户偏好 + 电影质量 + 一点随机噪声
ratings = np.add.outer(user_bias, movie_quality) + np.random.normal(0, 0.8, (num_users, num_movies))

# 归一化到 1~10 之间
ratings = 1 + 9 * (ratings - ratings.min()) / (ratings.max() - ratings.min())
ratings = np.round(ratings, 1)

输出:

[[4.9 4.6 6.3 ... 4.4 7.2 5.4]
 [4.  5.6 3.6 ... 4.4 5.2 6.2]
 [3.8 6.  6.3 ... 5.8 6.6 8.3]
 ...
 [4.8 5.  5.4 ... 5.2 6.3 5.8]
 [4.  5.3 3.9 ... 3.4 5.1 6.4]
 [4.  4.  4.1 ... 3.3 4.2 5.5]]

运行后你会得到一个 100×20 的评分矩阵,
每个数字都像是某个网友的「一己之见」。

四、核心分析:让数据开口说话 🎤

1️⃣ 每部电影的平均分

movie_avg = ratings.mean(axis=0)
print(movie_avg)

# [3.955 4.783 4.802 4.381 4.964 5.408 6.674 5.334 5.324 5.192 3.392 5.06
 5.159 7.327 5.096 5.369 5.079 4.13  6.187 5.861]

这行代码,直接把 100 个用户的打分在“电影维度”上取平均。
不需要任何循环,性能炸裂。

axis=0 → 对列求平均(即每部电影)
axis=1 → 对行求平均(即每个用户)


2️⃣ 找出最受欢迎的电影

best_movie_idx = np.argmax(movie_avg)
print(f"最受欢迎电影编号:{best_movie_idx},平均分:{movie_avg[best_movie_idx]:.2f}")

# 最受欢迎电影编号:13,平均分:7.33

3️⃣ 找出“争议最大”的电影(分数最分散)

movie_std = ratings.std(axis=0)
most_divided_idx = np.argmax(movie_std)
print(f"最两极分化电影编号:{most_divided_idx},标准差:{movie_std[most_divided_idx]:.2f}")

# 最两极分化电影编号:11,标准差:1.17

4️⃣ 哪个用户最严格?哪个最随便?

user_avg = ratings.mean(axis=1)

strict_user = np.argmin(user_avg)
easy_user = np.argmax(user_avg)

print(f"最严格用户:{strict_user},平均打分:{user_avg[strict_user]:.2f}")
print(f"最佛系用户:{easy_user},平均打分:{user_avg[easy_user]:.2f}")

# 最严格用户:74,平均打分:3.01
# 最佛系用户:6,平均打分:6.71

五、可视化评分数据 🎨

1️⃣ 平均分柱状图

plt.figure(figsize=(10,5))
plt.bar(range(num_movies), movie_avg, color='skyblue')
plt.xlabel('电影编号')
plt.ylabel('平均评分')
plt.title('各电影平均评分')
plt.show()

各电影平均评分.png

2️⃣ 评分热力图(用户 × 电影)

plt.figure(figsize=(10,6))
plt.imshow(ratings, cmap='coolwarm', aspect='auto')
plt.colorbar(label='评分')
plt.xlabel('电影编号')
plt.ylabel('用户编号')
plt.title('用户评分热力图')
plt.show()

热力图可以直观看出“哪部电影评分高”以及“谁在疯狂打高分”。

用户评分热力图.png

六、性能对比:循环 VS 向量化

import time

start = time.time()
avg_scores = []
for i in range(num_movies):
    avg_scores.append(sum(ratings[:, i]) / num_users)
print("循环耗时:", time.time() - start)

# 循环耗时: 7.82012939453125e-05
start = time.time()
movie_avg = ratings.mean(axis=0)
print("向量化耗时:", time.time() - start)

# 向量化耗时: 1.5020370483398438e-05

💡 实测差距可达 ​30~100 倍​。
循环是跑步,NumPy 是坐高铁。

七、总结:NumPy,从数组到洞察的旅程

模块技术点
数据生成np.random.randint()
聚合分析mean()std()argmax()
性能优化向量化计算
可视化matplotlib 热力图与柱状图

恭喜!到这里,你已经能独立用 NumPy 处理一份真实数据集,
从生成、分析、到可视化,闭环打通。

🏁 系列目录

  1. NumPy 入门:别再用 for 循环折磨自己了
  2. NumPy 进阶:搞懂广播机制,才能真正玩转数组
  3. NumPy 实战:用数组玩转图像与数据分析
]]>
0 https://reinness.com/posts/302#comments https://reinness.com/feed/
NumPy 进阶:搞懂广播机制,才能真正玩转数组 https://reinness.com/posts/299 https://reinness.com/posts/299 Fri, 24 Oct 2025 15:12:00 +0800 Caleb numpy_2.png

一、前情回顾:数组的“超能力”从哪来?

上一章我们已经知道,NumPy 数组能直接做加减乘除:

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
print(arr * 2)
# [ 2  4  6  8 10]

但你有没有想过,为什么这个操作不报错?

Python 原生列表可做不到这事儿。

答案就是:广播机制(Broadcasting)
NumPy 允许不同形状的数组在数学运算时“自动对齐”。

二、广播机制的核心原理

简单一句话总结:

广播就是 NumPy 在维度不匹配时自动扩展数组,使它们形状兼容。

来,我们先用一个经典例子开胃 👇

a = np.array([[1, 2, 3],
              [4, 5, 6]])

b = np.array([10, 20, 30])

print(a + b)

输出:

[14 25 36]]

看似理所当然,但实际上这背后 NumPy 悄悄做了扩展 👇

📊 广播过程可视化

数组原始形状扩展形状
a(2, 3)(2, 3)
b(3,)(1, 3) → (2, 3)

NumPy 自动在前面补了一个维度 (1, 3),然后复制两次让它变成 (2, 3),于是它们形状一致,就能运算了!

三、广播规则总结

判断两个数组是否能广播,其实就三步:

  1. 尾部维度 开始对齐;
  2. 每个维度要么相等,要么其中一个是 1;
  3. 不满足规则时,广播失败(直接报错)。

A.shape = (4, 1, 3)
B.shape = (1, 5, 3)
# 结果 -> (4, 5, 3)
A.shape = (3, 2)
B.shape = (2, 3)
# 维度都不匹配,直接报错!

四、copy vs view:NumPy 的内存诡计

你可能遇到过这种情况 👇

arr = np.arange(6).reshape(2, 3)
sub = arr[:, 1:]
sub[0, 0] = 99

print(arr)

输出:

[[ 0 99  2]
 [ 3  4  5]]

为什么我改了 subarr 也跟着变了?

那是因为 NumPy 返回的不是拷贝(copy),而是视图(view)
两者共用底层内存,一改俱改。

🔍 如何判断是否为 view?

sub.base is arr  # True -> 表示 sub 是 arr 的视图

如果想要真正复制一份独立的副本:

sub = arr[:, 1:].copy()

五、性能优化:让你的代码飞起来 🚀

1️⃣ 避免循环

# 慢
for i in range(len(arr)):
    arr[i] *= 2

# 快
arr *= 2

循环会触发 Python 解释器逐元素执行;
而向量化运算在底层 C 实现中批量计算,速度往往快几十倍。


2️⃣ 使用 numexpr 进行表达式优化

import numexpr as ne

a = np.random.rand(10_000_000)
b = np.random.rand(10_000_000)
c = np.random.rand(10_000_000)

# 普通写法
res1 = a * b + c

# numexpr 写法
res2 = ne.evaluate("a * b + c")

numexpr 会自动进行并行和缓存优化,CPU 利用率更高。


3️⃣ 就地操作(In-place)

arr *= 2  # 就地修改,不创建新数组

这种写法可以节省内存分配,尤其在大数组时效果显著。

六、实用技巧锦集 🧰

场景方法
随机数np.random.rand(3,3)
拼接np.concatenate((a,b), axis=0)
转置arr.T
排序np.sort(arr)
求唯一值np.unique(arr)

这些都是项目中常用的“手上活儿”。

七、性能演示:到底快多少?

import time

a = list(range(10_000_000))
start = time.time()
b = [x * 2 for x in a]
print("耗时:", time.time() - start)

# 耗时: 0.13944315910339355
import numpy as np
import time

a = np.arange(10_000_000)
start = time.time()
b = a * 2
print("耗时:", time.time() - start)

# 耗时: 0.05504918098449707

实测下来:NumPy 版本比纯 Python 快 30~100 倍
一旦数据量大,优势就像核弹一样炸裂 💥。

八、小结

模块要点
广播机制自动扩展维度,按尾部对齐
内存视图view 共用底层数据,copy 独立内存
性能优化向量化、numexpr、就地操作
实用技巧拼接、随机数、转置、排序、唯一值

下一篇(第 3 篇)我们将正式上手项目实战:
用 NumPy 做 图像处理与数据分析,把理论变成肌肉记忆。

🏁 系列目录

  1. NumPy 入门:别再用 for 循环折磨自己了
  2. NumPy 进阶:搞懂广播机制,才能真正玩转数组
  3. 🔜 NumPy 实战:用数组玩转图像与数据分析
]]>
0 https://reinness.com/posts/299#comments https://reinness.com/feed/
NumPy 入门:别再用 for 循环折磨自己了 https://reinness.com/posts/293 https://reinness.com/posts/293 Fri, 24 Oct 2025 14:36:00 +0800 Caleb numpy_1.png

一、为什么我们需要 NumPy?

还记得第一次在 Python 里写一堆 for 循环的时候的感受吗?
一行行循环、一层层嵌套,看起来像炼丹。

# 把列表每个元素平方
data = [1, 2, 3, 4, 5]
result = []
for x in data:
    result.append(x ** 2)
print(result)

结果当然是 [1, 4, 9, 16, 25]
但执行速度慢得让人想弃疗。

这时候登场的主角就是 NumPy(Numerical Python)
一个让你在 Python 里写出媲美 C 语言性能的科学计算库。

二、NumPy 的安装与导入

直接用 pip 安装就行:

pip install numpy

安装好后,你一般都会用一个简称:

import numpy as np

如果你看到别人写 np.array(),别慌,这只是大家约定俗成的简写。

三、创建数组的几种方式

NumPy 的核心是 ndarray(N 维数组)。
它像 Python 的列表,但更高效、更方便。

🧩 1. 从列表创建

import numpy as np

arr = np.array([1, 2, 3, 4, 5])
print(arr)

输出:

[1 2 3 4 5]

注意到没?打印的时候没有逗号——这就是 NumPy 的“数组风格”。

🧮 2. 创建特殊数组

np.zeros((2, 3))   # 2x3 的全零矩阵
np.ones((3, 3))    # 全一矩阵
np.eye(3)          # 单位矩阵
np.arange(0, 10, 2) # 等差数组 [0, 2, 4, 6, 8]
np.linspace(0, 1, 5) # 等分数组 [0., 0.25, 0.5, 0.75, 1.]

这些函数会在之后的项目里频繁使用。

四、数组运算的魔法

还在写循环?别傻了。
NumPy 可以直接对数组进行“广播式运算”:

data = [1, 2, 3, 4, 5]
result = []
for x in data:
    result.append(x * 2)
print(result)

输出:

[2, 4, 6, 8, 10]
import numpy as np

arr = np.array([1, 2, 3, 4, 5])
print(arr * 2)

输出:

[2, 4, 6, 8, 10]

是不是神清气爽?
向量化运算让你不写循环也能搞定所有数学操作。

五、索引与切片

NumPy 的索引比 Python list 强大得多:

arr = np.array([[1, 2, 3], [4, 5, 6]])

print(arr[0, 1])   # 访问第1行第2列 -> 2
print(arr[:, 1])   # 所有行的第2列 -> [2 5]
print(arr[1, :])   # 第2行 -> [4 5 6]

还可以用布尔索引:

arr[arr > 3]

六、常用数学函数

NumPy 内置了几乎所有数学函数:

np.mean(arr)     # 平均值
np.sum(arr)      # 求和
np.max(arr)      # 最大值
np.min(arr)      # 最小值
np.std(arr)      # 标准差

七、数组形状与维度

arr = np.array([[1,2,3], [4,5,6]])
arr.shape   # (2, 3)
arr.ndim    # 维度数 -> 2
arr.size    # 元素总数 -> 6

改变形状也很方便:

arr.reshape(3, 2)
⚠️ 注意:reshape 不会修改原数组,而是返回一个新数组。

八、小结

知识点说明
np.array()创建数组
np.zeros()/np.ones()创建特殊矩阵
arr.shape/arr.ndim查看形状与维度
arr * 2/arr + arr向量化运算
arr[arr > 3]布尔筛选

下一篇我们将深入 NumPy 的“隐藏魔法”——广播机制、内存布局与性能优化。
别眨眼,数组还能比你想的更聪明。

🏁 系列目录

  1. NumPy 入门:别再用 for 循环折磨自己了
  2. NumPy 进阶:搞懂广播机制,才能真正玩转数组
  3. 🔜 NumPy 实战:用数组玩转图像与数据分析
]]>
0 https://reinness.com/posts/293#comments https://reinness.com/feed/
FFmpeg 从入门到熟练:玩转视频剪辑、压缩与滤镜的神级工具 https://reinness.com/posts/276 https://reinness.com/posts/276 Wed, 06 Aug 2025 11:28:00 +0800 Caleb
适合人群:剪辑视频、压缩清晰度、搞图搞音、做自媒体的你,全都能用上!不需要编程经验,会复制命令行就行。

👉 在线体验 FFmpeg

你是否经常遇到这种情况?
  • 想把 .mov 转成 .mp4,结果文件暴涨 2 倍?
  • 用某宝压缩视频,画质一塌糊涂?
  • 剪视频软件动辄 2G,加载还卡顿?
  • 手里有几十个视频要统一转码,不想手动点鼠标?

只需一个命令就能搞定这些事的神奇工具,就是——FFmpeg

🛠️ 第 1 节:安装 FFmpeg

各平台安装方式

Windows 安装

  1. FFmpeg 官网下载页 → 选择 Windows 版本
  2. 推荐下载 gyaan/ffmpeg-release 或 gyan.dev 提供的 zip 包(含完整二进制)
  3. 解压后,将 bin 目录加入系统环境变量 Path
  4. 打开 CMD,输入 ffmpeg -version 检查是否安装成功
🎯 不想设置环境变量?可以用绿色版,直接进入 bin 目录运行。

macOS 安装

推荐使用 Homebrew:

brew install ffmpeg

如需支持更多格式(如 x264、fdk-aac):

brew install ffmpeg --with-fdk-aac --with-libvpx --with-x265

Linux 安装

Ubuntu/Debian:

sudo apt update
sudo apt install ffmpeg

CentOS/Fedora:

sudo dnf install ffmpeg

安装完成后,用这个命令验证版本:

ffmpeg -version

📐 第 2 节:命令结构快速理解

我们来看一条最基础的 FFmpeg 命令:

ffmpeg -i input.mp4 -vf scale=1280:720 -c:v libx264 output.mp4

命令逐项解析

  • -i input.mp4:输入文件
  • -vf scale=1280:720:缩放
  • -c:v libx264:使用 H.264 编码器
  • output.mp4:输出文件名

📌 小技巧:最小命令也能用!
你甚至可以不写任何参数:

ffmpeg -i input.mp4 output.avi

它会自动根据扩展名判断格式,非常适合小白。

🔁 第 3 节:常用操作速查

本节适合「剪一点」、「转一下」、「压一压」的视频需求,全是能立即拿来用的命令。

🎞️ 格式转换(转码)

ffmpeg -i input.mp4 -vf "fps=10,scale=480:-1:flags=lanczos" -c:v gif output.gif
  • fps=10:控制帧率,越低越小巧
  • scale=480:-1:宽 480,自动等比缩放
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset fast output.mp4
  • -crf 数值越低画质越好(0~51,推荐 18~28)

✂️ 视频剪辑

ffmpeg -ss 00:00:30 -i input.mp4 -t 10 -c copy output.mp4
  • -ss:开始时间
  • -t:持续时长
  • -c copy:不重新编码,超快!
ffmpeg -i input.mp4 -vf "crop=640:360" output.mp4
# 1. 创建文件列表.txt
echo "file '1.mp4'" > list.txt
echo "file '2.mp4'" >> list.txt

# 2. 合并
ffmpeg -f concat -safe 0 -i list.txt -c copy output.mp4

视频必须使用同一编码格式,否则拼接会失败。

🔊 音视频操作

ffmpeg -i input.mp4 -q:a 0 -map a output.mp3
ffmpeg -i video.mp4 -i audio.mp3 -c:v copy -map 0:v:0 -map 1:a:0 -shortest output.mp4
ffmpeg -i input.mp4 -an output.mp4

📏 视频参数调整

ffmpeg -i input.mp4 -vf "scale=640:-1" output.mp4
  • -1 表示自动等比例缩放
ffmpeg -i input.mp4 -r 24 output.mp4
ffmpeg -i input.mp4 -b:v 1000k -b:a 128k output.mp4
  • -b:v:视频比特率(越小越模糊)
  • -b:a:音频比特率

🖼 添加水印和字幕

ffmpeg -i input.mp4 -i logo.png -filter_complex "overlay=10:10" output.mp4
  • overlay=10:10 表示左上角(x=10, y=10)
ffmpeg -i input.mp4 -vf subtitles=subtitle.srt output.mp4

字幕文件必须是 .srt 格式,且 UTF-8 编码。

🧙 第 4 节:FFmpeg 滤镜魔法

滤镜 = 滤镜表达式 + 视频滤镜选项 -vf(Video Filter)。是 FFmpeg 视频处理的灵魂核心。

📋 滤镜的基本格式

ffmpeg -i input.mp4 -vf "滤镜1=参数,滤镜2=参数" output.mp4
  • -vf:指定视频滤镜(Video Filter)
  • , 分隔多个滤镜,按顺序处理(滤镜链)
  • filter_complex:用于更复杂的滤镜图(后面讲)

🎯 常见滤镜实战

我们一个个来,不多讲,直接用。

scale:视频缩放

ffmpeg -i input.mp4 -vf "scale=720:-1" output.mp4
  • 把视频宽度缩放为 720,高度按比例自动调整

crop:裁剪画面

ffmpeg -i input.mp4 -vf "crop=640:360:100:50" output.mp4
  • crop=w:h:x:y

    • 宽 640、高 360
    • 从位置 (100, 50) 开始裁剪

fade:淡入/淡出特效
淡入前 30 帧:

ffmpeg -i input.mp4 -vf "fade=t=in:st=0:d=1" output.mp4

淡出最后 1 秒:

ffmpeg -i input.mp4 -vf "fade=t=out:st=9:d=1" output.mp4
  • t=in/out:类型
  • st:开始时间(秒)
  • d:持续时间(秒)

hue:调色滤镜

调节色相、饱和度等:

ffmpeg -i input.mp4 -vf "hue=s=0" output.mp4
  • s=0:去饱和度,效果类似黑白

更多玩法:

ffmpeg -i input.mp4 -vf "hue=h=60:s=2" output.mp4

drawtext:添加文字(内置字幕)

ffmpeg -i input.mp4 -vf "drawtext=text='你好FFmpeg':fontcolor=white:fontsize=36:x=10:y=20" output.mp4

需安装字体支持库,如 Linux 下需 libfreetype

  • 可用参数:
  • x/y:坐标
  • fontsize:字体大小
  • fontfile:指定字体路径
  • enable='between(t,2,5)':只显示第 2\~5 秒

🔗 滤镜链 vs 滤镜图

进阶概念:filterchain vs filtergraph

✅ 滤镜链(Filterchain)

-vf "scale=640:-1,eq=contrast=1.5"
  • 滤镜按顺序执行,适合线性处理

✅ 滤镜图(Filtergraph)

-filter_complex "[0:v]scale=640:360,fade=t=in:st=0:d=2[out]" -map "[out]"
  • 适合复杂分支、混流、多轨合成

🧪 第 5 节:编码器与压缩优化

编码器是 FFmpeg 的发动机。选得好、调得巧,能压 70% 体积、保 90% 画质。

🎞️ 视频编码器选项速览

编码器优点缺点常见扩展名
libx264兼容性最好,速度快文件偏大.mp4
libx265体积更小,同等画质更强编码更慢,不支持老设备.mp4
libvpx-vp9开源,浏览器支持广压制较慢.webm
aom-av1体积最小,画质极佳压制极慢,硬件支持差.mkv

推荐用 libx264libx265,平衡兼容性与压缩效率。

🎛️ 常用压缩参数组合

我们用 H.264(libx264)为例,来压一个高清 MP4。

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset medium -c:a aac -b:a 128k output.mp4

参数解析:

  • -c:v libx264:选择 H.264 编码器
  • -crf 23:质量控制(越小越清晰,范围 051,推荐 1828)
  • -preset medium:编码速度(推荐值:ultrafast → slow)
  • -c:a aac -b:a 128k:音频编码器与码率

📊 CRF 不同数值的效果对比

CRF 值画质文件大小
18几乎无损最大
23兼顾清晰与体积推荐默认
28有轻微模糊更小体积

preset 的效果对比

-preset ultrafast / superfast / veryfast / faster / fast / medium / slow / slower / veryslow
  • 越慢越小,但越耗 CPU
  • 推荐一般用 medium,极限压缩才用 slower

💡 多线程加速压缩(高级用法)

FFmpeg 会自动启用多线程,但你可以手动指定:

ffmpeg -i input.mp4 -c:v libx264 -crf 23 -preset fast -threads 4 output.mp4
  • -threads 4:限制为 4 核压制(可省略)

🎯 libx265 的压缩优势

对同样一段 1080p 视频,用 H.264 和 H.265 压缩对比如下:

编码器文件大小肉眼清晰度编码耗时
libx264120MB清晰2 分钟
libx26575MB清晰3 分半

使用 libx265 时,音频编码器仍可用 aac,例如:

ffmpeg -i input.mp4 -c:v libx265 -crf 28 -preset slow -c:a aac -b:a 128k output.mp4

🧨 NVIDIA + hevc_nvenc 兼容性坑

在使用 hevc_nvenc 编码器时,第一帧(尤其是 seek 后重新编码的第一帧)如果不是关键帧或初始化参数未设置完整,会出现以下情况:

  • 首帧画面是绿屏或马赛克
  • ffmpeg 没有报错,但第一秒异常
  • 多发生在精准 seek (-ss + -accurate_seek) + HEVC 编码器时

这是 NVIDIA 硬件编码器的一个“兼容性坑”。

📦 第 6 节:批处理与自动化

当你面对一大堆视频,不想一个个手动操作时,批处理是你的好帮手。

💻 批量转码当前目录下所有 .mp4 文件

macOS / Linux 示例(bash

for f in *.mp4; do
  ffmpeg -i "$f" -c:v libx264 -crf 24 -preset medium -c:a aac -b:a 128k "out_$f"
done

Windows 示例(.bat 文件)

@echo off
for %%f in (*.mp4) do (
  ffmpeg -i "%%f" -c:v libx264 -crf 24 -preset medium -c:a aac -b:a 128k "out_%%f"
)

保存为 batch_compress.bat,放在视频目录下双击运行即可。

🧪 与 Python/Powershell 联动处理

Python 示例(调用 FFmpeg 命令)

import subprocess
from pathlib import Path

for f in Path(".").glob("*.mp4"):
    output = f"compressed_{f.name}"
    subprocess.run([
        "ffmpeg", "-i", str(f),
        "-c:v", "libx264", "-crf", "24", "-preset", "fast",
        "-c:a", "aac", "-b:a", "128k", output
    ])

可以将 FFmpeg 与 Flask、Gradio 等结合做成本地 Web 转码器。


🗃️ 文件名优化/自动重命名

ffmpeg -i input.mp4 -c:v libx264 -crf 24 output_$(date +%Y%m%d_%H%M).mp4
  • 通过 shell 插入时间戳,避免文件覆盖

🔚 结语:你的 FFmpeg 超能力已解锁!

恭喜你,已经掌握 FFmpeg 的核心技能:安装 → 转码 → 剪辑 → 滤镜 → 压缩 → 批处理。

你现在可以:

  • 🚀 批量压缩 20G 视频资料包
  • 🎬 制作带字幕的教学视频
  • 🖼 把视频画面变成 GIF + 淡入淡出 + 水印
  • 🎞 一行命令生成短视频片头片尾

🎁 推荐工具/文档/资源合集

FFmpeg 官网

FFmpeg Filters 文档

FFmpeg Cheat Sheet

]]>
0 https://reinness.com/posts/276#comments https://reinness.com/feed/
uv:用 Rust 武装的极速 Python 包管理器入门指南 https://reinness.com/posts/267 https://reinness.com/posts/267 Tue, 05 Aug 2025 16:35:00 +0800 Caleb

“你可能还在 pip install,隔壁已经跑完测试上线了。”
—— 一个用上 uv 的开发者

什么是 uv?

它是个比 pip / poetry 快 100 倍的 Python 包管理器,还是 Rust 写的,能不快?

如果你嫌 pip 慢,poetry 重,pipenv 老,那你可能正需要一个又快又干净的替代品:uv


🧠 为什么选择 uv?

我们来点直白的对比:

操作pip + venvpoetryuv
创建虚拟环境🏎️ 极速
安装依赖啥时候能装完?哎呀还在跑?⚡️ 咻——就好了
命令行体验各种命令混搭统一还行✅ 清爽原生 pip
使用成本低(但新)

🛠️ 安装 uv

你需要一台电脑,一条命令,以及 10 秒钟的等待:

curl -Ls https://astral.sh/uv/install.sh | sh

或者你偏爱 Homebrew(macOS 用户):

brew install astral-sh/tap/uv

装完后你就拥有了一个拥有超能力的 pip 替代品。


🧪 快速上手 uv

你已经习惯了 pip?放心,uv 的命令几乎一样,直接替换掉即可。

创建虚拟环境

uv venv

它会在当前目录创建一个 .venv 文件夹。完美兼容 python -m venv

激活环境:

# macOS / Linux
source .venv/bin/activate

# Windows(你知道的)
.venv\Scripts\activate

安装依赖(咻的一下)

uv pip install requests

会自动走缓存,比 pip 多个涡轮加速器,支持 requirements.txt:

uv pip install -r requirements.txt

你甚至可以把 uv pip 当成 drop-in 替代:

alias pip="uv pip"

从此告别 pip install 漫长等待。


生成 requirements.txt?

虽然 uv 没有官方的 freeze 命令,但你可以照常操作:

uv pip freeze > requirements.txt

和 pip 完全兼容,不会让你懵圈。


更新依赖

uv pip install --upgrade somepackage

一样的语法,不一样的速度体验。


🧹 清理缓存

uv 会将下载的 wheel 缓存在本地,速度飞快,但你可能想定期清理下:

uv cache clean

或者查看缓存路径:

uv cache dir

🎁 Bonus:兼容 poetry!

你有项目用 poetry 管理?

uv venv
uv pip install -r requirements.txt

甚至可以配合 poetry export 使用:

poetry export -f requirements.txt --without-hashes > requirements.txt
uv pip install -r requirements.txt

poetry 当个锁定工具,安装交给 uv 这位“闪电侠”。


❓ 小贴士(FAQ)

Q: uv 能完全替代 pip 吗?

是的!uv pip 是 drop-in 替代品,你可以用所有 pip 的命令,甚至更快。

Q: uv 支持 pyproject.toml 吗?

暂不完整支持。但可以和 poetry 配合使用(先导出 requirements.txt)。

Q: uv 稳定吗?

非常稳定,虽然还在快速迭代中。但已经可用于日常开发。


📦 总结

uv 是那种一用就回不去的工具:

✅ pip 的速度不够快?用 uv。
✅ 想要 poetry 的干净,但不要它的慢?用 uv。
✅ 喜欢新鲜事物,但又讨厌配置地狱?还是 uv。

🐍 Python 太优雅,不该被安装依赖拖慢节奏。用 uv,让开发像飙车一样爽!

🔗 相关链接

]]>
0 https://reinness.com/posts/267#comments https://reinness.com/feed/
PIP 更换国内安装源 https://reinness.com/posts/247 https://reinness.com/posts/247 Thu, 11 Jan 2024 14:31:00 +0800 Caleb 修改源方法

临时使用

可在使用 pip 的时候在后面加上 -i 参数,指定 pip 源

pip install scrapy -i https://pypi.tuna.tsinghua.edu.cn/simple

永久修改

Linux

使用文本编辑工具打开 ~/.pip/pip.conf 文件 (如果没有当前文件夹或文件,请创建后操作) ,修改文件内容如下:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple

Windows

WIN+R 打开用户目录 %HOMEPATH%,在此目录下创建 pip 文件夹,在 pip 目录下创建 pip.ini 文件, 内容如下:

[global]
index-url = https://pypi.tuna.tsinghua.edu.cn/simple

pip 国内镜像

厂商源地址
阿里云https://mirrors.aliyun.com/pypi/simple/
中国科技大学https://pypi.mirrors.ustc.edu.cn/simple/
豆瓣https://pypi.douban.com/simple/
清华大学https://pypi.tuna.tsinghua.edu.cn/simple/
]]>
1 https://reinness.com/posts/247#comments https://reinness.com/feed/
Conda安装与镜像源配置 https://reinness.com/posts/245 https://reinness.com/posts/245 Thu, 11 Jan 2024 14:04:00 +0800 Caleb 安装

Anaconda

清华源

Miniconda

清华源

配置

查看当前源

conda config --show channels

替换源

语法如下

conda config --add channels https://xxxxxxxxxxxxxxx

针对清华源和腾讯源替换的方法

清华源

conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/

腾讯源

conda config --add channels https://mirrors.cloud.tencent.com/anaconda/pkgs/free/
conda config --add channels https://mirrors.cloud.tencent.com/anaconda/pkgs/main/
conda config --add channels https://mirrors.cloud.tencent.com/anaconda/cloud/conda-forge/
conda config --add channels https://mirrors.cloud.tencent.com/anaconda/cloud/pytorch/
conda config --add channels https://mirrors.cloud.tencent.com/anaconda/pkgs/pro/

移除源

语法如下

conda config --remove channels https://xxxxxxxxxxxxxxx

针对清华源和腾讯源移除的方法

清华源

conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --remove channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/

腾讯源

conda config --remove channels https://mirrors.cloud.tencent.com/anaconda/pkgs/free/
conda config --remove channels https://mirrors.cloud.tencent.com/anaconda/pkgs/main/
conda config --remove channels https://mirrors.cloud.tencent.com/anaconda/cloud/conda-forge/
conda config --remove channels https://mirrors.cloud.tencent.com/anaconda/cloud/pytorch/
conda config --remove channels https://mirrors.cloud.tencent.com/anaconda/pkgs/pro/

使用配置文件修改

使用文档编辑工具打开 ~/.condarc 文件,即可手动修改配置信息。

channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/pro/
  - defaults
show_channel_urls: true
]]>
0 https://reinness.com/posts/245#comments https://reinness.com/feed/
2023年终总结 https://reinness.com/posts/251 https://reinness.com/posts/251 Sat, 06 Jan 2024 15:37:00 +0800 Caleb

总结视频

2023年,以时间线缓缓展开。

后视镜

登门

我和妻子于 2016 年相识并交往,今年是我们在一起的第七年。按照常理,我早就该登门拜访她的家人了,但由于家里变故和各种风俗礼教的原因,一直推迟。今年春节,我终于第一次登门拜访了丈人家。

作为新女婿,我的内心其实非常忐忑。我向周围许多朋友请教,学习他们的经验。空闲时就琢磨准备什么礼物,咨询各种风俗,生怕遗漏了什么。回想起来,其实都是自己内心的不确定导致的紧张。

年前收拾好东西送妻子回家,路上并没有什么感觉,但是快到目的地的时候就开始心跳加速了。但是岳父岳母都挺好的,逐渐熟络起来后,我的顾虑也消除了。后面聊聊家长里短,一切都平静了,很完美的第一次表现。

出差

二月,作为我第一次出远差的起点,值得特别记录。团队在成都组织了一次聚餐,这也是我第一次去四川。在那里,我结识了几位远程办公的同事,并品尝了正宗的四川火锅。麻辣过瘾的滋味让我难忘,但折耳根的味道,我这辈子恐怕都无法接受了。

宽窄

医院

后海风景

去年,妻子被诊断出了个妇科疾病,辗转几家医院治疗,但效果不佳。她因此承受了许多不必要的痛苦。最终,我们商量后决定去医院进行手术。评估结果显示,她的身体状况良好,可以尽快安排手术。这是她第一次手术,手术当日已经推进手术室了,但因过于紧张导致体温升高,医院担心新冠风险而取消了手术。这个意外却带来了意想不到的收获,国内顶尖的协和医生团队在周六来指导,并将她的手术作为教学示范进行。

陪同妻子送到手术室,一路上不断给她加油打气。但当手术室门关上的那一瞬间,我的心里却异常紧张。或许是因为担心她独自面对手术,或许是担心手术结果。两个半小时的等待后,医生告诉我手术非常顺利,所有病变组织都已摘除干净,麻药醒来后她就能出来。那一刻,我的心里终于安定了很多。

现在已经彻底康复了,成天哐哐炫零食

出游

我俩的第一次陕南之旅开启了。体会了完全不同的人文特色和美食文化。

石泉古城,被誉为‘丝路之源’,坐落在汉江边上的一座小城。依山傍水,风景秀丽。据说 1500 多年间名字没有改变,充满了历史感。这里不仅能体验到川渝风格,方言也带有浓厚的川味,但却隶属于陕西管辖。山水资源丰富,‘靠山吃山,靠水吃水’在这里得到了完美体现。渔获丰足,使得烤鱼和石锅鱼格外美味。

花

石泉老街

在这里也参加了同事的婚礼,帮忙布置婚房和收拾物品。祝愿他们和美长久。

说安康,不得不提“蒸面”,第一次吃还是大学室友带来的。虽然它与凉皮有些相似,但也拥有自己独特的风味。再次品尝了推荐的店家并且还去尝了另外的美味羊肉汤。晚间在汉江边吹着江风,品尝美食,喝着小酒,真是无与伦比的惬意。汉江轮渡也是另一番不错的体验。

柞水溶洞,天然地貌也不容错过啦。

柞水溶洞

订婚

12月2号,我们订婚啦!!!

订婚照

这是今年最大的一件事,长达七年的恋爱长跑终于迎来了曙光,离我们梦想中的生活越来越近了。

自从登门拜访后,双方家里人也来来回回地商讨了一番,最终确定了我们的婚约。敲定日期后,我们开始着手准备收拾房子。一步步完成打扫卫生、装修和家具布置等工作。

订婚摆台

最后在双方亲人的见证下,我们完成了订婚。收到了大家的祝福。这一刻,真的非常美好和珍贵。

婚期

作为23年尾的重要时刻,大家都在说24年无春是寡妇年。本着不迷信但是迎合家里人的想法,同意让演算结婚日期。

给出来了两个时间,一个是腊月二十六号,临近过年;另一个是 2024 年五四青年节。

后面由于时间太紧迫,而且冬季太冷,我们最终选择了五四青年节作为婚期。不过已经开始着手准备婚礼的各项用品了。

最后

写到最后,我满怀感激和幸福。过去七年的恋爱长跑,终于在这一刻迎来了新的开始。回顾这段旅程,从初识到相知,再到相守,每一步都充满了珍贵的回忆和成长。订婚不仅是我们感情的升华,也是双方家庭的融合。

在未来的日子里,我们将继续携手前行,共同迎接人生中的每一个挑战与欢乐。婚礼的准备虽然繁琐,但每一份细节都体现了我们对未来的期待与承诺。感谢亲友们的祝福与支持,你们的陪伴让这一切变得更加美好。

我期待着和爱人一起步入婚姻的殿堂,开启我们人生的新篇章。在这条充满爱的旅程中,我相信,幸福与美好将伴随我们左右。

猫咪奉上

汤圆猫咪

]]>
1 https://reinness.com/posts/251#comments https://reinness.com/feed/
2022年终总结 https://reinness.com/posts/204 https://reinness.com/posts/204 Mon, 26 Dec 2022 21:12:00 +0800 Caleb

2022年,一个充满憧憬的年份。

早在高中时期,得知22年要在北京开冬奥会。那会就下定决心,不管未来如何,都要去北京一趟亲眼看看。现在回想起来那是多么意气风发的年龄。25岁的我现在来告诉你:没去了!生活和疫情已经成了最近三年的基本基调,以至于没有多余的心态出去看看。(主要是因为太穷了)

父亲还在世的最后一次过年聚会时,他说想抓紧给我把婚事一办。时间想定在2022年2月2日。已经一年多了,不知道您可安好?


来自2024年的回复。我俩在2024年2月2日领证结婚了,刚好是北方小年。

今年为家里雪子找来了一个新伙伴。当当当当~ 汤圆(黑芝麻馅的)

汤圆.jpg

雪子汤圆.jpg

汤圆的到来,无疑是给雪子找了一个移动逗猫器。但是小家伙是一个戏精,老是来粘我,弄的雪子每次都是醋坛子打翻了。两个小朋友也是让人又笑又乐。

小汤圆是个调皮鬼,家里能钻的地方基本上都被她探索了一遍。最有趣的是她和雪子联合作案,她们知道放零食的是哪一个柜子。雪子先用爪子把柜门撬开一个缝隙,小汤圆就用头慢慢把柜门撑开,慢慢就挤进去了。颇为有趣的是,小汤圆进到柜子后会把零食拿出来和她姐姐一块吃,从来没吃过独食。所以最后的结果就是,我把柜门封死了!!!

五年,你知道这五年我是怎么过的吗?(图先欠着)

大学毕业后,我就想给自己配置一台台式机,可是种种原因致使我一年一年的都没有把这个事情办了。说来也巧合,买车送电脑可能也是第一回遇到。

去年年末提车,提车日期刚好卡在了政府对新能源汽车的补贴期间。11月25日开始,我车是11月26日提的,可真是吉星高照。顺利拿到六千块钱的补贴款,钱是早上到的,电脑是第二天开始组装的。

配件.png

配件陆陆续续3天之内到位,也顺利组装出了我特别满意的一套配置。

配置.png

得偿所愿的感觉真好,感谢我的 宋大米,给我送来的惊喜。

今年5月,老妈撺掇着开起了自己的门店,五一假期过去,搬房子、收拾整理、帮忙出谋划策。在摆脱诸多琐事后,小店也是顺顺利利的开始了运营。

安芙妍.jpg

瘦身减肥您找我哈~(加盟也可以呦)

经过了半年的积累,顾客也越来越多了,目前店面也在稳步的提升中。一切的一切都要归功于老妈的辛苦付出和亲姨的大力支持,向两位奋斗女性表示由衷的致意。

新的一年,想着和她们一块去把店面的客流量搞起来,多进一些新客。

工作上要写的话不多,基本上都是日复一日的流程。

今年的一整年的工作基本上都围绕着 Python,目前来讲已经全面拥抱 Python了。

值得庆幸的是通过了 TL答辩,目前在协助交付经理管理20人的小团队。

三年疫情,终于结束了。

阳康了。

疫情的封锁导致哪都去不了,老想苟着。望后面的形势越来越好,这样实体店的生意就又有生机了,不仅是为了我们的小店。

写到最后,太阳照在手上,怀中猫儿酣睡,哪有比这更安静的时刻。

冬日.jpg

让我们抛开思维,远离喧嚣。在这温暖的阳光里,独自享受大自然给予的宁静。

2022年,终!

]]>
1 https://reinness.com/posts/204#comments https://reinness.com/feed/