批量加水印:Python + OpenCV 一次给一文件夹图盖章

举报
柠檬-夏日清爽 发表于 2026/09/26 22:27:28 2026/09/26
【摘要】 用 Python + OpenCV 写批量加水印工具:遍历目录、对每张图 putText 画字 + addWeighted 做半透明叠加、输出到独立目录。

一、背景:给图片加水印,别一张张手动来

要把一批图片(作品集、产品图、活动照)都打上水印,Photoshop 一张张打开、拖文字、调位置、导出——十张就烦,一百张能让人崩溃,而且每张位置还可能不齐。这种"对一批文件做同一个确定性操作"的活,最适合写成脚本:指定文件夹、指定水印内容和位置,一条命令全部处理完,结果一致、可重复。

本文用 Python + OpenCV 做一个批量加水印工具:遍历输入目录的图片,在每张右下角叠上半透明水印文字,输出到另一个目录。讲清 OpenCV 怎么读图、怎么画半透明文字、怎么批量遍历。具体 API 以官方文档为准,思路是重点。

二、为什么用 OpenCV 而不是 Pillow

加水印 Pillow 也能做,OpenCV 的优势在"图像就是 numpy 数组",能做更灵活的合成、后续要加缩放/滤波也顺。本文选 OpenCV,一来覆盖常见计算机视觉场景(顺便熟悉 cv2),二来半透明叠加用数组运算很直观。

核心就两步:读图为 BGR 数组 → 在数组上画文字/叠加 → 写回文件。OpenCV 读进来的是 numpy 数组,所有操作都是对数组算,理解这一点后面都好懂。

三、单张加水印:半透明文字

import cv2

def add_watermark(img_path, out_path, text="© demo"):
    img = cv2.imread(img_path)
    h, w = img.shape[:2]
    # 水印位置:右下角留边
    pos = (int(w * 0.78), int(h * 0.92))
    # 先画不透明底字,再做半透明叠加
    cv2.putText(img, text, pos, cv2.FONT_HERSHEY_SIMPLEX,
                1.2, (255, 255, 255), 2, cv2.LINE_AA)
    cv2.imwrite(out_path, img)

cv2.imread 读成 numpy 数组(注意是 BGR 顺序,不是 RGB);putText 在图上直接画文字,pos 用图片宽高比例算,保证大图小图位置一致;FONT_HERSHEY_SIMPLEX 是内置字体,1.2 字号、(255,255,255) 白色、2 线宽;LINE_AA 抗锯齿让字边缘平滑。画完 imwrite 写回。这里画的是不透明白字,最简单可用。

四、半透明:让水印不挡画面

纯不透明字在某些图上很刺眼。更优雅的是半透明水印——字带 alpha,叠在原图上若隐若现。OpenCV 做半透明要用"alpha 合成",对一片区域混合:

import numpy as np

def add_watermark_alpha(img, text, alpha=0.5):
    overlay = img.copy()
    cv2.putText(overlay, text, (int(img.shape[1]*0.78), int(img.shape[0]*0.92)),
                cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255,255,255), 2, cv2.LINE_AA)
    # 原图 * (1-alpha) + 文字层 * alpha
    return cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0)

思路是:复制一份图,在上面画白字得到 overlay;再用 addWeighted 把 overlay 按 alpha、img 按 1-alpha 加权相加,得到"原图淡、白字按透明度浮现"的效果。alpha=0.5 即半透明,调小更淡、调大更显。这种合成比直接 putText 观感好很多,是水印的常用做法。

五、批量遍历整个文件夹

单张函数有了,批量就是遍历目录、调函数、结果写另一目录:

import os, glob

def batch(in_dir, out_dir, text):
    os.makedirs(out_dir, exist_ok=True)
    for path in glob.glob(os.path.join(in_dir, "*.[jp][pn]g")):
        name = os.path.basename(path)
        img = cv2.imread(path)
        if img is None:            # 读失败的跳过,不中断整体
            print("跳过:", name); continue
        out = add_watermark_alpha(img, text)
        cv2.imwrite(os.path.join(out_dir, name), out)
    print("处理完成")

glob 匹配 jpg/png,逐张读、处理、写到 out_dir。两点保护:第一,os.makedirs(..., exist_ok=True) 目标目录不存在就建;第二,读图返回 None(文件损坏或非图片)时 continue 跳过、打印提示,不让一张坏图中断整个批处理。out_dir 和 in_dir 分开,符合"保留原始、产出新的"原则,不会覆盖原图。

六、几个注意点

第一,中文水印可能变方块。OpenCV 的 putText 用的是内置位图字体,不含中文字形,画中文会显示成方块或乱码。要打中文水印,得用支持中文的字体文件(如把文字先用 Pillow/FreeType 渲染成图再叠加),或加载 TTF 字体。本文用纯英文/符号水印规避,需要中文请换字体方案,具体以所选库文档为准。

第二,BGR 顺序坑。OpenCV 读图是 BGR,(255,255,255) 在你以为是白、其实是蓝红通道对调的"白"(巧合白色不受影响),但指定彩色(如红色 (0,0,255))时要记住是 BGR,写 (0,0,255) 才是红。和 matplotlib/PIL 的 RGB 搞混是常见 bug。

第三,输出格式与质量。imwrite 写 jpg 默认有压缩,大量处理可能累积劣化;要保质量可指定 cv2.imwrite(out, img, [cv2.IMWRITE_JPEG_QUALITY, 95]),或输出 png 无损。按你对画质和体积的要求选。

第四,尺寸与字号。字号用固定 1.2 在不同分辨率图上视觉大小不一。更稳的是按图片短边比例算字号(font_scale = h / 720 之类),保证大图小图水印占比一致,别小图水印占半屏、大图小到看不见。

第五,别覆盖原图。再次强调 out_dir 单独放,万一水印参数不满意,原图还在能重跑。批量操作覆盖原始文件是高危动作,务必输出到新目录。

七、小结

批量加水印 = 单张叠加函数 + 目录遍历。单张用 cv2.imread 读成数组、putText 画字、addWeighted 做半透明、imwrite 写出;批量用 glob 遍历、结果写独立 out_dir、读失败跳过不中断。核心坑:OpenCV 内置字体不支持中文(要中文换字体方案)、读图是 BGR 顺序、字号按图比例算才一致、输出到新目录别覆盖原图。具体 API 参数以官方文档为准。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。