尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Overleaf LaTeX多行图片排版指南:浮动体、subfigure与宽度控制

Overleaf LaTeX多行图片排版指南:浮动体、subfigure与宽度控制 在Overleaf里写论文的朋友多半都经历过这样一幕明明只是想插两张对比图用回车分了两行编译出来图片却全挤在一行或者干脆跑到下一页去了。我第一次用LaTeX排多行图片时足足折腾了一下午最后翻遍文档才发现多行图片的关键根本不在“换行符”而在浮动体、盒子和图片宽度这三件事上。这篇内容想解决的就是这个具体问题在Overleaf里如何用LaTeX把多张图片排成多行、多列并且编号、引用、间距都规规矩矩。不管你是刚接触LaTeX的新手还是已经用过一段时间但一遇到多图就卡壳的同学这套方案都可以直接照着抄。先给结论多行图片的本质是在一个figure浮动体内摆放多个子图。子图可以是subfigure环境、minipage盒子也可以只是多个\includegraphics命令排列。具体用哪种取决于你想要子图编号还是只想纯拼图。下面我从浮动体机制讲起把所有细节和踩过的坑一次说清楚。1. 图片不听使唤先弄清浮动体机制再谈多行排版1.1 浮动体是LaTeX排版的基本单位很多新手第一次在Overleaf里插入图片时会发现图片出现的位置和代码里写的位置完全对不上。我在代码里明明把图放在第三页编译出来它却出现在第五页顶部。这不是Overleaf的问题而是LaTeX的浮动体机制在起作用。LaTeX里的figure环境是一个浮动体它的定位参数由[htbp]控制。h代表here当前位置t代表页面顶部b代表页面底部p代表单独一页。默认情况下LaTeX会按优先级把浮动体放到它认为“版面最合适”的位置而不是代码所在的位置。你可以把它理解成排版编辑在人工画版如果当前页放不下编辑就会把图挪到下一版面的顶部或底部。明白这个机制后再看多行图片思路就清晰了多行图片并不是简单地用回车把图片分成两行而是要在同一个figure浮动体内用多个子图或者多个盒子构造出“多行布局”。浮动体只负责把这一整组图片挪到合适的位置而组内的排版完全由我们自己控制。1.2 多行图片的典型场景与需求拆解我在实际写论文和帮学生改论文时发现多行图片的需求基本集中在三类场景A算法对比实验图。通常要放两张或四张结果图两行两列排列每张图有自己的子编号(a)(b)(c)(d)。场景B网络结构或流程示意图拆解。一张大图由多个小图拼成每一行代表一个阶段可能三行三列也可能每行列数不同。场景C数据增强或样本展示。九张缩略图排成3×3不需要子标题只需要整体一个图注。这三类场景对应着不同的排版诉求。场景A需要子图编号和引用能力场景B需要灵活的列宽控制场景C只需要简单拼图。如果你不先明确要哪种上手就开始写代码大概率会写一半发现排版方式不适合还得推倒重来。所以我的建议是先把需求拆清楚再回到下面介绍的方案里对号入座。2. 动手前把三件事定下来编译器、宏包、图片文件2.1 Overleaf中的编译器选择在开始写多行图片代码之前需要先确认Overleaf的编译器设置。编译器决定了你用的宏包是否兼容、中文字体能不能正常显示、图片格式有没有坑。Overleaf默认是pdfLaTeX适合绝大多数英文论文场景。如果你的文档使用ctex宏包写中文强烈建议把编译器切到XeLaTeX。原因很简单XeLaTeX对中文字体和系统字体的支持要比pdfLaTeX好得多直接用\usepackage{ctex}就能编译中文不用手动配置字体映射。切换方法也很简单在Overleaf左上角点击Menu找到Compiler下拉框选择XeLaTeX即可。切换之后记得重新编译一次因为宏包缓存会失效。2.2 必备宏包清单与职责多行图片涉及几个宏包它们的职责各不相同。下面这张表是我长期使用的组合你可以直接照抄。宏包作用建议graphicx提供\includegraphics命令必须float提供[H]强制定位参数强烈建议subcaption提供subfigure子图环境与子图编号多子图必须caption调整图注格式和间距可选但很好用这里特别提醒一下不要再用老旧的subfigure宏包它已经废弃多年和caption宏包会产生严重冲突编译出来子图编号经常错乱。现在主流方案是subcaption它内部会加载caption的功能所以只要写\usepackage{subcaption}就行了。如果你只是想纯拼图不想要子图编号那连subcaption都可以不加载只需要graphicx和float就够了。后面第3节会详细对比。2.3 图片文件上传与管理规范Overleaf左侧栏可以上传图片文件也可以把图片拖进文件夹。图片文件的命名和路径看着是小事但在多行图片场景里命名不规范会直接导致编译失败。我有一次给图命名成fig 1.png带了一个空格Overleaf编译直接报File not found。原因是LaTeX读取文件名时把空格当成命令边界。另外中文字符文件名在部分编译器下也会出问题。正确做法是文件名只用英文字母、数字和下划线比如fig_loss_curve.pdf、result_row1.png。图片格式方面矢量图优先用PDF位图用PNG或JPG。EPS老格式兼容性较差不建议在Overleaf里使用。如果你有目录结构比如figures/chapter1/fig1.png引用时要写相对路径\includegraphics{figures/chapter1/fig1.png}。我习惯在导言区加一行\graphicspath{{figures/}{figures/chapter1/}}这样写图片名时就不用带路径前缀了。3. 三套多行图片排版方案按场景选型3.1 subfigure环境最省心的多子图方案如果你的多行图片需要子编号(a)(b)(c)或者需要在正文里引用某一张子图直接用subcaption宏包提供的subfigure环境。这是目前最标准、也最推荐的方案。一个两行两列的完整示例\documentclass{article} \usepackage{graphicx} \usepackage{subcaption} \usepackage{float} \begin{document} \begin{figure}[H] \centering \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{fig1.png} \caption{网络结构} \label{fig:struct} \end{subfigure} \hfill \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{fig2.png} \caption{损失曲线} \label{fig:loss} \end{subfigure} \\ \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{fig3.png} \caption{测试精度} \label{fig:acc} \end{subfigure} \hfill \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{fig4.png} \caption{混淆矩阵} \label{fig:matrix} \end{subfigure} \caption{本文方法在四个维度上的实验结果} \label{fig:overall} \end{figure} \end{document}逐行解释一下关键点。\begin{subfigure}{0.45\linewidth}这一行的参数是子图的宽度0.45表示占当前行宽的45%。两个0.45加起来是0.9剩下的0.1由\hfill自动填充确保两张图一个靠左一个靠右整体居中。\\表示换行后面的两个子图就排在第二行。这里有个细节值得注意\label必须放在每一个subfigure内部的\caption之后这样\ref{fig:struct}引用出来才是(a)\ref{fig:overall}引用出来是整个大图的编号。如果把\label放在整个figure末尾而不放在子图内引用出来的编号会乱。一行放三张时把子图宽度改成0.32\linewidth三个加起来0.96留4%做间距一行放四张就改成0.23\linewidth。为什么不是0.25因为4张0.25加上间距会超过一行宽度触发Overfull hbox警告排版很难看。这个问题我在第5节还会详细讲。3.2 minipage方案手动控制每一块布局subfigure环境虽然方便但它的子图宽度必须统一布局相对固定。如果你需要每一块图片宽度不同或者每行图片数量不对称用minipage更灵活。minipage本质上是创建一个特定宽度的盒子盒子内部可以放图片也可以放图片加标题。一个混合布局示例第一行两张宽图第二行三张窄图\begin{figure}[H] \centering \begin{minipage}{0.48\linewidth} \centering \includegraphics[width\linewidth]{figA.png} \caption{方案A的结果} \end{minipage} \hfill \begin{minipage}{0.48\linewidth} \centering \includegraphics[width\linewidth]{figB.png} \caption{方案B的结果} \end{minipage} \vspace{10pt} \begin{minipage}{0.30\linewidth} \centering \includegraphics[width\linewidth]{figC.png} \caption{样本1} \end{minipage} \hfill \begin{minipage}{0.30\linewidth} \centering \includegraphics[width\linewidth]{figD.png} \caption{样本2} \end{minipage} \hfill \begin{minipage}{0.30\linewidth} \centering \includegraphics[width\linewidth]{figE.png} \caption{样本3} \end{minipage} \caption{不同方案的定性对比结果} \label{fig:compare} \end{figure}注意几点minipage内部的\caption生成的是大图编号不是子图编号。也就是说这个方案里每一块都有独立的\caption时实际上它们会变成多个编号的图这可能不是你想要的效果。如果不需要每块都有标题就不写\caption只放图片。如果想要子图编号但又想自定义宽度可以把subfigure嵌套在minipage里面不过这种情况很少见不是常规用法。minipage方案适合排版不规律、不需要子编号的场景。比如你在写技术报告想把两张细节放大图和一张整体图混合排列这个方案最顺手。3.3 不带子标题的纯图片拼接第三种方案最简单就是在figure环境里直接写多个\includegraphics命令用\hfill做行内间距用\\换行。适合纯展示不需要子图编号的情况比如九宫格样例图、多张结果对比图。\begin{figure}[H] \centering \includegraphics[width0.30\linewidth]{img1.png}\hfill \includegraphics[width0.30\linewidth]{img2.png}\hfill \includegraphics[width0.30\linewidth]{img3.png} \vspace{10pt} \includegraphics[width0.30\linewidth]{img4.png}\hfill \includegraphics[width0.30\linewidth]{img5.png}\hfill \includegraphics[width0.30\linewidth]{img6.png} \caption{六组实验结果的直观对比} \label{fig:six} \end{figure}这种写法的优点是代码量最少不需要加载subcaption宏包也不会有子图编号带来的标签问题。缺点是每张图都没有独立的\caption和\label正文里无法单独引用某一张图。如果你的论文要求“如图3(a)所示”这种引用必须用方案一。3.4 方案对比与选择建议我把三套方案的差异整理成了表格方便你快速选型。方案子图编号行内宽度控制代码复杂度适用场景subfigure环境自动生成(a)(b)(c)统一宽度规整中论文、需要引用子图的场景minipage盒子无自动编号每块可单独定制中布局灵活的技术报告多图直接拼接无编号统一宽度低样例图、纯展示图我的建议是写学术论文优先用subfigure方案它生成的子图编号自动且规范引用也方便写技术博客、内部报告或者PPT素材用minipage和多图拼接更省事。你先确定有没有“子图引用”需求再决定用哪套方案。4. 尺寸、间距与自动换行把多行图片排整齐的细节4.1 相对宽度的计算逻辑多行图片最容易出现的问题就是图片太大一行放不下。很多新手直接写\includegraphics[width5cm]{fig.png}结果换一个页面宽度就乱了。正确做法是使用相对宽度单位\linewidth、\textwidth、\columnwidth。\textwidth是正文区域的宽度\columnwidth是当前栏的宽度双栏模板中\columnwidth约等于\textwidth的一半。\linewidth则是当前环境的行宽在普通正文里等于\textwidth但在列表环境或者minipage内部它会随环境变化。所以我在子图里一律用\linewidth这样无论图片放在单栏还是双栏都能自适应。一行放n张图时单张图宽度的估算方法是把一行宽度减去间距余量再除以图片数量。一行放两张宽度用0.45\linewidth左右一行三张用0.32\linewidth上下一行四张用0.23\linewidth左右。为什么要留余地因为图片之间通常要用\hfill填充间距。如果不留这5%左右的空白图片会紧贴在一起而且三个0.33加起来接近1一旦加上间距就可能超宽触发编译警告。4.2 图片间距控制的三个层面多行图片的间距控制分三个层面分别是图片与图片、图片与标题、图片与正文。图片与图片之间行内水平间距用\hfill最省心。\hfill是一个弹性间隔会自动把剩余空间平均分配到图片之间让整行图片两端对齐并居中。你不需要计算精确的间距值。如果不想两端对齐也可以使用\hspace{1em}固定间距但这样就需要自己算宽度比较麻烦。行与行之间的垂直间距用\\[10pt]或者\vspace{10pt}数值可以按视觉效果调整。图片与标题之间的间距默认由caption宏包控制。如果你想缩小图注和图片之间的距离可以在导言区写入\usepackage[skip4pt]{caption}这样图注离图片更近版面更紧凑。图片与正文之间的距离一般由浮动体的内部参数控制[H]强制定位时会比较紧凑[t]或[b]定位时LaTeX会自动留出间距。4.3 自动换行的实现思路很多人在搜索“latex 多行图片自动换行”时其实是想要一个效果图片写满一行后自动换到下一行不用手动加\\。LaTeX本身支持这个行为核心在于宽度控制。LaTeX排版采用的是盒子模型。每个\includegraphics会生成一个盒子盒子按顺序排成一行。如果一行中所有盒子的总宽度超过了\linewidthLaTeX就会把超出的盒子自动推到下一行。所以只要每张图片宽度设置得足够小比如0.25\linewidth连续写五张图它们会自动排成一行四张加一行一张的效果。但这种“自动换行”的行尾通常参差不齐视觉效果不好我不推荐依赖它。真正让多行图片整齐的关键还是手动换行用\\控制行数用\hfill控制行内对齐。你只要记住这个原则宽度决定每行放几张换行符决定每行从哪里断开。4.4 一个完整的两行两列案例综合上面的知识点给一个可以直接套用的两行两列模板很多实验对比图场景都能直接用\documentclass{article} \usepackage{graphicx} \usepackage{subcaption} \usepackage{float} \usepackage[fontsmall,labelfontbf]{caption} \begin{document} \begin{figure}[H] \centering \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{loss_train.png} \caption{训练损失} \end{subfigure} \hfill \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{loss_val.png} \caption{验证损失} \end{subfigure} \vspace{8pt} \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{acc_train.png} \caption{训练准确率} \end{subfigure} \hfill \begin{subfigure}{0.45\linewidth} \centering \includegraphics[width\linewidth]{acc_val.png} \caption{验证准确率} \end{subfigure} \caption{模型训练过程中的损失与准确率变化} \label{fig:train_result} \end{figure} \end{document}这里caption宏包设置了fontsmall,labelfontbf图注字体略小、“图1、图2”编号加粗整体更接近期刊排版风格。没这个需求的可以删掉这一行。5. Overleaf排多行图片最容易踩的五个坑5.1 图片位置总是乱跑这是被问得最多的问题。明明代码里图片写在第二章编译出来跑到了第三章。根本原因我在第1节说过figure是浮动体默认参数[tbp]下LaTeX会自行决定位置。解决方案有两个思路。一是用float宏包提供的[H]参数强制把图放在当前位置也就是我在示例代码里一直在用的写法。二是接受浮动体机制改用[htbp]参数让LaTeX自己优化位置同时配合\section后面加\FloatBarrier之类的命令控制浮动体不跨越章节。[H]的缺点是版面容易留白图片多时页面底部会有大片空白[htbp]虽然位置不一定精确但整体版面更自然。我的经验是投稿期刊时如果模板没有特别要求尽量用[htbp]自己写报告、做作业直接用[H]保证图和文字对应阅读体验更好。5.2 子图编号和引用编号对不上一个常见错误是在subfigure环境的\caption前面加了\label或者把\label写在\caption之前导致\ref引用的编号是空值或上一个编号。正确做法是\label一定写在\caption之后且写在对应的subfigure内部。另一个容易忽略的点是subcaption宏包默认的子图编号是英文字母a、b、c。如果你希望子图编号显示成其他形式需要修改\renewcommand{\thesubfigure}{...}这类命令。但绝大多数期刊模板都接受字母编号不建议乱改。5.3 Overfull hbox警告图片比页面还宽编译时如果看到Overfull \hbox (xx pt too wide)的警告说明某一行图片的总宽度超过了文本区宽度。这个问题在多行图片里太常见了尤其是新手在宽度上贪心三个图都用0.34\linewidth加起来就超了。排查方法点开Overleaf的日志面板搜索Overfull日志会告诉你具体是第几行的内容。回到代码把那一行的图片宽度调小。比如三个0.34改成三个0.30。如果你发现图片内容没有充满整行可以加\hfill让它们分散排列不要用\hspace硬凑。5.4 图片文件路径和格式问题在Overleaf里还经常遇到这几种情况上传的图片文件名带空格导致编译失败用了.bmp或.eps格式导致部分编译器不识别引用的路径和文件所在目录不一致导致找不到图片。应对方法前面已经说过文件名统一英文、数字、下划线格式用PDF、PNG、JPG路径写准确。还有一个小细节如果Overleaf左侧文件树里能看到图片但编译报找不到文件检查一下是不是文件名大小写写错了。Linux系统下文件名区分大小写Fig1.png和fig1.png是两个文件。5.5 编译超时和引用显示成问号Overleaf免费版有编译时长限制多行图片多的时候如果每张图都是高分辨率PNG很容易编译超时。解决的思路有两个方向压缩图片体积或者用\includeonly只编译当前章节。Overleaf的自动编译默认只跑一次交叉引用可能显示成问号??手动点一次Recompile通常能解决。如果是\ref引用显示成??还有一个常见原因是.aux文件里没有生成标签。第一种编译时标签还没写入第二次编译才生效。在Overleaf里直接多点一次Recompile就好不用紧张。6. 进阶玩法跨页大图、横向图与矩阵排列6.1 跨页多行图片延续编号的排版处理有时候图片非常多一个figure环境根本放不下需要拆成两个页面但读者希望它们属于同一个图表编号。这时候用caption宏包的\ContinuedFloat命令就可以实现。思路是这样的第一页放一个figure环境使用\caption生成编号第二页开一个新的figure环境在环境开头加\ContinuedFloat这样第二个figure的编号会延续第一个而不是生成新编号。\begin{figure}[H] \centering \includegraphics[width0.45\linewidth]{part1.png}\hfill \includegraphics[width0.45\linewidth]{part2.png} \caption{模型总体架构上半部分} \label{fig:arch_up} \end{figure} \begin{figure}[H] \ContinuedFloat \centering \includegraphics[width0.45\linewidth]{part3.png}\hfill \includegraphics[width0.45\linewidth]{part4.png} \caption{模型总体架构下半部分} \label{fig:arch_down} \end{figure}跨页多行图片在高分论文里很常见尤其是超长网络结构图。用\ContinuedFloat能让两张跨页图共享同一个编号读者一看就知道它们是同一张图拆开的。6.2 双栏模板中横跨双栏的大图IEEE等双栏模板中普通figure只能占一栏宽度这对多行图片很不友好。一行放两张子图后每张宽度约0.45栏宽图会非常小。解决办法是用figure*环境它可以横跨双栏。\begin{figure*}[t] \centering \begin{subfigure}{0.30\textwidth} \centering \includegraphics[width\linewidth]{a.png} \caption{第一张} \end{subfigure} \hfill \begin{subfigure}{0.30\textwidth} \centering \includegraphics[width\linewidth]{b.png} \caption{第二张} \end{subfigure} \hfill \begin{subfigure}{0.30\textwidth} \centering \includegraphics[width\linewidth]{c.png} \caption{第三张} \end{subfigure} \caption{跨双栏排列的三张子图} \label{fig:three} \end{figure*}注意这里子图宽度用的是\textwidth而不是\linewidth。因为在双栏环境里\linewidth在栏内等于栏宽而figure*横跨双栏后实际宽度是双栏宽度用\textwidth才能占满整行。这是我当初在IEEE模板上花了不少时间才搞明白的细节。6.3 使用sidewaysfigure做横向大图有的多行图片内容太宽正常页面方向放不下尤其是长条形的时间线图、频谱图。用rotating宏包提供的sidewaysfigure环境可以把整个图旋转90度放在页面上。\usepackage{rotating} \begin{sidewaysfigure}[H] \centering \begin{subfigure}{0.45\textwidth} \includegraphics[width\linewidth]{wide1.png} \caption{第一部分} \end{subfigure} \hfill \begin{subfigure}{0.45\textwidth} \includegraphics[width\linewidth]{wide2.png} \caption{第二部分} \end{subfigure} \caption{横向放置的长图对比结果} \label{fig:wide} \end{sidewaysfigure}sidewaysfigure里照样可以用subfigure和\includegraphics只是整个页面被旋转了。需要注意的是sidewaysfigure通常单独占一页不适合放在文字中间一般用于附录或结果总览。6.4 规律矩阵排列的批量写法如果你要排九宫格甚至更多图手工复制代码会非常繁琐。一个笨办法是在循环里使用\foreach这个命令来自pgffor宏包LaTeX默认通过pgf加载。不过\foreach在figure里的用法稍绕且调试不方便我自己实际用得不多。更稳的方式是利用\newcommand把单个子图封装成命令再手工排列。比如定义\newcommand{\oneimg}[2]{% \begin{subfigure}{0.30\linewidth} \centering \includegraphics[width\linewidth]{#1} \caption{#2} \end{subfigure}% }然后在figure里写\begin{figure}[H] \centering \oneimg{img1.png}{图一}\hfill \oneimg{img2.png}{图二}\hfill \oneimg{img3.png}{图三} \\ \oneimg{img4.png}{图四}\hfill \oneimg{img5.png}{图五}\hfill \oneimg{img6.png}{图六} \caption{六张子图的规整排列} \end{figure}这种方式牺牲了一点灵活性但胜在代码简洁改图名时只需要改一行。矩阵排列的关键还是宽度总和不超过\linewidth三张图用0.30再加\hfill六张图排两行整体效果很规整。我在实际处理多行图片时最常用的组合是subcaption加float宏包子图宽度用0.44\linewidth或0.30\linewidth行间距用\vspace{8pt}。这个组合在Overleaf上编译稳定在多数期刊模板下也不会出格式冲突。如果你手头正在写论文建议直接用第3.1节里的模板跑一遍先把两块图的排版跑通再扩展成三块、四块。多行图片最忌讳一上来就追求复杂布局从最简单的两行两列开始把浮动体、子编号、宽度三个概念吃透了后面无论是九宫格还是跨页图都是同一套思路的延伸。
返回列表