refactor: 项目目录结构重组

- 中文目录名改为英文:cDNA图像处理实例→examples,参考资料→references
- web→app(Flask应用标准命名)
- 输出目录平移到 data/output/simple/ 和 data/output/full/
- 输入图像统一到 data/input/
- 构建脚本移到 scripts/
- 源码文件改用 snake_case 命名
- 更新所有文件路径引用和文档
This commit is contained in:
2026-07-16 20:59:58 +08:00
parent a720845948
commit 3ca079a980
100 changed files with 98 additions and 71 deletions
+2
View File
@@ -8,6 +8,7 @@
# Python
__pycache__/
*.pyc
.venv/
# OS
.DS_Store
@@ -18,6 +19,7 @@ Thumbs.db
# Flask
.playwright-mcp/
app/uploads/
# PyInstaller 打包产物
dist/
+22 -12
View File
@@ -8,30 +8,37 @@
```
src/
├── cDNA_segmentation.py ← 原版:完整处理流程
└── cDNA_gridding_simple.py ← 简化版:仅划线,用于课堂讲解 ★
├── segmentation.py ← 原版:完整处理流程
└── gridding_simple.py ← 简化版:仅划线,用于课堂讲解 ★
results/ ← 原版输出(6张图)
results_simple/ ← 简化版输出(1张图)
app/ ← Flask Web应用
scripts/ ← 构建脚本
data/
├── input/ ← 输入图像
└── output/
├── simple/ ← 简化版输出(6张图)
└── full/ ← 原版输出(6张图)
cDNA图像处理实例/数据/cDNA/ ← 输入图像 + MATLAB示例
参考资料/NewGridAndCV/ ← MATLAB参考实现
examples/数据/cDNA/ ← 课程原始数据 + MATLAB示例
references/matlab/ ← MATLAB参考实现
references/papers/ ← 论文PDF
```
## 两个Python实现
### 简化版 (`cDNA_gridding_simple.py`) — 课堂主讲
### 简化版 (`src/gridding_simple.py`) — 课堂主讲
- 算法:投影求和 → (max-min)×10%阈值 → 过零点配对 → 划线
-150行,带详细中文注释,适合课堂讲解
- 输出:`results_simple/gridding_simple.png`
-370行,带详细中文注释,适合课堂讲解
- 输出:`data/output/simple/` 下 6 张图
- **与原版网格线位置完全一致(误差0像素)**
### 原版 (`cDNA_segmentation.py`) — 完整实现
### 原版 (`src/segmentation.py`) — 完整实现
- 网格划分:自相关+白顶帽+Otsu+质心(参照MATLAB `GriddingAndCV.m`
- 三种阈值分割:人工阈值、Otsu、迭代阈值
- TV去噪(Chambolle投影算法,参照 `tvdenoise.m`
- 输出:`data/output/full/` 下 6 张图
## 算法要点
@@ -44,6 +51,7 @@ cDNA图像处理实例/数据/cDNA/ ← 输入图像 + MATLAB示例
- `cDNA.png`820×820 RGB23×23斑点阵列,Cy3/Cy5双色荧光
- 斑点间距约35px,每斑点约18px宽
- 输入路径:`data/input/cDNA.png`
## 编码规范
@@ -52,8 +60,10 @@ cDNA图像处理实例/数据/cDNA/ ← 输入图像 + MATLAB示例
## 运行环境
Python`D:\ProgramData\anaconda3\envs\my_env`
依赖:numpy, scipy, scikit-image, matplotlib, Pillow
Python虚拟环境:项目根目录 `.venv/`uv 创建,Python 3.13
依赖:numpy, scipy, scikit-image, matplotlib, Pillow, flask
安装:`uv pip install numpy scipy scikit-image matplotlib Pillow flask --python .venv/Scripts/python.exe`
## 仓库
+42 -27
View File
@@ -15,28 +15,38 @@
```
cDNA微阵列图像处理作业/
├── src/
│ ├── cDNA_segmentation.py # 原版:网格划分 + 三种阈值分割 + TV去噪
│ └── cDNA_gridding_simple.py # 简化版:仅网格划分,用于课堂讲解
│ ├── segmentation.py # 原版:网格划分 + 三种阈值分割 + TV去噪
│ └── gridding_simple.py # 简化版:仅网格划分,用于课堂讲解
├── web/ # Flask Web 应用
│ ├── app.py # Flask 主程序
├── app/ # Flask Web 应用
│ ├── main.py # Flask 主程序
│ ├── launcher.py # PyInstaller 打包入口
│ ├── templates/index.html # 前端页面
│ └── static/style.css # 样式文件
├── build_exe.py # PyInstaller 打包脚本
├── cDNA_Analyzer.spec # PyInstaller spec 配置
├── flowchart.drawio # 算法流程图(用 Draw.io 打开)
├── scripts/
│ └── build.py # PyInstaller 打包脚本
├── data/
│ ├── input/ # 输入图像
│ │ └── cDNA.png
│ └── output/ # 生成结果
│ ├── simple/ # 简化版输出(6张图)
│ └── full/ # 原版输出(6张图)
├── examples/ # 课程示例数据(含 pptx + MATLAB demo
├── references/ # 参考资料
│ ├── papers/ # 论文 PDF
│ └── matlab/ # MATLAB 参考代码(NewGridAndCV
├── results/ # 原版输出图像(6张)
├── results_simple/ # 简化版输出图像(6张)
├── docs/ # 技术文档
│ ├── cDNA 微阵列网格划分.md # 算法详解
│ ├── 两版差异说明.md # 简化版 vs 原版对比
│ └── 依赖库清单.md # Python 依赖
│ ├── cDNA 微阵列网格划分.md
│ ├── 两版差异说明.md
│ └── 依赖库清单.md
├── cDNA图像处理实例/ # 课程示例数据
── 参考资料/ # MATLAB 参考代码 + 论文 PDF
├── flowchart.drawio # 算法流程图
── README.md
└── CLAUDE.md
```
---
@@ -50,28 +60,33 @@ cDNA微阵列图像处理作业/
### 方式二:Web 开发模式
```bash
cd web
python app.py
# 首次使用需创建虚拟环境
uv venv .venv --python <你的Python路径>
uv pip install numpy scipy scikit-image matplotlib Pillow flask --python .venv/Scripts/python.exe
# 启动
cd app
python main.py
# 浏览器自动打开 http://localhost:5000
```
### 方式三:命令行脚本
```bash
# 简化版(课堂主讲,约150行)
python src/cDNA_gridding_simple.py
# 输出:results_simple/ 下 6 张图
# 简化版(课堂主讲,约370行)
python src/gridding_simple.py
# 输出:data/output/simple/ 下 6 张图
# 原版(完整实现,430行)
python src/cDNA_segmentation.py
# 输出:results/ 下 6 张图
# 原版(完整实现,约470行)
python src/segmentation.py
# 输出:data/output/full/ 下 6 张图
```
---
## 两种 Python 实现
### 简化版 `cDNA_gridding_simple.py`(课堂主讲)
### 简化版 `src/gridding_simple.py`(课堂主讲)
**算法思路**(初中学历即可理解):
@@ -85,12 +100,12 @@ python src/cDNA_segmentation.py
**特点**
-150 行代码,带详细中文注释
-370 行代码,带详细中文注释
- 核心逻辑仅 30 行
- 与原版网格线位置误差为 **0 像素**
- 完全自动化,无需人工设定参数
### 原版 `cDNA_segmentation.py`(完整实现)
### 原版 `src/segmentation.py`(完整实现)
| 模块 | 算法 | 参照 |
|------|------|------|
@@ -128,7 +143,7 @@ python src/cDNA_segmentation.py
使用 PyInstaller 将 Web 应用打包为单个 exe 文件:
```bash
python build_exe.py
python scripts/build.py
# 输出:dist/cDNA_Analyzer.exe(约 68MB
```
@@ -138,7 +153,7 @@ python build_exe.py
## 运行环境
- **Python**: `D:\ProgramData\anaconda3\envs\my_env`
- **Python**: 3.13uv 虚拟环境 `.venv/`
- **依赖**: numpy, scipy, scikit-image, matplotlib, Pillow, Flask
---
+1 -1
View File
@@ -12,7 +12,7 @@ os.environ['TEMPLATE_DIR'] = os.path.join(base, 'templates')
os.environ['STATIC_DIR'] = os.path.join(base, 'static')
# 导入 Flask app
from app import app
from main import app
def open_browser():
os.startfile('http://localhost:5000')
+2 -2
View File
@@ -1,8 +1,8 @@
"""
cDNA微阵列图像处理 - Web UI (Flask)
=====================================
启动python web/app.py
打包python build_exe.py
启动python app/main.py
打包python scripts/build.py
打开http://localhost:5000
"""

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

+7 -7
View File
@@ -102,9 +102,9 @@ plt.rcParams['axes.unicode_minus'] = False # 第 27 行
```python
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) # 第 30 行
BASE_DIR = os.path.dirname(SCRIPT_DIR) # 第 31 行
DATA_DIR = os.path.join(BASE_DIR, 'cDNA图像处理实例', # 第 32 行
DATA_DIR = os.path.join(BASE_DIR, 'data', 'input') # 第 32 行
'数据', 'cDNA')
OUTPUT_DIR = os.path.join(BASE_DIR, 'results_simple') # 第 33 行
OUTPUT_DIR = os.path.join(BASE_DIR, 'data', 'output', 'simple')
```
这 4 行在设置**相对路径常量**,保证脚本不管从哪个目录运行都能找到正确的文件。
@@ -116,14 +116,14 @@ OUTPUT_DIR = os.path.join(BASE_DIR, 'results_simple') # 第 33 行
| `os.path.dirname(...)` | 去掉文件名,只留目录 | `D:/.../src` |
| `SCRIPT_DIR` | 脚本所在目录 | `.../src` |
| `BASE_DIR` | 项目根目录(上级) | `.../cDNA微阵列图像处理作业` |
| `DATA_DIR` | 输入图像所在目录 | `.../cDNA图像处理实例/数据/cDNA` |
| `OUTPUT_DIR` | 输出图像保存目录 | `.../results_simple` |
| `DATA_DIR` | 输入图像所在目录 | `.../data/input` |
| `OUTPUT_DIR` | 输出图像保存目录 | `.../data/output/simple` |
| | | |
`os.sep.join(A, B, C)` 会把 A、B、C 用系统的路径分隔符(Windows 是 `\`)拼起来:
```
BASE_DIR + 'cDNA图像处理实例' + '数据' + 'cDNA'
→ '.../cDNA微阵列图像处理作业/cDNA图像处理实例/数据/cDNA'
BASE_DIR + 'data' + 'input'
→ '.../cDNA微阵列图像处理作业/data/input'
```
---
@@ -523,6 +523,6 @@ Python 的习惯写法。
## 课堂演示建议
1. 先在 PPT 上展示算法步骤的 6 步流程图
2. 投影曲线的图片可以直接从 `results_simple/gridding_simple.png` 截取
2. 投影曲线的图片可以直接从 `data/output/simple/01_grid_overlay.png` 截取
3. 重点讲清楚第 130~136 行"为什么从第一个 +→- 开始配对",这是最容易出错的地方
4. 如果有投影仪,可以现场改 `pct` 参数值(改成 0.05、0.20),让学生看到阈值怎么影响结果
+5 -5
View File
@@ -8,7 +8,7 @@
| | 功能 | 用途 |
|---|---|---|
| 简化版 | 只画网格线 | 课堂讲解 |
| 简化版 | 只画网格线+逐格分割 | 课堂讲解 |
| 原版 | 网格 + 去噪 + 分割 + 后处理 + 可视化 | 完整作业 |
---
@@ -17,7 +17,7 @@
### 1. TV全变分去噪
**位置**`cDNA_segmentation.py` 第 78-102 行,`tv_denoise()`
**位置**`src/segmentation.py` 第 78-102 行,`tv_denoise()`
**干什么**:对每个子块做 Chambolle 投影去噪,去除荧光噪点。简化版完全没有这一步。
@@ -25,7 +25,7 @@
### 2. 三种阈值分割
**位置**`cDNA_segmentation.py` 第 35-71 行
**位置**`src/segmentation.py` 第 35-71 行
| 函数 | 原理 |
|------|------|
@@ -37,7 +37,7 @@
### 3. 全图逐块分割
**位置**`cDNA_segmentation.py` 第 374-411 行
**位置**`src/segmentation.py` 第 397-434 行(逐块分割主循环)
**做什么**:对网格划分出的每个子块:
1. 如果太暗(均值<5或<30),先增强
@@ -50,7 +50,7 @@
### 4. 后处理
**位置**`cDNA_segmentation.py` 第 206-226
**位置**`src/segmentation.py` 第 206-249
| 函数 | 作用 |
|------|------|
+5 -5
View File
@@ -9,7 +9,7 @@
| **scikit-image** | 0.25.2 | `rgb2gray` 灰度转换、Otsu 阈值 | 全部 .py |
| **matplotlib** | 3.10.8 | 可视化绘图(投影曲线、直方图、结果图) | 全部 .py |
| **Pillow** | 12.1.1 | 读取图像文件(png/tif/jpg | 全部 .py |
| **Flask** | 3.1.2 | Web 后端 | web/app.py |
| **Flask** | 3.1.2 | Web 后端 | app/main.py |
## 仅打包时需要的额外依赖
@@ -20,17 +20,17 @@
## 安装命令
```bash
pip install numpy scipy scikit-image matplotlib Pillow flask
uv pip install numpy scipy scikit-image matplotlib Pillow flask --python .venv/Scripts/python.exe
```
如需打包:
```bash
pip install pyinstaller
python build_exe.py
uv pip install pyinstaller --python .venv/Scripts/python.exe
python scripts/build.py
```
## 版本兼容性
以上版本为当前开发环境(Python 3.10Anaconda `my_env`)实测版本。
以上版本为当前开发环境(Python 3.13uv 虚拟环境 `.venv/`)实测版本。
其他相近版本通常兼容,无特殊版本锁定需求。

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 100 KiB

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Before

Width:  |  Height:  |  Size: 3.1 MiB

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 257 KiB

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 87 KiB

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 124 KiB

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 126 KiB

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 270 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.4 KiB

+4 -4
View File
@@ -1,6 +1,6 @@
"""
PyInstaller打包脚本
运行: python build_exe.py
运行: python scripts/build.py
输出: dist/cDNA_Analyzer.exe
"""
@@ -8,12 +8,12 @@ import PyInstaller.__main__
import os
PyInstaller.__main__.run([
'web/launcher.py',
'app/launcher.py',
'--name=cDNA_Analyzer',
'--onefile',
'--noconsole',
'--add-data', f'web/templates;templates',
'--add-data', f'web/static;static',
'--add-data', f'app/templates;templates',
'--add-data', f'app/static;static',
# 排除不相关的重量级包(环境里有torch/pandas等)
'--exclude-module', 'torch',
'--exclude-module', 'torchvision',
@@ -2,7 +2,7 @@
cDNA微阵列图像处理 简化版
======================================
D:\ProgramData\anaconda3\envs\my_env\python.exe src/cDNA_gridding_simple.py
.venv\Scripts\python.exe src/gridding_simple.py
算法流程总览
@@ -56,8 +56,8 @@ plt.rcParams['axes.unicode_minus'] = False
# 路径设置(从脚本位置动态推导,禁止硬编码绝对路径)
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
BASE_DIR = os.path.dirname(SCRIPT_DIR)
DATA_DIR = os.path.join(BASE_DIR, 'cDNA图像处理实例', '数据', 'cDNA')
OUTPUT_DIR = os.path.join(BASE_DIR, 'results_simple')
DATA_DIR = os.path.join(BASE_DIR, 'data', 'input')
OUTPUT_DIR = os.path.join(BASE_DIR, 'data', 'output', 'simple')
# ================================================================
@@ -24,8 +24,8 @@ rcParams['axes.unicode_minus'] = False
# 路径配置(使用脚本位置向上两级作为基准)
_SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
_BASE_DIR = os.path.dirname(_SCRIPT_DIR) # 项目根目录
DATA_DIR = os.path.join(_BASE_DIR, 'cDNA图像处理实例', '数据', 'cDNA')
OUTPUT_DIR = os.path.join(_BASE_DIR, 'results')
DATA_DIR = os.path.join(_BASE_DIR, 'data', 'input')
OUTPUT_DIR = os.path.join(_BASE_DIR, 'data', 'output', 'full')
# ============================================================