当前位置: 代码网 > it编程>前端脚本>Python > 基于Python+OpenCV实现图片批量缩放与加水印功能

基于Python+OpenCV实现图片批量缩放与加水印功能

2026年05月09日 Python 我要评论
在日常工作中,经常会遇到这样的图片处理需求:商品图片上传前需要统一尺寸。活动照片需要批量压缩后再发送。资料截图需要加上公司名或项目名水印。文件夹里有几十张、几百张图片,不适合一张张手工处理。这些任务非

在日常工作中,经常会遇到这样的图片处理需求:

  • 商品图片上传前需要统一尺寸。
  • 活动照片需要批量压缩后再发送。
  • 资料截图需要加上公司名或项目名水印。
  • 文件夹里有几十张、几百张图片,不适合一张张手工处理。

这些任务非常适合用 python 自动化完成。本文使用 opencv-python 库,带你从图片读取、保存、缩放、添加文字水印开始,逐步完成一个“批量缩放并加水印”的完整小项目。

文章适合刚接触图像处理的 python 初学者阅读,不要求你提前掌握复杂的图像算法。

1. opencv 是什么

opencv 是一个常用的计算机视觉和图像处理库,可以完成图片读取、图片保存、尺寸变换、颜色处理、绘图、视频处理、人脸检测、目标检测等任务。

在 python 中,我们通常安装的是 opencv-python

pip install opencv-python

安装完成后,可以在 python 中导入:

import cv2

print(cv2.__version__)

如果能正常打印版本号,说明安装成功。

2. 图片读取与保存

opencv 读取图片使用 cv2.imread(),保存图片使用 cv2.imwrite()

import cv2

image = cv2.imread("input.jpg")

if image is none:
    raise filenotfounderror("图片读取失败,请检查文件路径")

cv2.imwrite("output.jpg", image)

需要注意的是,opencv 读取到的图片是一个 numpy 数组。它的形状通常是:

(高度, 宽度, 通道数)

例如:

print(image.shape)

输出可能是:

(1080, 1920, 3)

这表示图片高度是 1080,宽度是 1920,通道数是 3。

3. 图片缩放

图片缩放使用 cv2.resize()

3.1 按指定宽高缩放

resized = cv2.resize(image, (800, 600))
cv2.imwrite("resized.jpg", resized)

注意:cv2.resize() 的尺寸参数顺序是 (宽度, 高度),不是 (高度, 宽度)

3.2 按比例缩放

如果希望图片按 50% 缩小:

resized = cv2.resize(image, none, fx=0.5, fy=0.5)

其中:

  • fx 表示宽度缩放比例。
  • fy 表示高度缩放比例。

3.3 按目标宽度等比例缩放

实际项目中,更常见的是限制图片宽度,同时保持原始宽高比:

height, width = image.shape[:2]
target_width = 1000
scale = target_width / width
target_height = int(height * scale)

resized = cv2.resize(image, (target_width, target_height))

这样不会把图片拉伸变形。

4. 添加文字水印

opencv 可以使用 cv2.puttext() 在图片上绘制文字。

watermarked = image.copy()

cv2.puttext(
    watermarked,
    text="python opencv",
    org=(30, 60),
    fontface=cv2.font_hershey_simplex,
    fontscale=1.2,
    color=(255, 255, 255),
    thickness=2,
    linetype=cv2.line_aa
)

cv2.imwrite("watermarked.jpg", watermarked)

参数说明:

  • text:水印文字。
  • org:文字左下角坐标,格式是 (x, y)
  • fontface:字体类型。
  • fontscale:字体大小。
  • color:文字颜色,opencv 使用 bgr 顺序,不是 rgb。
  • thickness:文字粗细。
  • linetype:抗锯齿方式,cv2.line_aa 会让文字边缘更平滑。

5. 半透明文字水印

直接把文字画到图片上会比较生硬。更常见的做法是使用一张透明图层,再和原图混合。

overlay = image.copy()
output = image.copy()

cv2.puttext(
    overlay,
    "python opencv",
    (30, 60),
    cv2.font_hershey_simplex,
    1.2,
    (255, 255, 255),
    2,
    cv2.line_aa
)

alpha = 0.35
watermarked = cv2.addweighted(overlay, alpha, output, 1 - alpha, 0)

这里的 alpha 表示水印透明度。值越大,水印越明显;值越小,水印越淡。

6. 批量处理整个文件夹图片

