Python批量添加水印的優(yōu)雅實(shí)現(xiàn)與進(jìn)階
1. 簡(jiǎn)介
在日常圖像處理中,為圖片添加水印是一項(xiàng)常見(jiàn)任務(wù)。有多種方法和工具可供選擇,而今天我們將專注于使用Python語(yǔ)言結(jié)合PIL庫(kù)批量添加水印。
需要注意的是,所選用的圖片格式不應(yīng)為JPG或JPEG,因?yàn)檫@兩種格式的圖片不支持透明度設(shè)置。
2. PIL庫(kù)概述
先前的文章已經(jīng)詳細(xì)介紹過(guò)PIL庫(kù),這里不再贅述。
- PIL是Python的圖像處理庫(kù),支持多種文件格式。
- PIL提供強(qiáng)大的圖像和圖形處理功能,包括縮放、裁剪、疊加以及添加線條、文字等操作。
- 安裝PIL庫(kù)可使用以下命令:
pip install Pillow
3. PIL庫(kù)中涉及的類
模塊或類 | 說(shuō)明 |
---|---|
image模塊 | 用于圖像處理 |
ImageDraw | 2D圖像對(duì)象 |
ImageFont | 字體存儲(chǔ) |
ImageEnhance | 圖像增強(qiáng) |
4. 實(shí)現(xiàn)原理
本文的主要目標(biāo)是批量為某個(gè)文件夾下的圖片添加水印,實(shí)現(xiàn)原理如下:
- 設(shè)置水印內(nèi)容;
- 使用Image對(duì)象的open()方法打開(kāi)原始圖片;
- 使用Image對(duì)象的new()方法創(chuàng)建存儲(chǔ)水印圖片的對(duì)象;
- 使用ImageDraw.Draw對(duì)象的text()方法繪制水印文字;
- 使用ImageEnhance中Brightness的enhance()方法設(shè)置水印透明度。
5. 實(shí)現(xiàn)過(guò)程
5.1 原始圖片
設(shè)定原始圖片的存儲(chǔ)目錄,例如:
F:\python_study\image\image01
5.2 導(dǎo)入相關(guān)模塊
導(dǎo)入所需的PIL模塊或類:
from PIL imort Image, ImageDraw, ImageFont, ImageEnhance import os
5.3 初始化數(shù)據(jù)
通過(guò)用戶手動(dòng)輸入相關(guān)信息,如圖片存儲(chǔ)路徑、水印文字、水印位置、水印透明度等:
class WatermarkText(): def __init__(self): super(WatermarkText, self).__init__() self.image_path = input('圖片路徑:') self.watermark_text = input('水印文字:') self.position_flag = int(input('水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):')) self.opacity = float(input('水印透明度(0—1之間的1位小數(shù)):'))
5.4 水印字體設(shè)置
選擇系統(tǒng)字體庫(kù)中的字體:
self.font = ImageFont.truetype("cambriab.ttf", size=35)
5.5 打開(kāi)原始圖片并創(chuàng)建存儲(chǔ)對(duì)象
打開(kāi)原始圖片并轉(zhuǎn)換為RGBA:
image = Image.open(img).convert('RGBA')
創(chuàng)建繪制對(duì)象:
new_img = Image.new('RGBA', image.size, (255, 255, 255, 0)) image_draw = ImageDraw.Draw(new_img)
5.6 計(jì)算圖片和水印的大小
計(jì)算圖片大?。?/p>
w, h = image.size
計(jì)算文字大小:
w1 = self.font.getsize(self.watermark_text)[0] h1 = self.font.getsize(self.watermark_text)[1]
5.7 選擇性設(shè)置水印文字
通過(guò)if語(yǔ)句實(shí)現(xiàn):
if self.position_flag == 1: # 左上角 location = (0, 0) elif self.position_flag == 2: # 左下角 location = (0, h - h1) elif self.position_flag == 3: # 右上角 location = (w - w1, 0) elif self.position_flag == 4: # 右下角 location = (w - w1, h - h1) elif self.position_flag == 5: # 居中 location = (h/2, h/2)
5.8 繪制文字并設(shè)置透明度
繪制文字:
image_draw.text(location, self.watermark_text, font=self.font, fill="blue")
設(shè)置透明度:
transparent = new_img.split()[3] transparent = ImageEnhance.Brightness(transparent).enhance(self.opacity) new_img.putalpha(transparent) Image.alpha_composite(image, new_img).save(img)
5.9 遍歷獲取圖片文件并調(diào)用繪制方法
watermark_text = WatermarkText() try: file_list = os.listdir(watermark_text.image_path) for i in range(0, len(file_list)): filepath = os.path.join(watermark_text.image_path, file_list[i]) if os.path.isfile(filepath): filetype = os.path.splitext(filepath)[1] if filetype == '.png': watermark_text.add_text_watermark(filepath) else: print("圖片格式有誤,請(qǐng)使用png格式圖片") print('批量添加水印完成') except: print('輸入的文件路徑有誤,請(qǐng)檢查~~')
6. 完整源碼
from PIL import Image, ImageDraw, ImageFont, ImageEnhance import os class WatermarkText(): def __init__(self): super(WatermarkText, self).__init__() self.image_path = input('圖片路徑:') self.watermark_text = input('水印文字:') self.position_flag = int(input('水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):')) self.opacity = float(input('水印透明度(0—1之間的1位小數(shù)):')) # 設(shè)置字體 self.font = ImageFont.truetype("cambriab.ttf", size=35) # 文字水印 def add_text_watermark(self, img): global location image = Image.open(img).convert('RGBA') new_img = Image.new('RGBA', image.size, (255, 255, 255, 0)) image_draw = ImageDraw.Draw(new_img) w, h = image.size # 圖片大小 w1 = self.font.getsize(self.watermark_text)[0] # 字體寬度 h1 = self.font.getsize(self.watermark_text)[1] # 字體高度 # 設(shè)置水印文字位置 if self.position_flag == 1: # 左上角 location = (0, 0) elif self.position_flag == 2: # 左下角 location = (0, h - h1) elif self.position_flag == 3: # 右上角 location = (w - w1, 0) elif self.position_flag == 4: # 右下角 location = (w - w1, h - h1) elif self.position_flag == 5: # 居中 location = (h/2, h/2) # 繪制文字 image_draw.text(location, self.watermark_text, font=self.font, fill="blue") # 設(shè)置透明度 transparent = new_img.split()[3] transparent = ImageEnhance.Brightness(transparent).enhance(self.opacity) new_img.putalpha(transparent) Image.alpha_composite(image, new_img).save(img) if __name__ == "__main__": watermark_text = WatermarkText() try: file_list = os.listdir(watermark_text.image_path) for i in range(0, len(file_list)): filepath = os.path.join(watermark_text.image_path, file_list[i]) if os.path.isfile(filepath): filetype = os.path.splitext(filepath)[1] if filetype == '.png': watermark_text.add_text_watermark(filepath) else: print("圖片格式有誤,請(qǐng)使用png格式圖片") print('批量添加水印完成') except: print('輸入的文件路徑有誤,請(qǐng)檢查~~')
7. 效果展示
運(yùn)行過(guò)程:
D:\Python37\python.exe F:/python_study/python_project/watermark_text.py
圖片路徑:F:\python_study\image\image01
水印文字:
水印位置(1:左上角,2:左下角,3:右上角,4:右下角,5:居中):1
水印透明度(0—1之間的1位小數(shù)):0.5
F:/python_study/python_project/watermark_text.py:32: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
w1 = self.font.getsize(self.watermark_text)[0] # 獲取字體寬度
F:/python_study/python_project/watermark_text.py:33: DeprecationWarning: getsize is deprecated and will be removed in Pillow 10 (2023-07-01). Use getbbox or getlength instead.
h1 = self.font.getsize(self.watermark_text)[1] # 獲取字體高度
批量添加水印完成
8. 改進(jìn)與建議
8.1 參數(shù)輸入方式優(yōu)化
在初始化數(shù)據(jù)的部分,我們可以考慮通過(guò)命令行參數(shù)或配置文件的方式輸入相關(guān)信息,以提高用戶體驗(yàn)。例如使用argparse
庫(kù)來(lái)解析命令行參數(shù)。
import argparse class WatermarkText(): def __init__(self): parser = argparse.ArgumentParser(description='Add watermark to images.') parser.add_argument('--image_path', type=str, help='Path to the image directory.') parser.add_argument('--watermark_text', type=str, help='Text for watermark.') parser.add_argument('--position_flag', type=int, help='Position flag for watermark (1: top-left, 2: bottom-left, 3: top-right, 4: bottom-right, 5: center).') parser.add_argument('--opacity', type=float, help='Opacity for watermark (0-1 with 1 decimal place).') args = parser.parse_args() self.image_path = args.image_path or input('Image path: ') self.watermark_text = args.watermark_text or input('Watermark text: ') self.position_flag = args.position_flag or int(input('Watermark position (1: top-left, 2: bottom-left, 3: top-right, 4: bottom-right, 5: center): ')) self.opacity = args.opacity or float(input('Watermark opacity (0-1 with 1 decimal place): '))
8.2 異常處理改進(jìn)
在處理異常的部分,我們可以更具體地捕獲異常類型,并提供更友好的提示信息。
try: # existing code... except FileNotFoundError: print('Error: The specified image directory does not exist.') except PermissionError: print('Error: Permission denied to access the specified image directory.') except Exception as e: print(f'An unexpected error occurred: {e}')
8.3 代碼結(jié)構(gòu)優(yōu)化
可以考慮將一些功能模塊化,提高代碼的可讀性和維護(hù)性。例如,將文字水印的添加功能獨(dú)立成一個(gè)方法。
class WatermarkText(): # existing code... def add_text_watermark(self, img): # existing code...
8.4 日志記錄
考慮在程序中添加日志記錄,記錄關(guān)鍵步驟和出錯(cuò)信息,以便于排查問(wèn)題。
import logging logging.basicConfig(level=logging.INFO) class WatermarkText(): # existing code... def add_text_watermark(self, img): try: # existing code... logging.info(f'Successfully added watermark to {img}') except Exception as e: logging.error(f'Error adding watermark to {img}: {e}')
8.5 擴(kuò)展功能
在程序中可以考慮添加更多功能,比如支持不同的水印顏色、字體大小等選項(xiàng),以使程序更加靈活。
這些改進(jìn)和建議將有助于提高程序的穩(wěn)定性、易用性和可維護(hù)性。
當(dāng)然,我們將繼續(xù)改進(jìn)和完善你的代碼。在這一部分,我們會(huì)考慮一些進(jìn)一步的優(yōu)化和改進(jìn)。
9. 優(yōu)化圖片格式檢查
在處理圖片文件時(shí),可以優(yōu)化檢查圖片格式的方式。使用os.path.splitext
得到的文件擴(kuò)展名可能包含大寫字母,為了確保匹配,可以將文件擴(kuò)展名轉(zhuǎn)換為小寫。
if filetype.lower() == '.png': watermark_text.add_text_watermark(filepath) else: print("Error: Image format is not supported. Please use PNG format.")
10. 增加用戶交互性
可以考慮在程序中增加更多用戶交互性,比如在成功添加水印后詢問(wèn)用戶是否繼續(xù)添加水印。
while True: try: # existing code... print('Watermark added successfully.') another = input('Do you want to add watermark to another image? (yes/no): ').lower() if another != 'yes': break except Exception as e: logging.error(f'Error: {e}')
這樣,用戶可以選擇是否繼續(xù)添加水印,提高程序的交互性。
11. 多線程處理
如果你需要處理大量圖片,可以考慮使用多線程來(lái)加速處理過(guò)程。這可以通過(guò)concurrent.futures
模塊實(shí)現(xiàn)。
from concurrent.futures import ThreadPoolExecutor # existing code... if __name__ == "__main__": watermark_text = WatermarkText() try: file_list = os.listdir(watermark_text.image_path) with ThreadPoolExecutor() as executor: executor.map(watermark_text.add_text_watermark, [os.path.join(watermark_text.image_path, file) for file in file_list]) print('Batch watermarking completed.') except Exception as e: logging.error(f'Error: {e}')
這將允許同時(shí)處理多個(gè)圖片,提高處理速度。
12. 其他優(yōu)化建議
考慮支持更多圖片格式,而不僅限于PNG。你可以使用Pillow庫(kù)中的Image.register_open()
方法注冊(cè)其他格式的圖片打開(kāi)器。
如果水印文字較長(zhǎng),可以考慮自動(dòng)調(diào)整文字大小,以適應(yīng)圖片。
以上就是Python批量添加水印的優(yōu)雅實(shí)現(xiàn)與進(jìn)階的詳細(xì)內(nèi)容,更多關(guān)于Python添加水印的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python通過(guò)ElementTree操作XML獲取結(jié)點(diǎn)讀取屬性美化XML
本文講解如何通過(guò)ElementTree解析XML,獲取兒子結(jié)點(diǎn)、插入兒子結(jié)點(diǎn)、操作屬性、美化XML2013-12-12python 表達(dá)式和語(yǔ)句及for、while循環(huán)練習(xí)實(shí)例
下面小編就為大家?guī)?lái)一篇python 表達(dá)式和語(yǔ)句及for、while循環(huán)練習(xí)實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07Pycharm連接遠(yuǎn)程服務(wù)器并實(shí)現(xiàn)遠(yuǎn)程調(diào)試的實(shí)現(xiàn)
這篇文章主要介紹了Pycharm連接遠(yuǎn)程服務(wù)器并實(shí)現(xiàn)遠(yuǎn)程調(diào)試的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08python繪制規(guī)則網(wǎng)絡(luò)圖形實(shí)例
今天小編大家分享一篇python繪制規(guī)則網(wǎng)絡(luò)圖形實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12詳解如何使用Python實(shí)現(xiàn)復(fù)制粘貼的功能
pandas?里面有一個(gè)?pd.read_clipboard?函數(shù),可以根據(jù)你復(fù)制的內(nèi)容生成DataFrame。本文就利用這個(gè)函數(shù)實(shí)現(xiàn)復(fù)制粘貼的功能,感興趣的可以了解一下2023-01-01python+mediapipe+opencv實(shí)現(xiàn)手部關(guān)鍵點(diǎn)檢測(cè)功能(手勢(shì)識(shí)別)
這篇文章主要介紹了python+mediapipe+opencv實(shí)現(xiàn)手部關(guān)鍵點(diǎn)檢測(cè)功能(手勢(shì)識(shí)別),本文僅僅簡(jiǎn)單介紹了mediapipe的使用,而mediapipe提供了大量關(guān)于圖像識(shí)別等的方法,需要的朋友可以參考下2022-01-01