前言
本系统通过 Laravel 作为前端框架接收用户上传的图片,调用 Python 脚本处理水印添加,最终返回处理后的图片。这种架构充分利用了 Laravel 的便捷性和 Python 图像处理库的强大功能。
一、Python 水印处理脚本
from PIL import Image, ImageEnhance
import fitz
import io
import sys
import os
import logging
import traceback
# 配置日志记录
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filename='watermark_process.log'
)
def compress_image(image, quality=85, max_size=(1920, 1920)):
"""压缩图片尺寸和质量"""
try:
if image.size[0] > max_size[0] or image.size[1] > max_size[1]:
image.thumbnail(max_size, Image.Resampling.LANCZOS)
logging.info(f"图片已压缩至尺寸: {image.size}")
return image
except Exception as e:
logging.error(f"图片压缩失败: {str(e)}")
raise
def add_watermark_on_top(image_path, pdf_watermark_path, output_path):
"""将水印清晰地覆盖整个图片"""
try:
logging.info(f"开始处理图片: {image_path}")
# 检查文件是否存在
if not os.path.exists(image_path):
error_msg = f"找不到图片文件: {image_path}"
logging.error(error_msg)
raise FileNotFoundError(error_msg)
if not os.path.exists(pdf_watermark_path):
error_msg = f"找不到水印文件: {pdf_watermark_path}"
logging.error(error_msg)
raise FileNotFoundError(error_msg)
# 打开并压缩原始图片
base_image = Image.open(image_path)
logging.info(f"原始图片格式: {base_image.format}, 尺寸: {base_image.size}")
base_image = compress_image(base_image)
# 打开PDF水印
pdf_doc = fitz.open(pdf_watermark_path)
page = pdf_doc[0]
logging.info(f"PDF水印已加载,页数: {len(pdf_doc)}")
# 使用更高分辨率转换PDF水印
mat = fitz.Matrix(8, 8)
watermark_pix = page.get_pixmap(matrix=mat, alpha=True)
watermark_bytes = watermark_pix.tobytes("png")
watermark_image = Image.open(io.BytesIO(watermark_bytes))
logging.info(f"水印图片已转换,尺寸: {watermark_image.size}")
# 确保图片模式正确
if base_image.mode != 'RGBA':
base_image = base_image.convert('RGBA')
logging.info("原始图片已转换为RGBA模式")
if watermark_image.mode != 'RGBA':
watermark_image = watermark_image.convert('RGBA')
logging.info("水印图片已转换为RGBA模式")
# 将水印调整为与原图完全相同的尺寸
watermark_image = watermark_image.resize(base_image.size, Image.Resampling.LANCZOS)
logging.info(f"水印已调整为图片尺寸: {base_image.size}")
# 增强水印的不透明度
watermark_data = list(watermark_image.getdata())
enhanced_data = []
for item in watermark_data:
if item[3] > 0: # 如果像素不是完全透明的
enhanced_data.append((item[0], item[1], item[2], min(255, int(item[3] * 2))))
else:
enhanced_data.append(item)
watermark_image.putdata(enhanced_data)
logging.info("水印不透明度已增强")
# 创建新的图层并合成
result = Image.new('RGBA', base_image.size, (0,0,0,0))
result.paste(base_image, (0,0))
result.paste(watermark_image, (0,0), watermark_image)
logging.info("图片与水印已合成")
# 确保输出目录存在
output_dir = os.path.dirname(output_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir, exist_ok=True)
logging.info(f"创建输出目录: {output_dir}")
# 保存结果
result.save(output_path, 'PNG', quality=85, optimize=True)
logging.info(f"处理后的图片已保存至: {output_path}")
# 关闭PDF文档
pdf_doc.close()
return output_path
except Exception as e:
error_msg = f"处理过程中出现错误:{str(e)}"
logging.error(error_msg)
logging.error(traceback.format_exc()) # 记录完整的堆栈信息
return None
def main():
try:
logging.info("===== 开始新的水印处理任务 =====")
if len(sys.argv) != 4:
error_msg = "参数数量错误,使用方法: python add_watermark.py 输入图片路径 水印PDF路径 输出图片路径"
logging.error(error_msg)
print(error_msg)
return 1
input_image = sys.argv[1]
watermark_pdf = sys.argv[2]
output_image = sys.argv[3]
logging.info(f"接收到命令行参数: 输入={input_image}, 水印={watermark_pdf}, 输出={output_image}")
result = add_watermark_on_top(input_image, watermark_pdf, output_image)
if result:
success_msg = f"处理成功,输出文件:{result}"
logging.info(success_msg)
print(success_msg)
return 0
else:
error_msg = "处理失败,详情请查看日志文件"
logging.error(error_msg)
print(error_msg)
return 1
except Exception as e:
logging.critical(f"程序崩溃: {str(e)}")
logging.critical(traceback.format_exc())
print(f"程序异常终止: {str(e)}")
return 1
if __name__ == "__main__":
sys.exit(main())
二、Laravel 后端集成
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class WatermarkController extends Controller
{
/**
* 添加水印到图片
*/
public function addWatermark(Request $request)
{
// 验证请求
$request->validate([
'image' => 'required|image|mimes:jpeg,png,jpg,gif|max:2048',
]);
try {
// 存储上传的图片
$imagePath = $request->file('image')->store('temp', 'public');
$imageFullPath = storage_path('app/public/' . $imagePath);
Log::info("图片已上传: {$imageFullPath}");
// 水印 PDF 路径
$watermarkPdfPath = public_path('watermark.pdf');
if (!file_exists($watermarkPdfPath)) {
throw new \Exception("水印文件不存在: {$watermarkPdfPath}");
}
// 准备输出路径
$outputFileName = 'watermarked_' . time() . '.' . $request->file('image')->extension();
$outputPath = 'temp/' . $outputFileName;
$outputFullPath = storage_path('app/public/' . $outputPath);
// 执行 Python 脚本
$pythonPath = env('PYTHON_PATH', 'python3'); // 配置 Python 路径
$scriptPath = base_path('scripts/add_watermark.py');
$process = new Process([
$pythonPath,
$scriptPath,
$imageFullPath,
$watermarkPdfPath,
$outputFullPath
]);
$process->run();
// 检查执行结果
if (!$process->isSuccessful()) {
Log::error("Python 脚本执行失败: " . $process->getErrorOutput());
throw new ProcessFailedException($process);
}
$output = $process->getOutput();
Log::info("Python 脚本输出: {$output}");
// 检查输出文件是否存在
if (!file_exists($outputFullPath)) {
throw new \Exception("处理后的图片不存在,可能 Python 脚本执行失败");
}
// 返回处理后的图片 URL
$imageUrl = Storage::url($outputPath);
return response()->json([
'success' => true,
'message' => '水印添加成功',
'image_url' => $imageUrl
]);
} catch (\Exception $e) {
Log::error("添加水印失败: " . $e->getMessage());
return response()->json([
'success' => false,
'message' => '处理过程中出错: ' . $e->getMessage()
], 500);
}
}
}
三、环境配置
在 .env 文件中添加 Python 路径配置:
PYTHON_PATH=python3 # 根据实际环境修改
四、错误调试指南
1. Python 脚本调试
日志文件: 查看 watermark_process.log 获取详细处理过程
命令行测试: 直接通过命令行执行 Python 脚本测试
python3 scripts/add_watermark.py /path/to/input.jpg /path/to/watermark.pdf /path/to/output.png
2. Laravel 调试
日志检查: 查看 storage/logs/laravel.log
异常信息: 捕获并记录完整的异常堆栈
权限问题: 确保 storage 目录可写
Python 路径: 确认 .env 中的 PYTHON_PATH 配置正确
3. 常见错误及解决方案
错误信息 | 可能原因 | 解决方案 |
---|---|---|
找不到图片文件 | 文件路径错误或权限不足 | 检查文件路径和权限 |
Python 脚本执行失败 | Python 环境问题或依赖缺失 | 确认 Python 路径和依赖(如 PyMuPDF、Pillow) |
处理后的图片不存在 | Python 脚本内部错误 | 查看 Python 日志文件获取详细信息 |
五、部署注意事项
1、Python 依赖安装:
pip install pillow pymupdf
2、文件权限设置:
chmod -R 755 storage/
chmod -R 755 bootstrap/cache/
3、Nginx 配置:确保上传文件大小限制足够大:
client_max_body_size 20M;
通过以上方案,你可以实现一个完整的 Laravel + Python 图片水印系统,并具备完善的错误调试能力。这种架构既发挥了 Laravel 的 Web 开发优势,又利用了 Python 在图像处理领域的强大生态。