批量处理的基本思路是:

  1. 遍历输入文件夹。
  2. 找到所有图片文件。
  3. 读取每张图片。
  4. 缩放图片。
  5. 添加水印。
  6. 保存到输出文件夹。

常见图片后缀包括:

image_extensions = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}

遍历文件夹可以使用 pathlib

from pathlib import path

input_dir = path("input_images")

for file_path in input_dir.iterdir():
    if file_path.suffix.lower() in image_extensions:
        print(file_path)

7. 中文路径兼容处理

很多初学者会遇到一个问题:图片路径里有中文时,cv2.imread() 可能读取失败,cv2.imwrite() 也可能保存失败。

为了更稳定地兼容中文路径,可以使用 numpy.fromfile() + cv2.imdecode() 读取图片,用 cv2.imencode() + tofile() 保存图片。

7.1 兼容中文路径读取

import cv2
import numpy as np


def imread_unicode(file_path):
    data = np.fromfile(str(file_path), dtype=np.uint8)
    image = cv2.imdecode(data, cv2.imread_color)
    return image

7.2 兼容中文路径保存

def imwrite_unicode(file_path, image):
    ext = file_path.suffix
    success, encoded_image = cv2.imencode(ext, image)
    if not success:
        return false

    encoded_image.tofile(str(file_path))
    return true

这两个函数在 windows 中文目录、中文文件名场景下非常实用。

8. 完整项目代码

下面是完整可运行代码。建议保存为 batch_resize_watermark.py

项目目录示例:

image_project/
├─ batch_resize_watermark.py
├─ input_images/
│  ├─ 示例图片1.jpg
│  ├─ 示例图片2.png
│  └─ 示例图片3.webp
└─ output_images/

完整代码:

from pathlib import path

import cv2
import numpy as np


image_extensions = {".jpg", ".jpeg", ".png", ".bmp", ".webp"}


def imread_unicode(file_path: path):
    """兼容中文路径的图片读取。"""
    data = np.fromfile(str(file_path), dtype=np.uint8)
    image = cv2.imdecode(data, cv2.imread_color)
    return image


def imwrite_unicode(file_path: path, image) -> bool:
    """兼容中文路径的图片保存。"""
    file_path.parent.mkdir(parents=true, exist_ok=true)

    ext = file_path.suffix
    success, encoded_image = cv2.imencode(ext, image)
    if not success:
        return false

    encoded_image.tofile(str(file_path))
    return true


def resize_keep_ratio(image, max_width: int = 1200):
    """按最大宽度等比例缩放图片。"""
    height, width = image.shape[:2]

    if width <= max_width:
        return image.copy()

    scale = max_width / width
    target_height = int(height * scale)

    resized = cv2.resize(
        image,
        (max_width, target_height),
        interpolation=cv2.inter_area
    )
    return resized


def add_text_watermark(
    image,
    text: str,
    alpha: float = 0.35,
    margin: int = 30
):
    """在图片右下角添加半透明文字水印。"""
    output = image.copy()
    overlay = image.copy()

    height, width = image.shape[:2]

    font_face = cv2.font_hershey_simplex
    font_scale = max(width / 1200, 0.7)
    thickness = max(int(width / 600), 1)

    text_size, baseline = cv2.gettextsize(
        text,
        font_face,
        font_scale,
        thickness
    )
    text_width, text_height = text_size

    x = max(width - text_width - margin, margin)
    y = max(height - margin, text_height + margin)

    # 先画一层深色阴影,提高浅色背景下的可读性
    cv2.puttext(
        overlay,
        text,
        (x + 2, y + 2),
        font_face,
        font_scale,
        (0, 0, 0),
        thickness + 1,
        cv2.line_aa
    )

    # 再画白色文字
    cv2.puttext(
        overlay,
        text,
        (x, y),
        font_face,
        font_scale,
        (255, 255, 255),
        thickness,
        cv2.line_aa
    )

    watermarked = cv2.addweighted(overlay, alpha, output, 1 - alpha, 0)
    return watermarked


def process_image(
    input_path: path,
    output_path: path,
    watermark_text: str,
    max_width: int
) -> bool:
    """处理单张图片:读取、缩放、加水印、保存。"""
    image = imread_unicode(input_path)
    if image is none:
        print(f"读取失败: {input_path}")
        return false

    resized = resize_keep_ratio(image, max_width=max_width)
    watermarked = add_text_watermark(resized, watermark_text)

    success = imwrite_unicode(output_path, watermarked)
    if not success:
        print(f"保存失败: {output_path}")
        return false

    return true


