基于Python+OpenCV實(shí)現(xiàn)圖片批量縮放與加水印功能
在日常工作中,經(jīng)常會(huì)遇到這樣的圖片處理需求:
- 商品圖片上傳前需要統(tǒng)一尺寸。
- 活動(dòng)照片需要批量壓縮后再發(fā)送。
- 資料截圖需要加上公司名或項(xiàng)目名水印。
- 文件夾里有幾十張、幾百?gòu)垐D片,不適合一張張手工處理。
這些任務(wù)非常適合用 Python 自動(dòng)化完成。本文使用 opencv-python 庫(kù),帶你從圖片讀取、保存、縮放、添加文字水印開(kāi)始,逐步完成一個(gè)“批量縮放并加水印”的完整小項(xiàng)目。
文章適合剛接觸圖像處理的 Python 初學(xué)者閱讀,不要求你提前掌握復(fù)雜的圖像算法。
1. OpenCV 是什么
OpenCV 是一個(gè)常用的計(jì)算機(jī)視覺(jué)和圖像處理庫(kù),可以完成圖片讀取、圖片保存、尺寸變換、顏色處理、繪圖、視頻處理、人臉檢測(cè)、目標(biāo)檢測(cè)等任務(wù)。
在 Python 中,我們通常安裝的是 opencv-python:
pip install opencv-python
安裝完成后,可以在 Python 中導(dǎo)入:
import cv2 print(cv2.__version__)
如果能正常打印版本號(hào),說(shuō)明安裝成功。
2. 圖片讀取與保存
OpenCV 讀取圖片使用 cv2.imread(),保存圖片使用 cv2.imwrite()。
import cv2
image = cv2.imread("input.jpg")
if image is None:
raise FileNotFoundError("圖片讀取失敗,請(qǐng)檢查文件路徑")
cv2.imwrite("output.jpg", image)
需要注意的是,OpenCV 讀取到的圖片是一個(gè) NumPy 數(shù)組。它的形狀通常是:
(高度, 寬度, 通道數(shù))
例如:
print(image.shape)
輸出可能是:
(1080, 1920, 3)
這表示圖片高度是 1080,寬度是 1920,通道數(shù)是 3。
3. 圖片縮放
圖片縮放使用 cv2.resize()。
3.1 按指定寬高縮放
resized = cv2.resize(image, (800, 600))
cv2.imwrite("resized.jpg", resized)
注意:cv2.resize() 的尺寸參數(shù)順序是 (寬度, 高度),不是 (高度, 寬度)。
3.2 按比例縮放
如果希望圖片按 50% 縮?。?/p>
resized = cv2.resize(image, None, fx=0.5, fy=0.5)
其中:
fx表示寬度縮放比例。fy表示高度縮放比例。
3.3 按目標(biāo)寬度等比例縮放
實(shí)際項(xiàng)目中,更常見(jiàn)的是限制圖片寬度,同時(shí)保持原始寬高比:
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))
這樣不會(huì)把圖片拉伸變形。
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)
參數(shù)說(shuō)明:
text:水印文字。org:文字左下角坐標(biāo),格式是(x, y)。fontFace:字體類(lèi)型。fontScale:字體大小。color:文字顏色,OpenCV 使用 BGR 順序,不是 RGB。thickness:文字粗細(xì)。lineType:抗鋸齒方式,cv2.LINE_AA會(huì)讓文字邊緣更平滑。
5. 半透明文字水印
直接把文字畫(huà)到圖片上會(huì)比較生硬。更常見(jiàn)的做法是使用一張透明圖層,再和原圖混合。
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. 批量處理整個(gè)文件夾圖片
批量處理的基本思路是:
- 遍歷輸入文件夾。
- 找到所有圖片文件。
- 讀取每張圖片。
- 縮放圖片。
- 添加水印。
- 保存到輸出文件夾。
常見(jiàn)圖片后綴包括:
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. 中文路徑兼容處理
很多初學(xué)者會(huì)遇到一個(gè)問(wèn)題:圖片路徑里有中文時(shí),cv2.imread() 可能讀取失敗,cv2.imwrite() 也可能保存失敗。
為了更穩(wěn)定地兼容中文路徑,可以使用 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
這兩個(gè)函數(shù)在 Windows 中文目錄、中文文件名場(chǎng)景下非常實(shí)用。
8. 完整項(xiàng)目代碼
下面是完整可運(yùn)行代碼。建議保存為 batch_resize_watermark.py。
項(xiàng)目目錄示例:
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)
# 先畫(huà)一層深色陰影,提高淺色背景下的可讀性
cv2.putText(
overlay,
text,
(x + 2, y + 2),
font_face,
font_scale,
(0, 0, 0),
thickness + 1,
cv2.LINE_AA
)
# 再畫(huà)白色文字
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"沒(méi)有找到可處理的圖片: {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"批量處理結(jié)束,成功 {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()
運(yùn)行腳本:
python batch_resize_watermark.py
如果運(yùn)行成功,終端會(huì)輸出類(lèi)似信息:
處理完成: 示例圖片1.jpg 處理完成: 示例圖片2.png 處理完成: 示例圖片3.webp 批量處理結(jié)束,成功 3/3 張 輸出目錄: D:\image_project\output_images
9. 效果演示說(shuō)明
運(yùn)行前,input_images 文件夾中存放原始圖片:
input_images/ ├─ 風(fēng)景照片.jpg ├─ 商品主圖.png └─ 活動(dòng)現(xiàn)場(chǎng).webp
運(yùn)行腳本后,output_images 文件夾會(huì)生成同名圖片:
output_images/ ├─ 風(fēng)景照片.jpg ├─ 商品主圖.png └─ 活動(dòng)現(xiàn)場(chǎng).webp
處理后的圖片會(huì)有兩個(gè)變化:
- 如果原圖寬度超過(guò)
max_width,會(huì)被等比例縮放到指定最大寬度,例如 1200 像素。 - 圖片右下角會(huì)添加半透明文字水印,例如
Python OpenCV。
由于代碼使用了中文路徑兼容讀寫(xiě)函數(shù),即使文件名是 風(fēng)景照片.jpg、目錄名是 測(cè)試圖片,也可以正常讀取和保存。
10. 常見(jiàn)問(wèn)題
10.1 為什么 OpenCV 的顏色是 BGR
OpenCV 默認(rèn)使用 BGR 通道順序,而很多其他庫(kù)使用 RGB。比如白色在 OpenCV 中是:
(255, 255, 255)
紅色不是 (255, 0, 0),而是:
(0, 0, 255)
10.2 PNG 透明背景會(huì)保留嗎
本文代碼使用 cv2.IMREAD_COLOR 讀取圖片,會(huì)把圖片讀成三通道 BGR,透明通道不會(huì)保留。如果你需要保留透明背景,可以使用 cv2.IMREAD_UNCHANGED 讀取,并額外處理 alpha 通道。
10.3 水印位置如何調(diào)整
代碼中的水印位置由下面幾行控制:
x = max(width - text_width - margin, margin) y = max(height - margin, text_height + margin)
這是右下角位置。如果想放到左上角,可以改成:
x = margin y = text_height + margin
10.4 如何遞歸處理子文件夾
當(dāng)前代碼只處理輸入目錄第一層圖片。如果要遞歸處理子文件夾,可以把:
input_dir.iterdir()
改成:
input_dir.rglob("*")
同時(shí)輸出路徑可以根據(jù)相對(duì)路徑生成,避免不同子目錄下的同名圖片互相覆蓋。
總結(jié)
本文完成了一個(gè)適合圖像處理初學(xué)者的 OpenCV 實(shí)戰(zhàn)項(xiàng)目:批量縮放文件夾圖片,并添加半透明文字水印。這個(gè)項(xiàng)目雖然不復(fù)雜,但覆蓋了圖片自動(dòng)化處理中的幾個(gè)核心能力:
- 使用
opencv-python讀取和保存圖片。 - 使用
cv2.resize()等比例縮放圖片。 - 使用
cv2.putText()添加文字水印。 - 使用
cv2.addWeighted()實(shí)現(xiàn)半透明效果。 - 使用
pathlib批量遍歷文件夾。 - 使用
imdecode和imencode兼容中文路徑。
掌握這些基礎(chǔ)之后,你可以繼續(xù)擴(kuò)展更多功能,例如圖片壓縮、格式轉(zhuǎn)換、添加 Logo 水印、遞歸處理子目錄、生成處理日志等,把它變成真正適合日常工作的圖片批處理工具。
以上就是基于Python+OpenCV實(shí)現(xiàn)圖片批量縮放與加水印功能的詳細(xì)內(nèi)容,更多關(guān)于Python OpenCV圖片批量縮放與加水印的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python matplotlib畫(huà)圖庫(kù)學(xué)習(xí)繪制常用的圖
這篇文章主要為大家詳細(xì)介紹了python matplotlib畫(huà)圖庫(kù)學(xué)習(xí)繪制常用的圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03
Python?return函數(shù)返回值類(lèi)型和幫助函數(shù)使用教程
這篇文章主要為大家介紹了Python?return函數(shù)返回值類(lèi)型和幫助函數(shù)使用教程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
python使用tkinter實(shí)現(xiàn)屏幕中間倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了python使用tkinter實(shí)現(xiàn)屏幕中間倒計(jì)時(shí),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
5道關(guān)于python基礎(chǔ) while循環(huán)練習(xí)題
這篇文章主要給大家分享的是5道關(guān)于python基礎(chǔ) while循環(huán)練習(xí)題,無(wú)論學(xué)習(xí)什么語(yǔ)言,練習(xí)都是必不可少的,下面文章的練習(xí)題挺精湛的,需要的朋友可以參考一下2021-11-11
Python讀寫(xiě)文件模式和文件對(duì)象方法實(shí)例詳解
這篇文章主要介紹了Python讀寫(xiě)文件模式和文件對(duì)象方法,結(jié)合實(shí)例形式詳細(xì)分析了Python文件操作常用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2019-09-09
深入了解python中的常見(jiàn)錯(cuò)誤類(lèi)型與解決
在Python編程過(guò)程中,經(jīng)常會(huì)遇到各種錯(cuò)誤,了解這些錯(cuò)誤的類(lèi)型以及如何處理它們是成為一位優(yōu)秀的Python開(kāi)發(fā)者所必備的技能之一,下面就跟隨小編一起學(xué)習(xí)一下python中的常見(jiàn)錯(cuò)誤類(lèi)型吧2023-11-11
PyCharm:method may be static問(wèn)題及解決
這篇文章主要介紹了PyCharm:method may be static問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Python pyinstaller庫(kù)的安裝配置教程分享
pyinstaller模塊主要用于python代碼打包成exe程序直接使用,這樣在其它電腦上即使沒(méi)有python環(huán)境也是可以運(yùn)行的。本文就來(lái)和大家分享一下pyinstaller庫(kù)的安裝配置教程,希望對(duì)大家有所幫助2023-04-04

