ArticleReplace/images_edit.py

341 lines
12 KiB
Python
Raw Normal View History

2025-10-25 16:45:02 +08:00
import logging
import os
import random
import requests
from PIL import Image
from PIL import ImageDraw, ImageFont, ImageEnhance
from config import *
from utils import safe_open_directory, safe_filename
IMGS_BASE_PATH = CONFIG['General']['images_path']
def crop_and_replace_images(folder_path):
"""
修改图片尺寸
:param folder_path:
:return:
"""
print("开始处理图片。。。。")
# 遍历文件夹中的所有文件
for filename in os.listdir(folder_path):
# 检查文件扩展名是否为图片格式
if filename.lower().endswith(('.jpg')):
# 拼接完整的文件路径
file_path = os.path.join(folder_path, filename)
print("文件夹路径:" + folder_path)
print("文件路径:" + file_path)
# 打开图片
with Image.open(file_path) as img:
# 获取图片的尺寸
width, height = img.size
# 裁剪图片裁剪下方10px
print("裁剪图片。。。")
cropped_img = img.crop((0, 0, width, height - (height * 0.1)))
# 保存裁剪后的图片,覆盖原文件
# 通过拉伸使改变裁剪后图片的尺寸与原图片尺寸相同
resized_img = cropped_img.resize((width, height))
# output_path = file_path[0:file_path.find('.')] + '.png'
resized_img.save(file_path, 'jpg')
def deduplicate_images(folder_path):
print("开始对图片去重。。。")
"""扫描 folder_path 下的图片,对每张图片做修改并直接覆盖原文件"""
if not os.path.exists(folder_path):
print("错误:输入文件夹不存在!")
return
supported_ext = ('.png', '.jpg', '.jpeg', '.bmp', '.gif', '.webp')
for root, _, files in os.walk(folder_path):
for file in files:
if file.lower().endswith(supported_ext):
file_path = os.path.join(root, file)
try:
with Image.open(file_path) as img:
modified_img = modify_image(img)
modified_img.save(file_path) # 直接覆盖原图片
print(f"已处理并覆盖:{file_path}")
except Exception as e:
print(f"处理 {file_path} 时出错:{e}")
def download_image(image_url, save_path):
"""
下载图片并保存
:param image_url: 图片链接
:param save_path: 保存路径
:return:
"""
try:
response = requests.get(image_url)
if response.status_code == 200:
with open(save_path, 'wb') as f:
f.write(response.content)
print(f"图片下载成功,保存路径为:{save_path}")
else:
print(f"图片下载失败,状态码为:{response.status_code}")
except requests.exceptions.RequestException as e:
print(f"请求出错:{e}")
def download_and_process_images(img_urls, article_title, save_dir=None):
"""
下载并处理图片
:param img_urls: 图片URL列表
:param article_title: 文章标题
:param save_dir: 自定义保存目录如果为None则使用默认目录
"""
if save_dir is None:
save_dir = IMGS_BASE_PATH
# 使用safe_filename处理文章标题
safe_title = safe_filename(article_title)
# 使用os.path.normpath来规范化路径避免路径分隔符的问题
img_dir_path = os.path.normpath(os.path.join(str(save_dir), safe_title))
logger.info(f"图片保存路径:{img_dir_path}")
safe_open_directory(img_dir_path)
for i, img_url in enumerate(img_urls):
if img_url.startswith("https"):
imgurl = img_url
else:
imgurl = "https:" + img_url
# 使用os.path.normpath来规范化图片路径
img_path = os.path.normpath(os.path.join(img_dir_path, f"图片{i}.jpg"))
try:
download_image(imgurl, img_path)
# 只处理当前下载的图片,而不是整个文件夹
with Image.open(img_path) as img:
modified_img = modify_image(img)
modified_img.save(img_path) # 直接覆盖原图片
print(f"已处理并覆盖:{img_path}")
except Exception as e:
logging.error(f"处理图片失败: {e}")
# def download_and_process_images(img_urls, article_title, save_dir=None):
# """
# 下载并处理图片
# :param img_urls: 图片URL列表
# :param article_title: 文章标题
# :param save_dir: 自定义保存目录如果为None则使用默认目录
# """
# if save_dir is None:
# save_dir = IMGS_BASE_PATH
#
# img_dir_path = os.path.join(str(save_dir), str(article_title))
# logger.info(f"图片保存路径:{img_dir_path}")
# safe_open_directory(img_dir_path)
#
# for i, img_url in enumerate(img_urls):
# if img_url.startswith("https"):
# imgurl = img_url
# else:
# imgurl = "https:"+img_url
# img_path = os.path.join(img_dir_path, f"图片{i}.jpg")
# try:
# download_image(imgurl, img_path)
# # crop_and_replace_images(img_dir_path)
# deduplicate_images(img_dir_path)
# except Exception as e:
# logging.error(f"处理图片失败: {e}")
# def modify_image(img):
# print("修改图片")
# """对图片应用去重处理,不翻转,仅裁剪、旋转、亮度调整、添加水印、加透明蒙版"""
# width, height = img.size
#
# # 从配置中获取参数
# crop_percent = float(CONFIG['ImageModify']['crop_percent'])
# min_rotation = float(CONFIG['ImageModify']['min_rotation'])
# max_rotation = float(CONFIG['ImageModify']['max_rotation'])
# min_brightness = float(CONFIG['ImageModify']['min_brightness'])
# max_brightness = float(CONFIG['ImageModify']['max_brightness'])
# watermark_text = CONFIG['ImageModify']['watermark_text']
# watermark_opacity = int(CONFIG['ImageModify']['watermark_opacity'])
# overlay_opacity = int(CONFIG['ImageModify']['overlay_opacity'])
#
# # 1. 裁剪边缘
# crop_px_w = int(width * crop_percent)
# crop_px_h = int(height * crop_percent)
# img = img.crop((crop_px_w, crop_px_h, width - crop_px_w, height - crop_px_h))
#
# # 2. 随机旋转
# angle = random.uniform(min_rotation, max_rotation) * random.choice([-1, 1])
# img = img.rotate(angle, expand=True)
#
# # 3. 调整亮度
# enhancer = ImageEnhance.Brightness(img)
# factor = random.uniform(min_brightness, max_brightness) # 亮度调整因子
# img = enhancer.enhance(factor)
#
# # 4. 添加文字水印
# draw = ImageDraw.Draw(img)
# font_size = max(20, int(min(img.size) * 0.05))
# try:
# font = ImageFont.truetype("arial.ttf", font_size)
# except:
# font = ImageFont.load_default()
#
# # 获取文本尺寸
# text_width, text_height = draw.textbbox((0, 0), watermark_text, font=font)[2:]
#
# # 水印放在图片右下角
# x = img.size[0] - text_width - 5
# y = img.size[1] - text_height - 5
# draw.text((x, y), watermark_text, font=font, fill=(255, 255, 255, watermark_opacity))
#
# # 5. 添加半透明蒙版
# overlay = Image.new('RGBA', img.size, (255, 255, 255, overlay_opacity))
# if img.mode != 'RGBA':
# img = img.convert('RGBA')
# img = Image.alpha_composite(img, overlay)
#
# return img.convert('RGB')
def modify_image(img):
"""
对图片应用去重处理不翻转仅裁剪旋转亮度调整添加水印加透明蒙版
参数:
img: PIL.Image对象要处理的图片
返回:
PIL.Image对象处理后的图片
"""
print("修改图片")
# 确保图片是RGB模式
if img.mode != 'RGB':
img = img.convert('RGB')
# 从配置中获取参数
config = CONFIG['ImageModify']
crop_percent = float(config['crop_percent'])
min_rotation = float(config['min_rotation'])
max_rotation = float(config['max_rotation'])
min_brightness = float(config['min_brightness'])
max_brightness = float(config['max_brightness'])
watermark_text = config['watermark_text']
watermark_opacity = int(config['watermark_opacity'])
overlay_opacity = int(config['overlay_opacity'])
# 1. 新增功能裁剪图片下方20px
img = crop_bottom(img, 20)
# 2. 裁剪边缘
img = crop_edges(img, crop_percent)
# 3. 随机旋转
img = random_rotate(img, min_rotation, max_rotation)
# 4. 调整亮度
img = adjust_brightness(img, min_brightness, max_brightness)
# 5. 添加文字水印
img = add_watermark(img, watermark_text, watermark_opacity)
# 6. 添加半透明蒙版
img = add_overlay(img, overlay_opacity)
# 返回RGB模式的图片
return img.convert('RGB')
def crop_bottom(img, pixels):
"""
裁剪图片底部指定像素
参数:
img: PIL.Image对象要裁剪的图片
pixels: int要裁剪的像素数
返回:
PIL.Image对象裁剪后的图片
"""
width, height = img.size
if height > pixels: # 确保图片高度大于要裁剪的像素
return img.crop((0, 0, width, height - pixels))
return img
def crop_edges(img, percent):
"""
按比例裁剪图片边缘
参数:
img: PIL.Image对象要裁剪的图片
percent: float裁剪比例0-1之间
返回:
PIL.Image对象裁剪后的图片
"""
width, height = img.size
crop_px_w = int(width * percent)
crop_px_h = int(height * percent)
return img.crop((crop_px_w, crop_px_h, width - crop_px_w, height - crop_px_h))
def random_rotate(img, min_rotation, max_rotation):
"""
随机旋转图片
参数:
img: PIL.Image对象要旋转的图片
min_rotation: float最小旋转角度
max_rotation: float最大旋转角度
返回:
PIL.Image对象旋转后的图片
"""
angle = random.uniform(min_rotation, max_rotation) * random.choice([-1, 1])
return img.rotate(angle, expand=True)
def adjust_brightness(img, min_brightness, max_brightness):
"""
调整图片亮度
参数:
img: PIL.Image对象要调整亮度的图片
min_brightness: float最小亮度因子
max_brightness: float最大亮度因子
返回:
PIL.Image对象调整亮度后的图片
"""
enhancer = ImageEnhance.Brightness(img)
factor = random.uniform(min_brightness, max_brightness)
return enhancer.enhance(factor)
def add_watermark(img, text, opacity):
"""
添加文字水印到图片右下角
参数:
img: PIL.Image对象要添加水印的图片
text: str水印文本
opacity: int水印透明度0-255
返回:
PIL.Image对象添加水印后的图片
"""
# 确保图片是RGBA模式以支持透明度
if img.mode != 'RGBA':
img = img.convert('RGBA')
draw = ImageDraw.Draw(img)
font_size = max(20, int(min(img.size) * 0.05))
try:
font = ImageFont.truetype("arial.ttf", font_size)
except:
font = ImageFont.load_default()
# 获取文本尺寸
text_width, text_height = draw.textbbox((0, 0), text, font=font)[2:]
# 确保水印不超出图片边界
x = max(5, img.size[0] - text_width - 5)
y = max(5, img.size[1] - text_height - 5)
# 添加水印
draw.text((x, y), text, font=font, fill=(255, 255, 255, opacity))
return img
def add_overlay(img, opacity):
"""
添加半透明蒙版
参数:
img: PIL.Image对象要添加蒙版的图片
opacity: int蒙版透明度0-255
返回:
PIL.Image对象添加蒙版后的图片
"""
# 确保图片是RGBA模式以支持透明度
if img.mode != 'RGBA':
img = img.convert('RGBA')
overlay = Image.new('RGBA', img.size, (255, 255, 255, opacity))
return Image.alpha_composite(img, overlay)