341 lines
12 KiB
Python
341 lines
12 KiB
Python
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) |