← 所有文章

怎么把 NotebookLM 生成的水印干净地去掉

NotebookLM 生成的幻灯片很好看,但每一页角落都印着水印,于是我写了个工具把它真正去掉。


NotebookLM 刚把我丢进去的一堆研究笔记,生成了一份幻灯片。效果确实不错,排版干净,结构合理,要是自己在 Keynote 里做,少说得花一个小时。可我扫到每一页的右下角,都有一个小小的灰色字样:Gemini Notebook。自己看没关系,但要拿去给别人看,这就不行了。

这些内容是我的。NotebookLM 是根据我自己的笔记生成的,Google 也明确说过不会对工具产出的内容主张所有权。说到底,这就是清理自己的导出文件,跟去掉设计稿里的占位 logo 是同一类事,跟绕付费墙、盗版都扯不上关系。

第一反应当然是最直接的,在水印上盖一个白色方块。可这招只在幻灯片背景是纯白色的时候管用。NotebookLM 的幻灯片有渐变、有细腻的纹理,角落常常带颜色,有时候一条分割线正好穿过水印所在的位置。方块盖上去,在哪张幻灯片上都是一个方块。这份幻灯片一共三十页。打开 Preview,一张张对着背景色调方块,手动画三十次矩形,这不是解决方案,这是给自己加了一份工作。想让它在这种规模下真正干净,就得让程序自己理解水印背后到底是什么,一次性把整份文件都处理掉。

修复前:幻灯片右下角印着 Gemini Notebook 水印

修复后:水印消失,背景被还原,不是简单遮住

不能靠硬编码去找水印

“找水印”最简单的做法是裁出右下角,找深色像素,完事。可一旦这个角落本身就有深色照片或者色块,程序立刻把真实内容当成水印去处理,画面就毁了。

真正管用的做法,是把两个不够强的信号叠加起来,而不是指望某一个信号单独扛住。第一层是对比度检测,先用中值滤波估算局部背景色(按区域算,而不是假设整个角落是同一种颜色),再挑出明显突出的部分,文字、图标,或者任何一块异常深色的区域。第二层是模板匹配,把水印的文字按不同尺寸渲染出来,在候选区域里滑动比对,看和真实像素的相关性够不够高。单靠哪一层都不靠谱。一块深色可能只是页面内容本身,一次模板匹配的高分也可能只是巧合命中了不相关的文字。两个条件都满足才算,误判几乎就消失了。

有个细节是我事先完全没想到要处理的。代码得先判断这张幻灯片是”浅底深字”还是”深底浅字”,从搜索区域边缘的一圈像素里取样判断,因为背景极性不同,相关性的正负号会反过来。少了这一步,匹配器会很自信地找到水印的”负片”,出现在完全不该出现的地方。

还有一处,让我特别庆幸自己没有把水印文字写成一个死字符串。我自己的导出文件上都是Gemini Notebook。可做到中途,我翻到几份更早的导出文件,水印写的是”NotebookLM”,是 Google 改名之前用的老标签。检测器现在两种都认,当前标签排在前面优先命中,旧标签留着,好让那些老文件也能清干净。要是我当初把Gemini Notebook写死成唯一的字符串,那些老文件的水印就一个都匹配不上。

一个 bug,教我明白”去掉”到底是什么意思

PDF 里”去掉文字”其实有两种做法,只有一种是真的。在文字上盖一个白色矩形,哪怕颜色对得再准,底层的文字对象依然原封不动地留在 PDF 的内容流里。全选一下,或者拿任何一个文本提取工具跑一遍,水印文字照样在,只是被挡住了看不见。这不是去掉,这是贴了张贴纸。

真正的办法是用 PyMuPDF 的编辑(redaction)接口。先标记水印文字所在的矩形区域,再用 text=PDF_REDACT_TEXT_REMOVE 应用编辑,文字对象就从内容流里被真正删除了。周围的矢量背景,渐变、图形,不管是什么,完全不受影响,因为编辑操作只处理标记区域内的内容。这跟”往白板上刷漆盖住”和”真的把白板擦干净”是两件不同的事。

水印旁边的小图标不属于文字层,编辑操作管不到它。所以还是得靠像素级的检测加修复来处理,范围缩小到文字原本旁边那一小块区域就够了。

真正让我花了不少时间排查的一个 bug 是这样的。生成去除用的蒙版时,我只取了检测阶段里高对比度的那部分像素,想法是这样蒙版更精准,不会误伤真实内容。这个想法本身没错,可不少水印的文字背后,还垫着一层几乎透明的胶囊形浅色底,而这层底色的对比度根本达不到判定阈值。只擦掉文字笔画,那层底色的”鬼影”就留在原地,一个若隐若现的矩形,反而比原来的水印更显眼。修复方式其实简单得有点让人哭笑不得:别再纠结蒙版里哪些像素该擦哪些不该擦,直接把整个水印的边界框全部填满,当成一整块区域来修复。

修复背景用的是从同一页里取一块干净的相邻区域来覆盖,而不是常规的 inpainting 算法。做法是比较候选源区域边缘一圈像素和目标区域边缘一圈像素的差异,挑出最贴合的那一块贴过去,边缘再做一点透明度过渡。普通的 cv2.inpaint 也能用,但遇到带纹理或者虚线底纹的背景,它会把细节糊成一片模糊。从三厘米外挖一块真实的背景贴过去,纹理和颗粒感都能保住。

真正让我有感觉的时刻,是拿自己那份幻灯片去跑一遍。命令行指向那份三十页的文件,一条进度条自己一页一页跑完全部三十页,省掉了三十次打开 Preview 对着背景描方块的功夫。三十种不同的背景,三十个水印,没有一页需要回头再看一眼。

一天做完之后,想法变了

这东西是一口气做完的。检测、修复、PDF/PPTX/图片各自的处理逻辑、打包成单文件可执行程序、给 README 配上前后对比图,几个 commit,没有拖成一个长期项目。让我意外的不是图像处理那部分,一旦想清楚”两个弱信号叠加”这个思路,剩下的都是水到渠成。真正难的地方,是各种”忍住不做”的克制:不去碰水印周围的内容,不假设背景只有一种极性,不把文字写死成一个字符串,不把”看不见”和”真的没了”混为一谈。

一开始我以为水印去除工具就是”裁剪工具加几个步骤”。做完才发现,这其实是一个披着命令行外壳的图像取证问题。最后做出来的工具能处理 PDF、PPTX 和普通图片,能批量处理整个文件夹,也打包成了单文件可执行程序,给不想装 Python 环境的人用。这些功能都没有改变它存在的初衷。一份本来就很不错的导出文件,只差一个瑕疵,而这个瑕疵值得被修好,前提是修的过程别搞出比它更大的问题。

代码在 GitHub 上,github.com/encoreshao/nlm-unwatermark,MIT 协议。pip install -r requirements.txt,指向一个文件或者整个文件夹,处理完的时间比在 Preview 里打开第一页还快。

Encore Shao
Encore Shao

全栈工程师 & AI 研究员,就职于上海 Ekohe。10 年以上经验,专注于 Rails 应用与 Agentic AI 系统。