发布于2026-05-21 阅读(0)
扫一扫,手机访问
处理图片是很多工作中的高频需求,比如商品图要统一尺寸、活动照片需要批量压缩、资料截图得加上公司水印。如果文件夹里有几十上百张图片,手动操作简直让人头皮发麻。
这类重复性劳动,正是Python自动化脚本大显身手的地方。今天,我们就用opencv-python这个库,从零开始,一步步搭建一个能“批量缩放并添加水印”的实用工具。整个过程不涉及复杂的图像算法,非常适合Python初学者上手实践。

简单来说,OpenCV是一个功能强大的计算机视觉和图像处理库。从基础的图片读写、尺寸变换、颜色调整,到高级的人脸识别、目标检测,它都能胜任。在Python生态里,我们通常安装的是opencv-python包:
pip install opencv-python
安装完成后,在Python中导入并打印版本号,能成功输出就说明环境没问题:
import cv2 print(cv2.__version__)
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数组。它的形状(shape)遵循(高度, 宽度, 通道数)的格式。比如一张1080p的彩色图片,打印出来的形状可能就是(1080, 1920, 3)。
改变图片尺寸,主要靠cv2.resize()函数。
直接指定目标宽度和高度即可,但要注意参数顺序是(宽度, 高度),别弄反了。
resized = cv2.resize(image, (800, 600))
如果想等比例缩小一半,可以设置缩放因子:
resized = cv2.resize(image, None, fx=0.5, fy=0.5)
其中,fx控制宽度缩放比例,fy控制高度缩放比例。
实际项目中,更常见的需求是限制图片的最大宽度,同时保持原始宽高比不变,避免图片被拉伸变形。实现思路是先计算缩放比例,再确定对应的高度。
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))
使用cv2.putText()函数可以在图片上绘制文字。它的参数比较多,但都很好理解:
watermarked = image.copy()
cv2.putText(
watermarked,
text="Python OpenCV", # 水印文字
org=(30, 60), # 文字左下角坐标(x, y)
fontFace=cv2.FONT_HERSHEY_SIMPLEX, # 字体
fontScale=1.2, # 字体大小
color=(255, 255, 255), # 文字颜色(BGR顺序)
thickness=2, # 线条粗细
lineType=cv2.LINE_AA # 抗锯齿,让边缘更平滑
)
需要特别提醒的是,OpenCV默认使用BGR颜色通道,而不是常见的RGB。所以纯红色是(0, 0, 255),而不是(255, 0, 0)。
直接把不透明的文字画上去会显得很生硬。更优雅的做法是,先在一张 overlay(覆盖层)上画好文字,再通过图像混合,以一定的透明度叠加到原图上。
overlay = image.copy() output = image.copy() # 在overlay上绘制文字 cv2.putText(overlay, "Python OpenCV", (30, 60), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (255, 255, 255), 2, cv2.LINE_AA) # 混合图像,alpha值控制水印透明度 alpha = 0.35 watermarked = cv2.addWeighted(overlay, alpha, output, 1 - alpha, 0)
这里的alpha参数是关键,值越大,水印越明显;值越小,水印越淡。
要实现批量处理,逻辑很清晰:遍历输入文件夹,筛选出图片文件,然后对每一张执行“读取->缩放->加水印->保存”的流程。
首先,定义常见的图片后缀:
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) # 找到了一张图片
很多朋友在Windows环境下会遇到一个坑:当文件路径或名称包含中文时,cv2.imread()和cv2.imwrite()可能会失败。一个稳妥的解决方案是,用numpy.fromfile()和cv2.imdecode()来读取,用cv2.imencode()和tofile()来保存。
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
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
将上面的功能模块整合起来,就是一个完整的、健壮的批处理脚本。建议保存为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
运行脚本前,input_images文件夹里放着你的原始图片。运行后,output_images文件夹会生成处理后的新图片。每张图片都会经历两个变化:
max_width(比如1200像素),它会被等比例缩放到这个最大宽度。得益于中文路径兼容函数,即使你的文件夹或文件名全是中文,整个过程也能畅通无阻。
这是OpenCV的历史遗留设计。它默认使用BGR通道顺序,而许多其他库(如PIL、matplotlib)使用RGB。处理颜色时记住这个区别即可。
本文代码使用cv2.IMREAD_COLOR模式读取,会忽略透明通道(Alpha通道)。如果需要处理带透明背景的PNG,应使用cv2.IMREAD_UNCHANGED读取,并在后续步骤中妥善处理Alpha通道。
水印位置由add_text_watermark函数中的坐标计算逻辑控制。默认是放在右下角。如果想放到左上角,可以修改坐标计算部分:
x = margin y = text_height + margin
当前代码只处理输入目录的第一层。要递归处理所有子目录,可以将遍历部分从input_dir.iterdir()改为input_dir.rglob("*")。同时,生成输出路径时需要考虑原文件的相对路径,避免不同子目录下的同名文件相互覆盖。
通过这个项目,我们串联起了OpenCV图像处理的几个核心环节:图片的读取与保存、等比例缩放、添加文字水印以及实现半透明效果。同时,也解决了实际开发中常见的中文路径问题和批量文件遍历需求。
掌握了这些基础,你就可以以此为核心,扩展出更多实用功能,比如批量格式转换、添加Logo图片水印、递归处理复杂目录结构、生成详细处理报告等,逐步打造出一个真正贴合自身工作流的自动化图片处理工具。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8