单篇论文图片重复检测(paper-figure-dup-scan)
一、定位与边界
做什么:对一篇论文的全部图片做"疑似重复"扫描,输出分级报告供人工审核。
不做什么:
- 不做学术判定——分级只是机器置信度,最终以人工审核为准;
- 不跨文章查重(单篇场景,小 N 两两比对即可);
- 不检测矢量绘制图片(默认只处理 PDF 嵌入位图;矢量页会在报告中列出页码,提示人工翻看。
需要覆盖矢量页时可用 --render-pdf 页码 把整页渲染成位图再检,见第七节)。
- 不联网、不读论文元数据(标题/作者/DOI/正文一概不读)。
召回优先原则:宁可错报、不可遗漏。所有候选只分级、不丢弃;复核失败只降级、
不剔除。L4 弱信号也列档(紧凑表格)。
非实体内容应被忽略:坐标轴/边框、柱体/图形元素、标签文字、边缘空白、
纯色块这类内容的相似,每张图都有,报出来只是噪声。判据与实现见 9.10、9.14。
- 服务层(
paper-dupscan应用)已内置该过滤:命中不报,只给计数。 - 本技能的命令行脚本保持原始输出(召回优先、全部候选照列),
过滤交给调用方——命中记录里带flags字段,上层自行决定是否丢掉。
二、一键用法
PY="C:/Users/xin/.workbuddy/binaries/python/envs/default/Scripts/python.exe"
S="<skill>/scripts"
# 输入论文 PDF(自动提取全部嵌入位图)
$PY $S/paper_dup_scan.py "论文.pdf"
# 或输入图片目录(递归收集 png/jpg/bmp/webp/tif)
$PY $S/paper_dup_scan.py "图片目录/"
# 常用选项
$PY $S/paper_dup_scan.py "论文.pdf" --outdir 输出目录 \
--block 96 --step 48 --thr 0.90 --grid-thr 0.90 --top 20 --min-dim 80
# 投稿前自查:同时导出 Word(证据图内嵌,方便发给导师/编辑)
$PY $S/paper_dup_scan.py "论文.pdf" --docx
输出目录结构:
<输出目录>/
├── report.md # 分级检测报告(主交付物)
├── report.docx # 加 --docx 时生成:Word 版,证据图内嵌
├── evidence/ # 证据图(标注框图 / 命中区域并排 / 整图对并排)
└── images/ # 从 PDF 提取的中间图片(人工复核后可整目录删除)
依赖:numpy opencv-python-headless pillow pymupdf
(PDF 输入才需要 pymupdf;图片目录模式不需要;--docx 需要 python-docx 与 pillow)。
三、检测流水线(三条线)
| 线 | 检测什么 | 方法 |
|---|---|---|
| ① 整图比对 | 不同图片文件之间:完全相同 / 编辑变体 | SHA-256 → pHash 8 方向扩展(距离≤6 判近似)→ ORB+RANSAC 复核(距离 6–40 区间) |
| ② panel 网格 | 拼合图内 panel 阵列重复 | 行列投影探测空白带切分格子 → 两两 NCC(恒等/水平镜像/垂直镜像/旋转180 四变换) |
| ③ 块匹配 CMFD | 图内任意区域复制-移动 | 滑窗取块(默认 96px/步长48)→ 零均值单位范数化 → 矩阵乘法一次算全部块对 NCC(四变换)→ NMS 聚合 |
四、分级标准(召回优先)
| 级别 | 条件 | 处置建议 |
|---|---|---|
| L1 像素级复制 | NCC≥0.99 且 MAE≤2 且 多尺度方差≤0.02 且 窗口生长不崩塌 | 机器高置信,仍需人工确认 |
| L2 高度疑似 | NCC≥0.99(复核未过,注明原因);或 NCC≥0.95 且 MAE≤6;整图 pHash≤6 / ORB 通过 | 强烈建议人工复核 |
| L3 疑似 | NCC≥0.95;panel NCC≥0.90 | 浏览证据图 |
| L4 弱信号 | NCC≥0.90(块报告阈值) | 列档备查(紧凑表) |
| 整图完全相同 | SHA-256 一致 | 常见于方法图合法复用,结合语境判断 |
| 非实体(不报) | 坐标轴/柱体/文字/边缘背景/纯色块 | 直接忽略,仅计数(见 9.10、9.14) |
复核(只标注、不剔除):对 L1 候选做多尺度验证(48–256px 中心对齐取样,
NCC 标准差>0.02 = 特定尺寸巧合)与窗口生长测试(放大 1–4 倍,NCC 崩塌 = 直线/
边界伪影)。复核失败 → 降 L2 并注明原因。每图最多复核 5 个候选(控制耗时)。
五、误报三关 + 多尺度第四关(内置于脚本)
- 排背景:块方差 <12 跳过(纯背景互相关必然虚高)。
- 排直线:行/列剖面方差 <3 跳过(坐标轴/边框/标尺线上任意两块必然像素相同,
是 NCC=1.000 的完美假阳性来源)。 - 窗口生长:真复制放大窗口后 NCC 维持;伪影单调崩塌。
- 多尺度方差:仅在 96px 下匹配、换尺寸即崩塌的为巧合(实测曾把
NCC=0.9989/MAE=0.2 的"完美命中"证伪为底部标签区巧合)。
六、报告解读要点(给审核人)
- 报告不含论文元数据,图片用"文件名(页码)"标识;坐标基于原生分辨率检测图(长边超 4200 才缩放)。
- 整图完全相同 ≠ 违规:方法示意图跨页复用是常见排版操作。
- L2 中注明"多尺度不稳定/窗口生长崩塌"的项已单独归入四-2 复核降级项(低优先),
强 L2 清单里剩下的都是未崩塌的真候选,人工复核聚焦它们即可。 - L5(
--struct开启时)是结构相似提示、非证据:品红框独立证据图*_struct.png,
报告别把它当像素级结果引用。 - 第七节列出无嵌入位图的页面——矢量图重复本工具检不出来,需人工翻看
(或对该页用--render-pdf兜底重检)。 - 判读标准:NCC≥0.99 且 MAE≤2 且多尺度恒定 = 像素级复制;0.95–0.99 = 高度相似需人眼。
七、参数调优
| 参数 | 默认 | 说明 |
|---|---|---|
--thr |
0.90 | 块匹配报告阈值。调低→召回更高、误报更多(0.85 极限召回) |
--grid-thr |
0.90 | panel 网格阈值 |
--block/--step |
96/48 | 块大小/步长。小目标重复可试 64/32 |
--min-dim |
80 | PDF 嵌入图最小边长,调小可捞小图 |
--top |
20 | 每图块命中保留上限(NMS 后) |
--no-evidence |
— | 跳过证据图,批量提速用 |
--struct |
关 | L5 结构同构相似提示(实验性):面向 "appear similar" 类弱指控, |
| 用 panel 亮度布局结构描述子(灰度块+梯度块余弦)+ 同图内相对 z 分(默认 sim≥0.90 且 z≥2.0σ, | ||
--struct-sim/--struct-z 可调)挑出异常相似配对。非证据、纯提示——同类型 assay |
||
| 的生物学重复天然相似,会误报;已在内部过滤窄条/近空格(长宽比≤3、短边≥2×min_cell、 | ||
| 墨水占比 3%–92%)与像素级已检出的配对 | ||
--render-pdf / --dpi |
— / 200 | 见第二节用法 |
其他内部行为(2026-09-08 更新):
- 大图自适应降采样:块数 >2000 时 step 自动翻倍重采(防内存超限,与
figure-integrity-check intra_block 一致);
- L2 分离:NCC≥0.99 但多尺度/窗口生长复核未过的项归入报告"四-2 复核降级项"
(低优先,多为文本块/直线/尺寸巧合伪影),不再混入强 L2,也不进人工审核清单;
- L1 复核名额:每图最多 20 个 L1 级候选做多尺度+窗口生长复核(与 top 上限一致);
- 目录模式命名:图片目录输入用相对路径命名(如 sub/figure1.png),
避免不同子目录同名文件互相覆盖证据图。
已知弱区(如实告知用户,不算失败):部分重叠(同图部分区域重叠,非整块复制)
与弱相似(官方措辞"appear similar"级)常常只有人眼可感,像素层面复现不出来。
实测案例(2026-09-08,p3 Fig 1C"跨列 panel 相似"指控):裁 panel 全对暴力互算,
块 NCC 天花板仅 0.72——远低于 0.85 极限阈值,属结构性弱相似,像素工具天生覆盖
不到。需要覆盖这类指控时用 --struct(见下)。
八、与 figure-integrity-check 的关系
- 本技能:产品化自查工具——单篇、隐私优先(离线/不存数据)、一键出报告、人工兜底。
- figure-integrity-check:研究验证工具——多脚本组合(含跨图比对、PubPeer
ground truth 对照、逐 Figure 定位),适合撤稿调查、批量样本验证等场景。
用户强调"不上网/不保存/只要单篇报告"→ 本技能;
用户要"查某篇被指控的论文、对照 PubPeer、系统验证"→ figure-integrity-check。
8.1 跨图比对的经验
(原 figure-integrity-check 的职责已并入本技能,下列经验备查)
-
ORB 跨图比对在低纹理图(blot 条带、染色)上不可信:
pHash 距离 22–32(接近随机)仍能"匹配"出 20–80 个点并过内点率门槛。
跨图判定必须再加一层像素 NCC 复核(缩到同高直算),否则一次能出几十条假阳性。 -
panel_grid在"多 panel 柱状图拼合图"上几乎无判别力:
Figure 1 有 a–i 九个 panel 全是柱状图时,任意两个 panel 的坐标轴、柱体
都会给出 NCC 0.98–0.99。这类命中只能靠内容类型判断或人工排除。 -
示意图的周期结构也要防:细胞膜磷脂双分子层是一排相同单元,
任意两段NCC=0.9920 / MAE=1.58。检测时若发现沿某方向存在 ≥1 处
等间距相似块,应判为周期性结构。 -
报告要落到 Figure 编号:作者只认"图 3 与 图 6 疑似重复",
不认p6_x624.png。用page.get_text("blocks")找同页最近Figure N
文本块做映射,实测三篇论文都能正确对应(同页多图追加序号:图 4-1 / 图 4-2)。 -
投稿前自查场景应"宁多报不漏报":把候选分成
优先核实 / 进一步核实(带伪影特征)/ 参考 三级全部列出,
由作者核实——不要为了"看起来干净"把大半候选过滤掉。
九、实测踩坑(2026-10-22,9 篇撤稿论文 + 6 篇未撤稿论文验证)
以下问题都是在真实投稿论文上跑出来的,按误报影响从大到小排列。数字是实测值。
9.1 整图 ORB 在低纹理图像上严重误判(误报 50+ 条)
orb_verify 对 Western blot 条带、染色图这类低纹理图像会给出 20–80 个"匹配"、
10+ 内点,ratio 也能过 0.40。实测一张 blot 图与无关图 pHash8 距离 22–32
(64 位哈希差近一半,等同随机)仍被判成"同一来源的编辑版本",一次跑出 77 条 L2 全是假的。
对策(服务层已实施,脚本默认值未改):
- pHash8 距离 > 20 直接归备查,不再送 ORB(到这个量级已不可能是同一图)。
- ORB 门槛提到 匹配≥40 / 内点≥25 / ratio≥0.55。
- 通过 ORB 的配对再做一次像素 NCC 兜底(缩到同高直算),<0.80 一律降级;
宽高比差 >35% 也直接排除。
9.2 "贯穿直线"判据会误杀真重复(必须限定线宽)
用"某列/某行几乎贯穿整块"判定坐标轴,会把 Western blot 条带一起杀掉——
条带本身就是横向长条,二值化后覆盖率可达 0.87。实测把
NCC=1.0000 / MAE=0.00 的完美重复判成了伪影。
对策:判据必须带线宽上限(坐标轴 1–3px,条带 40+px)。当前用
覆盖率≥0.78 且连续厚度≤10px 才算直线。
9.3 三类高频假阳性(NCC 可达 1.0000)
| 类型 | 实测 | 特征 |
|---|---|---|
| 柱状图柱体 | NCC=1.0000 / MAE=0.19 |
两块都是纯黑实心矩形,ink>0.85、sd<30 |
| 坐标轴 / 刻度线 | NCC=0.9994 / MAE=0.64(刻度等间距 96px) |
细直线满覆盖,或沿某方向有 ≥1 处等间距副本 |
| 示意图周期结构 | NCC=0.9920 / MAE=1.58 |
细胞膜磷脂双分子层、网格线——一排相同单元 |
另:多 panel 拼合图(Figure 1a–i 全是柱状图)的 panel 之间天然相似,
detect_grid 会大量命中,这类命中的 NCC 0.98–0.99 完全没有判别力。
9.4 render_pdf_pages 的页号是 1-based(易错)
name = "render_p%03d.png" % pno,其中 pno 从 1 开始(doc[pno-1])。
外部代码若按 0-based 反解文件名(如做文字层掩码),会整整偏一页甚至越界返回空,
导致整页渲染模式的文字过滤完全空转。实测就是这么踩的。
9.5 整页渲染时"模板图"判据会掩盖真重复
整页本身必然是多元素网格(实测 15×15),ux>=3 and uy>=3 永远成立,
于是"强 L2 全部被判成模板图 → 复核清单为空"。这比误报更危险——把真问题藏起来了。
整页模式下门槛须显著提高(如强命中 ≥15)。
9.6 大图会触发自适应降采样,可能漏小块重复
block=96 / step=48 下,3600×2400 的图会让块数超过 MAX_PATCHES=2000,
脚本自动把 step 翻倍到 96。块网格变粗后,小于块尺寸的局部复制会漏。
实测 6.7MB 的 PDF 中 4 张图触发降采样。需要高召回时,对大图单独指定 --block 64。
9.7 collect_dir 只认目录,传单个图片文件会静默返回空
结果是"0 命中",看着像干净,其实是啥都没测。单文件输入需自行包装成 1 元素列表。
9.8 给报告加"图号"能大幅提升可读性
报告写 p6_x624.png × p8_x862.png 作者看不懂。做法:
page.get_text("blocks") 找同页最近的 Figure N 文本块,按 bbox 中心距离匹配,
同页多图追加序号(图 4-1 / 图 4-2)。实测三篇论文的图号都能正确对上。
9.9 定位提醒:投稿前自查 ≠ 降噪
上述判据都是"降级"而非"删除"。若用途是投稿前自查,正确做法是
全部保留、标注伪影特征、按"优先核实/进一步核实/参考"分组,
由作者自己核实——而不是把 90% 的候选过滤掉。宁可多报,不可漏报。
9.10 非实体内容要直接忽略,不要降级到「参考」
> 实现位置:本技能的命令行脚本保持「召回优先、输出全部候选」,
> 不做这层过滤(见下方说明)。下列判据实现在 paper-dupscan 服务的
> core/dupcheck.py,若要复用请见本节末尾的代码。
用户的原话:「坐标轴、柱体、标签文字、边缘背景等非实体重复其实可以忽略,
否则每张图都有重的。」
之前把这些降级到「参考」组,结果是报告大半篇幅都是无意义命中:
| 篇目 | 忽略前 nonentity | 保留后 |
|---|---|---|
| 23486834 | — | 119 条 |
| 33578038 | — | 229 条 |
| 26581161 | — | 146 条 |
做法:在生成命中记录时先算 flags,命中非实体就 nonentity=True 且不生成证据图
(顺带省掉 imwrite 开销,实测占 11.5s / 54.6s)。报告单列一节只给计数。
范围要卡准(踩过的坑):一开始把 periodic(等间距重复)和 geometry
(单一连通域)也算进去,结果 26581161 有 76 条 NCC=0.9998/MAE=0.9 的命中、
33578038 有 112 条纯 geometry 的命中被一并忽略。阵列状铺图(重复的菌落/条带/孔板)
本身就呈周期性,忽略会漏真重复。
最终口径:
NONENTITY = ("axis", "solid", "geometry", "text", "edge", "flat") # 用户点名的类别
# periodic 只在同时伴随 axis/text 时才归入非实体
可复用判据(对每个命中算一次,命中即 nonentity=True,不生成证据图):
NONENTITY = ("axis", "solid", "geometry", "text", "edge", "flat")
flags = []
if tcov >= 0.35 or text_like(ca) >= 0.55: # 落在正文文字区/文字块
flags.append("text")
if ink < 0.10 or (ink < 0.25 and sd < 25): # 稀疏、几乎全白
flags.append("edge")
if sd < 20: # 平淡背景
flags.append("flat")
if ink > 0.90 and sd < 15: # 纯色块
flags.append("solid")
if geometry_likeness(ca) >= 0.50: # 单一连通域(柱体、图例方块)
flags.append("geometry")
if line_through(ca) >= 0.78 or axis_likeness(ca) >= 0.55: # 贯穿直线/坐标轴
flags.append("axis")
if periodic_copies(g, boxA, boxB) >= 1: # 等间距重复(刻度/网格)
flags.append("periodic")
if ink > 0.85 and sd < 30 and mae > 8: # 渐变底:NCC 虚高但灰度差大
flags.append("flat")
is_nonentity = bool(set(flags) & set(NONENTITY))
# periodic 单独不忽略:阵列状铺图会呈周期性,实测有 NCC=0.9998 的命中
if not is_nonentity and "periodic" in flags and ("axis" in flags or "text" in flags):
is_nonentity = True
为什么不把这些判据塞进本脚本:命令行版定位是「召回优先、输出原始候选」,
让人自己判断;这层过滤改变了输出语义(有内容不出现),属于产品决策,
放在服务层更合适。要过滤就在调用方做——命中记录里带上 flags,由上层决定。
9.11 并行化:ThreadPoolExecutor,不要进程池
cProfile(23486834,54.6s)显示:
| 环节 | 耗时 | 说明 |
|---|---|---|
detect_blocks |
22.7s | 主体是 numpy 矩阵乘,释放 GIL |
collect(std) |
19.1s | numpy std,释放 GIL |
imwrite |
11.5s | 证据图写盘(非实体过滤后自然减少) |
因为瓶颈都在 numpy 里,线程池有效且不需要跨进程序列化大数组。
把「单图检测 + 判据 + 证据图」抽成 _process_one_image(job),用
ThreadPoolExecutor(max_workers=min(4, cpu-1)) + ex.map 保序合并。
实测(9 篇撤稿论文):平均 96s/篇 → 18.3s/篇。图多的收益更大
(26581161 有 37 张图:46.6s → 19.7s)。
9.12 报告分组要以 hit_tier 为唯一来源
报告原本自己又算一遍分组(level<=2 / ==3 / else),和 hit_tier() 是两套逻辑。
改 hit_tier 后报告纹丝不动,排查了半天。
现在报告直接按 h["tier"] 分桶。
另外:NCC ≥ 99.5% 的命中不能放进「参考」。即使带伪影解释(周期性结构、
柱体图形),99.7% 且水平镜像仍是强信号,应进「建议进一步核实」。
hit_tier 里加:
if (h.get("ncc") or 0) >= 0.995:
return "further"
9.13 Word 导出:报告要发给导师/编辑(服务层实现)
.md 不方便发给导师/编辑。core/docx_export.py 做 Markdown → DOCX:
- 只覆盖报告实际用到的语法:标题、列表、表格、引用、
**粗体**、--- - 证据图在 md 里写成
,正文不出现文件名 - 导出时按
base_dir解析相对路径并嵌图,宽度 15.5cm
坑:图片行会先被无序列表分支吃掉(-  被当成列表文字)。
图片判断必须提到列表分支之前,并先剥掉 - 前缀。
坑:图多时 Word 会到 15.7MB。_shrink_for_docx() 用 PIL 缩到宽 1150px、
JPEG q80,降到 2.0MB。
9.14 真实论文误判验证:白底图是关键分界(重要)
撤稿论文本身就有重复,看不出误判率。必须拿未撤稿的正常论文做基线。
样本:6 篇 OA 原始研究(89 张图),含 Nature 三篇(Lung adenocarcinoma /
senescence atlas / cGAS-STING)。抓取走
https://pmc.ncbi.nlm.nih.gov/articles/PMCxxxx/ 的 HTML 解析 CDN 图
(Europe PMC 的 /articles/*?pdf=render 与 bin/*.jpg 对脚本一律 403)。
第一次跑的结果:10 条「优先核实」,逐条看证据图,9 条是误判——
框全落在示意图文本框、统计表格行、带误差棒的柱体上。
对比 9 篇撤稿论文:优先核实最高 NCC 全部 ≥0.9916;这 6 篇正常论文
最高只有 0.9851。分界其实很清楚,但根因不是相似度,而是图类型。
整图指标实测(_fig_stats):
| 类型 | 白底占比 | 灰度级数 | 边缘密度 | 中间调 |
|---|---|---|---|---|
| 示意图/表格/柱状图 | 0.70–0.84 | 119–256 | 0.050–0.120 | 0.10–0.19 |
| 照片(WB/荧光/染色,PDF 提图) | 0.12–0.14 | 173–179 | 0.043–0.059 | 0.26–0.29 |
只有白底占比分得开,其余指标重叠。
最终规则(hit_tier):
if (h.get("bg_white") or 0) > 0.55:
# 白底图里的相似多半是文字块/图形元素
if not ((h.get("ink") or 0) > 0.60 and (h.get("sd") or 0) > 50):
return "further" # 降级,不跳过
例外不能省:26459241(撤稿)的真重复就在白底 WB 图上,
命中 ink 0.83–0.92、sd 63–77(条带暗区,不是文字块)。
一刀切会让它从 6 条优先掉到 0 条。
同理 ncc ≥ 0.995 那条规则也要给实心纹理开口子:
28750740 的 p5_x47 NCC=0.9979 水平镜像、ink 0.86、sd 99 —— 真重复。
不整图跳过的理由:白底 WB 也会被判白底,跳过就真漏检了;降级只是不进
「优先」,报告里照样列出、照样有证据图。
最终双向结果:
| 样本 | 图数 | 优先核实 |
|---|---|---|
| 未撤稿 6 篇 | 89 | 1 条(彩色同心圆示意图,无白底,判据覆盖不到) |
| 已撤稿 9 篇 | 112 | 102 条,ground truth(23486834 图 3)命中 |
未覆盖的剩余误判类型:无白底的彩色示意图(分子机制图、通路图),
白底判据抓不到,需要靠饱和度/纯色块判据,暂未做。
回归基线(以后改判据就拿这两组对照,别只看撤稿论文):
| 样本 | 图数 | 期望「优先核实」 |
|---|---|---|
| 未撤稿 6 篇(36544018 / 37020004 / 37532932 / 38459338 / 39966355 / 40500258) | 89 | ≤1 条 |
| 已撤稿 9 篇(22902401 / 23333094 / 23486834 / 26459241 / 26581161 / 27203442 / 28750740 / 31530503 / 33578038) | 112 | 约 100 条,且 23486834 必须命中图 3 |
抓正常论文样本的可用路径(Europe PMC 直连会 403):
# 文章页 HTML 里解析 CDN 图,比拿 PDF 干净(无渲染伪影)
page = "https://pmc.ncbi.nlm.nih.gov/articles/%s/" % pmcid
html = requests.get(page, headers=UA).text
urls = re.findall(r'https://cdn\.ncbi\.nlm\.nih\.gov/pmc/blobs/[^"\'\s]+?\.(?:jpg|jpeg|png)', html)
# 过滤掉 "thumb" 的缩略图
9.15 cv2.imwrite 在中文路径下静默失败(必看)
Windows 上 cv2.imwrite("D:/桌面/报告/evidence/a.png", img) 会返回 False 且不抛异常,
证据图一张都写不出来,但报告照常生成、照常引用那些不存在的文件——
从报告表面完全看不出问题,只有打开 evidence/ 目录才发现是空的。
触发条件很常见:输出目录含中文(桌面下的中文文件夹、论文中文名)。
修法(已内置 _imwrite_u):
def _imwrite_u(path, img):
ok, buf = cv2.imencode(os.path.splitext(path)[1].lower() or ".png", img)
if not ok:
return False
buf.tofile(path) # 走 Python 文件写入,Unicode 路径可用
return True
脚本里 4 处写图已全部替换。自己再写脚本时也要用这个,别直接用 cv2.imwrite。
cv2.imread 同理会返回 None,读图请用:
im = cv2.imdecode(np.fromfile(path, dtype=np.uint8), cv2.IMREAD_COLOR)
9.16 Word 导出(--docx)
scripts/docx_export.py 把 report.md 转成 docx:
- 覆盖报告实际用到的语法:标题、列表、表格、引用、
**粗体**、--- - 认得两种图片写法:
和证据图:\evidence/x.png``
(本脚本的报告用后者) - 证据图按
base_dir解析相对路径并内嵌,宽 15.5cm - 图片先经
_shrink_for_docx()压到宽 1150px / JPEG q80;
不压的话 20 多张证据图会让 docx 到 15MB,压完约 2MB
坑:图片行若带列表标记(- ),会被无序列表分支先吃掉、
当成普通文字塞进段落。图片判断必须放在列表判断之前,并先剥掉 - 前缀。