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

 更新时间:2026年05月09日 09:45:07   作者:yuanpan  
本文通过使用OpenCV-python库,介绍了如何批量处理图片(缩放和添加水印),并提供了完整的Python代码实现,从图片读取与保存,图片缩放,文字水印添加,中文路径兼容处理,到整个文件夹批量处理,适合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图片批量缩放与加水印的资料请关注脚本之家其它相关文章!

相关文章

  • Python+DeOldify实现老照片上色功能

    Python+DeOldify实现老照片上色功能

    DeOldify是一种技术,以彩色和恢复旧的黑白图像,甚至电影片段。它是由一个叫Jason Antic的人开发和更新的。本文将利用DeOldify实现老照片上色功能,感兴趣的可以了解一下
    2022-06-06
  • OpenCV清除小面积连通域的实现方法

    OpenCV清除小面积连通域的实现方法

    本文主要介绍了OpenCV清除小面积连通域的实现方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Python中Pipreqs自动生成项目依赖清单的实现

    Python中Pipreqs自动生成项目依赖清单的实现

    本文主要介绍了Python中Pipreqs自动生成项目依赖清单的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2026-04-04
  • Python中有几个关键字

    Python中有几个关键字

    在本篇文章里小编给大家分享的是一篇关于Python中关键字个数的相关知识点内容,需要的朋友们可以学习下。
    2020-06-06
  • Python实现求解最大公约数的五种方法总结

    Python实现求解最大公约数的五种方法总结

    求最大公约数是习题中比较常见的类型,本文小编将给大家提供五种比较常见的算法,都是用Python语言实现的,感兴趣的小伙伴可以了解一下
    2022-07-07
  • 浅谈spring boot 集成 log4j 解决与logback冲突的问题

    浅谈spring boot 集成 log4j 解决与logback冲突的问题

    今天小编就为大家分享一篇浅谈spring boot 集成 log4j 解决与logback冲突的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-02-02
  • 如何利用Python分析出微信朋友男女统计图

    如何利用Python分析出微信朋友男女统计图

    这篇文章主要给大家介绍了关于如何利用Python分析出微信朋友男女统计图的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面来一起看看吧
    2019-01-01
  • 使用Python向DataFrame中指定位置添加一列或多列的方法

    使用Python向DataFrame中指定位置添加一列或多列的方法

    今天小编就为大家分享一篇使用Python向DataFrame中指定位置添加一列或多列的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • python3去掉string中的标点符号方法

    python3去掉string中的标点符号方法

    今天小编就为大家分享一篇python3去掉string中的标点符号方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-01-01
  • Python Pandas中数据类型查看与转换方法

    Python Pandas中数据类型查看与转换方法

    Pandas提供了丰富的数据类型系统以及灵活的类型转换方法,下面小编就来详细介绍一下如何查看Pandas数据结构中的数据类型和进行有效的类型转换吧
    2025-04-04

最新评论