def batch_process_images(
    input_dir: path,
    output_dir: path,
    watermark_text: str = "python opencv",
    max_width: int = 1200
) -> none:
    """批量处理文件夹中的图片。"""
    if not input_dir.exists():
        raise filenotfounderror(f"输入文件夹不存在: {input_dir}")

    output_dir.mkdir(parents=true, exist_ok=true)

    image_files = [
        file_path
        for file_path in input_dir.iterdir()
        if file_path.is_file() and file_path.suffix.lower() in image_extensions
    ]

    if not image_files:
        print(f"没有找到可处理的图片: {input_dir}")
        return

    success_count = 0

    for input_path in image_files:
        output_path = output_dir / input_path.name
        success = process_image(
            input_path=input_path,
            output_path=output_path,
            watermark_text=watermark_text,
            max_width=max_width
        )

        if success:
            success_count += 1
            print(f"处理完成: {input_path.name}")

    print(f"批量处理结束,成功 {success_count}/{len(image_files)} 张")
    print(f"输出目录: {output_dir.resolve()}")


def main():
    input_dir = path("input_images")
    output_dir = path("output_images")

    batch_process_images(
        input_dir=input_dir,
        output_dir=output_dir,
        watermark_text="python opencv",
        max_width=1200
    )


if __name__ == "__main__":
    main()

运行脚本:

python batch_resize_watermark.py

如果运行成功,终端会输出类似信息:

处理完成: 示例图片1.jpg
处理完成: 示例图片2.png
处理完成: 示例图片3.webp
批量处理结束,成功 3/3 张
输出目录: d:\image_project\output_images

9. 效果演示说明

运行前,input_images 文件夹中存放原始图片:

input_images/
├─ 风景照片.jpg
├─ 商品主图.png
└─ 活动现场.webp

运行脚本后,output_images 文件夹会生成同名图片:

output_images/
├─ 风景照片.jpg
├─ 商品主图.png
└─ 活动现场.webp

处理后的图片会有两个变化:

  1. 如果原图宽度超过 max_width,会被等比例缩放到指定最大宽度,例如 1200 像素。
  2. 图片右下角会添加半透明文字水印,例如 python opencv

由于代码使用了中文路径兼容读写函数,即使文件名是 风景照片.jpg、目录名是 测试图片,也可以正常读取和保存。

10. 常见问题

10.1 为什么 opencv 的颜色是 bgr

opencv 默认使用 bgr 通道顺序,而很多其他库使用 rgb。比如白色在 opencv 中是:

(255, 255, 255)

红色不是 (255, 0, 0),而是:

(0, 0, 255)

10.2 png 透明背景会保留吗

本文代码使用 cv2.imread_color 读取图片,会把图片读成三通道 bgr,透明通道不会保留。如果你需要保留透明背景,可以使用 cv2.imread_unchanged 读取,并额外处理 alpha 通道。

10.3 水印位置如何调整

代码中的水印位置由下面几行控制:

x = max(width - text_width - margin, margin)
y = max(height - margin, text_height + margin)

这是右下角位置。如果想放到左上角,可以改成:

x = margin
y = text_height + margin

10.4 如何递归处理子文件夹

当前代码只处理输入目录第一层图片。如果要递归处理子文件夹,可以把:

input_dir.iterdir()

改成:

input_dir.rglob("*")

同时输出路径可以根据相对路径生成,避免不同子目录下的同名图片互相覆盖。

总结

本文完成了一个适合图像处理初学者的 opencv 实战项目:批量缩放文件夹图片,并添加半透明文字水印。这个项目虽然不复杂,但覆盖了图片自动化处理中的几个核心能力:

  • 使用 opencv-python 读取和保存图片。
  • 使用 cv2.resize() 等比例缩放图片。
  • 使用 cv2.puttext() 添加文字水印。
  • 使用 cv2.addweighted() 实现半透明效果。
  • 使用 pathlib 批量遍历文件夹。
  • 使用 imdecodeimencode 兼容中文路径。

掌握这些基础之后,你可以继续扩展更多功能,例如图片压缩、格式转换、添加 logo 水印、递归处理子目录、生成处理日志等,把它变成真正适合日常工作的图片批处理工具。

以上就是基于python+opencv实现图片批量缩放与加水印功能的详细内容,更多关于python opencv图片批量缩放与加水印的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com