Python自动化办公:扫描件旋转批处理脚本
Python自动化办公:扫描件旋转批处理脚本
你是不是也遇到过这样的烦恼?扫描了一堆文件,结果发现有些图片是歪的,有些是倒着的,一张张手动旋转调整,眼睛都看花了,手也点麻了。特别是处理PDF里的扫描件,导出图片后方向五花八门,整理起来简直是一场噩梦。
今天,我就来分享一个自己用了很久的“办公神器”——一个用Python写的扫描件旋转批处理脚本。它能自动从PDF里提取图片,智能判断图片该不该旋转、该往哪边转,然后批量处理好,最后还能一键集成到Windows右键菜单里,用起来就像系统自带功能一样方便。
这个脚本特别适合经常需要处理扫描文档、票据、合同的朋友。咱们不搞那些复杂的深度学习模型,就用最实在的图像处理技术,效果够用,速度够快,代码也清晰易懂。下面,我就手把手带你从零开始,把这个工具做出来。
1. 准备工作:安装必要的库
工欲善其事,必先利其器。我们先来把需要的Python库装好。打开你的命令行(CMD或终端),依次输入下面的命令:
pip install PyMuPDF # 用来处理PDF,提取图片
pip install opencv-python # 核心图像处理库
pip install Pillow # 图像处理辅助库
pip install numpy # 科学计算基础库
如果安装速度慢,可以加上国内的镜像源,比如:
pip install PyMuPDF -i https://pypi.tuna.tsinghua.edu.cn/simple
安装完成后,我们可以先简单验证一下。创建一个新的Python文件,比如叫 test_env.py,写入以下内容:
import fitz # PyMuPDF
import cv2
from PIL import Image
import numpy as np
print("所有库导入成功!")
print(f"PyMuPDF版本: {fitz.__version__}")
print(f"OpenCV版本: {cv2.__version__}")
print(f"Pillow版本: {Image.__version__}")
运行一下,如果没报错,就说明环境准备好了。
2. 核心功能一:从PDF中提取图片
处理扫描件,第一步就是把PDF里的图片“抠”出来。我们用 PyMuPDF(也叫 fitz)来做这件事,它处理PDF非常高效。
import fitz
import os
def extract_images_from_pdf(pdf_path, output_folder):
"""
从PDF文件中提取所有图片
参数:
pdf_path: PDF文件路径
output_folder: 图片输出文件夹
"""
# 创建输出文件夹
os.makedirs(output_folder, exist_ok=True)
# 打开PDF文档
doc = fitz.open(pdf_path)
image_count = 0
print(f"正在处理PDF: {os.path.basename(pdf_path)}")
print(f"总页数: {len(doc)}")
# 遍历每一页
for page_num in range(len(doc)):
page = doc[page_num]
# 获取当前页的所有图片
image_list = page.get_images(full=True)
# 遍历当前页的每张图片
for img_index, img in enumerate(image_list):
xref = img[0] # 图片的交叉引用编号
# 提取图片数据
base_image = doc.extract_image(xref)
image_bytes = base_image["image"]
image_ext = base_image["ext"]
# 生成图片文件名
image_filename = f"page_{page_num+1:03d}_img_{img_index+1:03d}.{image_ext}"
image_path = os.path.join(output_folder, image_filename)
# 保存图片
with open(image_path, "wb") as f:
f.write(image_bytes)
image_count += 1
print(f" 已提取: {image_filename}")
doc.close()
print(f"\n提取完成!共提取 {image_count} 张图片到: {output_folder}")
return output_folder
# 使用示例
if __name__ == "__main__":
# 替换成你的PDF文件路径
pdf_file = "example.pdf"
output_dir = "extracted_images"
if os.path.exists(pdf_file):
extract_images_from_pdf(pdf_file, output_dir)
else:
print(f"文件不存在: {pdf_file}")
这段代码会遍历PDF的每一页,找到所有嵌入的图片,然后按照“页码_序号”的格式保存下来。这样,我们就有了待处理的图片素材。
3. 核心功能二:智能判断图片旋转角度
这是整个脚本最核心的部分——怎么让程序“看懂”图片是正的还是歪的?我们采用一种基于文字方向检测的实用方法:通过分析图片中水平线条的分布来判断方向。
简单来说,正常的文档(比如合同、发票)里面,文字大多是水平排列的。如果我们把图片旋转到不同角度,然后分析每个角度下水平方向的“线条强度”,强度最大的那个角度,很可能就是图片正确的方向。
import cv2
import numpy as np
from PIL import Image
import os
def detect_rotation_angle(image_path):
"""
检测图片需要的旋转角度(0, 90, 180, 270度)
返回:
angle: 需要旋转的角度(0, 90, 180, 270)
confidence: 置信度(0-1之间)
"""
# 读取图片
img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
if img is None:
print(f"无法读取图片: {image_path}")
return 0, 0.0
# 1. 预处理:缩小图片加快处理速度
height, width = img.shape
scale = 800 / max(height, width)
if scale < 1:
new_width = int(width * scale)
new_height = int(height * scale)
img = cv2.resize(img, (new_width, new_height))
# 2. 边缘检测:找到图片中的线条
# 先用高斯模糊降噪
img_blur = cv2.GaussianBlur(img, (5, 5), 0)
# Canny边缘检测
edges = cv2.Canny(img_blur, 50, 150)
# 3. 霍夫变换检测直线
lines = cv2.HoughLinesP(
edges,
rho=1,
theta=np.pi/180,
threshold=50,
minLineLength=30,
maxLineGap=10
)
if lines is None:
# 如果没有检测到明显线条,默认不需要旋转
return 0, 0.0
# 4. 分析直线的角度分布
angles = []
for line in lines:
x1, y1, x2, y2 = line[0]
angle = np.degrees(np.arctan2(y2 - y1, x2 - x1))
# 只关注接近水平或垂直的线条
if abs(angle) < 30 or abs(angle) > 60:
angles.append(angle)
if not angles:
return 0, 0.0
# 5. 将角度转换为0, 90, 180, 270度
# 计算平均角度
avg_angle = np.mean(angles)
# 判断最接近哪个标准角度
possible_angles = [0, 90, 180, 270, -90]
closest_angle = min(possible_angles, key=lambda x: abs(x - avg_angle))
# 计算置信度(基于角度分布的集中程度)
angle_std = np.std(angles)
confidence = max(0, 1 - angle_std / 45) # 标准差越小,置信度越高
# 角度修正:如果检测到-90度,实际上就是270度
if closest_angle == -90:
closest_angle = 270
return closest_angle, confidence
def rotate_image_by_angle(image_path, angle, output_path=None):
"""
按照指定角度旋转图片
参数:
image_path: 输入图片路径
angle: 旋转角度(0, 90, 180, 270)
output_path: 输出图片路径(如果为None则覆盖原文件)
"""
if angle == 0:
# 不需要旋转
if output_path and output_path != image_path:
img = Image.open(image_path)
img.save(output_path)
return
# 打开图片
img = Image.open(image_path)
# 旋转图片
rotated_img = img.rotate(angle, expand=True)
# 保存图片
if output_path is None:
output_path = image_path
rotated_img.save(output_path)
return output_path
# 测试旋转检测功能
def test_rotation_detection():
"""测试图片旋转角度检测"""
test_images = ["test1.jpg", "test2.jpg", "test3.jpg"]
for img_file in test_images:
if os.path.exists(img_file):
angle, confidence = detect_rotation_angle(img_file)
print(f"{img_file}: 检测到旋转角度 {angle}度, 置信度 {confidence:.2f}")
if angle != 0 and confidence > 0.3:
# 创建旋转后的副本
base_name = os.path.splitext(img_file)[0]
output_file = f"{base_name}_rotated{angle}.jpg"
rotate_image_by_angle(img_file, angle, output_file)
print(f" 已生成旋转后图片: {output_file}")
else:
print(f"测试图片不存在: {img_file}")
if __name__ == "__main__":
test_rotation_detection()
这个检测方法虽然不是100%准确,但对于大多数扫描文档来说,效果已经相当不错了。置信度可以帮助我们判断检测结果的可信程度,一般大于0.3就可以认为是比较可靠的。
4. 核心功能三:批量处理与自动化
有了单张图片的处理能力,现在我们来实现批量处理功能。这个函数会遍历一个文件夹里的所有图片,自动检测并旋转,然后保存到指定位置。
import glob
from tqdm import tqdm # 进度条库,让等待更有趣
def batch_process_images(input_folder, output_folder=None, min_confidence=0.3):
"""
批量处理文件夹中的所有图片
参数:
input_folder: 输入图片文件夹
output_folder: 输出文件夹(如果为None则覆盖原文件)
min_confidence: 最小置信度,低于此值不进行旋转
"""
# 获取所有图片文件
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.tiff']
image_files = []
for ext in image_extensions:
image_files.extend(glob.glob(os.path.join(input_folder, ext)))
if not image_files:
print(f"在文件夹 {input_folder} 中没有找到图片文件")
return
print(f"找到 {len(image_files)} 张图片需要处理")
# 创建输出文件夹
if output_folder:
os.makedirs(output_folder, exist_ok=True)
# 统计信息
stats = {
'total': len(image_files),
'rotated': 0,
'skipped': 0,
'failed': 0
}
# 使用进度条显示处理进度
for image_file in tqdm(image_files, desc="处理图片"):
try:
# 检测旋转角度
angle, confidence = detect_rotation_angle(image_file)
# 确定输出路径
if output_folder:
filename = os.path.basename(image_file)
output_file = os.path.join(output_folder, filename)
else:
output_file = image_file
if angle != 0 and confidence >= min_confidence:
# 需要旋转且置信度足够
rotate_image_by_angle(image_file, angle, output_file)
stats['rotated'] += 1
# 记录旋转信息
base_name = os.path.splitext(os.path.basename(image_file))[0]
log_msg = f"{base_name}: 旋转 {angle}度 (置信度: {confidence:.2f})"
tqdm.write(log_msg)
else:
# 不需要旋转或置信度不足
if output_folder and output_file != image_file:
# 复制到输出文件夹
import shutil
shutil.copy2(image_file, output_file)
stats['skipped'] += 1
except Exception as e:
print(f"处理图片 {image_file} 时出错: {str(e)}")
stats['failed'] += 1
# 打印统计信息
print("\n" + "="*50)
print("处理完成!统计信息:")
print(f" 总共处理: {stats['total']} 张图片")
print(f" 成功旋转: {stats['rotated']} 张")
print(f" 跳过处理: {stats['skipped']} 张")
print(f" 处理失败: {stats['failed']} 张")
print("="*50)
# 使用示例
if __name__ == "__main__":
# 批量处理示例
input_dir = "extracted_images" # 之前从PDF提取的图片
output_dir = "rotated_images" # 旋转后的图片
if os.path.exists(input_dir):
batch_process_images(input_dir, output_dir, min_confidence=0.3)
else:
print(f"输入文件夹不存在: {input_dir}")
这个批量处理函数还加了个进度条,处理大量图片时能看到进度,不会觉得程序“卡死”了。
5. 完整工作流:从PDF到整理好的图片
现在我们把前面的功能串起来,形成一个完整的工作流:输入一个PDF,自动提取图片、智能旋转、批量保存。
def process_pdf_to_rotated_images(pdf_path, output_base_folder="processed_output"):
"""
完整的PDF处理流程:提取图片 -> 旋转校正 -> 保存结果
"""
# 1. 创建输出文件夹结构
pdf_name = os.path.splitext(os.path.basename(pdf_path))[0]
output_folder = os.path.join(output_base_folder, pdf_name)
extracted_folder = os.path.join(output_folder, "extracted")
rotated_folder = os.path.join(output_folder, "rotated")
print("="*60)
print(f"开始处理PDF: {os.path.basename(pdf_path)}")
print("="*60)
# 2. 从PDF提取图片
print("\n步骤1: 从PDF提取图片...")
extract_images_from_pdf(pdf_path, extracted_folder)
# 3. 批量旋转图片
print("\n步骤2: 智能旋转校正图片...")
batch_process_images(extracted_folder, rotated_folder, min_confidence=0.3)
# 4. 生成处理报告
print("\n步骤3: 生成处理报告...")
rotated_count = len(glob.glob(os.path.join(rotated_folder, "*.*")))
report_file = os.path.join(output_folder, "处理报告.txt")
with open(report_file, "w", encoding="utf-8") as f:
f.write(f"PDF文件: {os.path.basename(pdf_path)}\n")
f.write(f"处理时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"提取图片数: {len(glob.glob(os.path.join(extracted_folder, '*.*')))}\n")
f.write(f"旋转后图片数: {rotated_count}\n")
f.write(f"输出文件夹: {rotated_folder}\n")
print("\n" + "="*60)
print("处理完成!")
print(f"所有文件保存在: {output_folder}")
print(f"旋转后的图片在: {rotated_folder}")
print(f"处理报告: {report_file}")
print("="*60)
return rotated_folder
# 使用示例
if __name__ == "__main__":
import datetime
# 处理单个PDF
pdf_file = "需要处理的文档.pdf"
if os.path.exists(pdf_file):
process_pdf_to_rotated_images(pdf_file)
else:
print(f"PDF文件不存在: {pdf_file}")
print("\n你可以:")
print("1. 将PDF文件放在当前目录")
print("2. 修改代码中的pdf_file变量为你的PDF路径")
print("3. 或者使用下面的方式处理整个文件夹的PDF")
# 批量处理文件夹中所有PDF的示例
pdf_folder = "pdf_files"
if os.path.exists(pdf_folder):
pdf_files = glob.glob(os.path.join(pdf_folder, "*.pdf"))
for pdf in pdf_files:
process_pdf_to_rotated_images(pdf)
这个完整流程基本上能满足大部分扫描件处理需求了。但如果我们想更方便一点,比如直接在文件上右键就能处理,那就要看下一个进阶功能了。
6. 进阶功能:集成到Windows右键菜单
这是让这个脚本真正变成“办公神器”的一步。通过修改Windows注册表,我们可以把脚本添加到右键菜单中,这样在任何PDF文件上右键,就能直接调用我们的处理脚本。
重要提示:修改注册表有风险,请先备份重要数据!下面的代码会创建一个 .reg 文件,需要手动导入。
def create_windows_context_menu_reg():
"""
创建Windows右键菜单注册表文件
注意:需要以管理员权限运行此脚本生成的reg文件
"""
# 获取当前脚本的绝对路径
script_path = os.path.abspath(__file__)
# 创建批处理文件
bat_content = f'''@echo off
echo 正在处理PDF文件: %1
python "{script_path}" --process-pdf "%1"
pause
'''
bat_file = "process_pdf.bat"
with open(bat_file, "w", encoding="gbk") as f: # Windows命令行用GBK编码
f.write(bat_content)
print(f"已创建批处理文件: {bat_file}")
# 创建注册表文件
reg_content = f'''Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\\SystemFileAssociations\\.pdf\\shell\\旋转处理扫描件]
@="智能旋转扫描件"
[HKEY_CLASSES_ROOT\\SystemFileAssociations\\.pdf\\shell\\旋转处理扫描件\\command]
@="\\"{os.path.abspath(bat_file)}\\" \\"%1\\""
'''
reg_file = "add_context_menu.reg"
with open(reg_file, "w", encoding="utf-16") as f: # REG文件需要UTF-16编码
f.write(reg_content)
print(f"已创建注册表文件: {reg_file}")
print("\n使用方法:")
print("1. 双击运行 add_context_menu.reg 文件")
print("2. 选择'是'导入注册表")
print("3. 现在在任意PDF文件上右键,就能看到'智能旋转扫描件'选项了")
print("\n注意:")
print("- 需要以管理员权限运行")
print("- 首次使用可能需要重启文件资源管理器")
return reg_file
# 修改主函数,支持命令行参数
if __name__ == "__main__":
import sys
import argparse
parser = argparse.ArgumentParser(description='扫描件旋转批处理工具')
parser.add_argument('--process-pdf', type=str, help='要处理的PDF文件路径')
parser.add_argument('--create-menu', action='store_true', help='创建Windows右键菜单')
parser.add_argument('--input-folder', type=str, help='要处理的图片文件夹')
parser.add_argument('--output-folder', type=str, help='输出文件夹')
args = parser.parse_args()
if args.create_menu:
# 创建右键菜单
create_windows_context_menu_reg()
elif args.process_pdf:
# 处理指定的PDF文件
if os.path.exists(args.process_pdf):
process_pdf_to_rotated_images(args.process_pdf)
else:
print(f"文件不存在: {args.process_pdf}")
elif args.input_folder:
# 处理图片文件夹
output_folder = args.output_folder or "rotated_output"
batch_process_images(args.input_folder, output_folder)
else:
# 交互式模式
print("扫描件旋转批处理工具")
print("="*40)
print("请选择操作:")
print("1. 处理单个PDF文件")
print("2. 处理图片文件夹")
print("3. 添加到Windows右键菜单")
print("4. 测试旋转检测功能")
choice = input("\n请输入选项 (1-4): ").strip()
if choice == "1":
pdf_file = input("请输入PDF文件路径: ").strip()
if os.path.exists(pdf_file):
process_pdf_to_rotated_images(pdf_file)
else:
print(f"文件不存在: {pdf_file}")
elif choice == "2":
input_dir = input("请输入图片文件夹路径: ").strip()
output_dir = input("请输入输出文件夹路径 (直接回车使用默认): ").strip()
if not output_dir:
output_dir = "rotated_images"
if os.path.exists(input_dir):
batch_process_images(input_dir, output_dir)
else:
print(f"文件夹不存在: {input_dir}")
elif choice == "3":
create_windows_context_menu_reg()
elif choice == "4":
test_rotation_detection()
else:
print("无效选项")
添加了命令行参数支持后,我们的脚本就更灵活了。可以直接在命令行调用,也可以集成到其他工作流中。
7. 使用技巧与注意事项
在实际使用中,有几个小技巧可以让这个工具更好用:
- 处理前先预览:对于特别重要的文档,可以先处理几张看看效果,调整置信度阈值。
- 批量处理建议:一次不要处理太多文件,可以先分批次,避免内存不足。
- 备份原文件:脚本默认不会覆盖原PDF,但处理图片时如果选择覆盖,建议先备份。
- 特殊文档处理:有些文档本身就有倾斜的文字(比如艺术字体),这种可能需要手动处理。
如果你遇到检测不准的情况,可以尝试调整 detect_rotation_angle 函数中的参数:
- 调整
cv2.Canny的边缘检测阈值 - 修改霍夫变换的
threshold和minLineLength - 改变置信度的计算方式
8. 总结
写到这里,一个完整的扫描件旋转批处理工具就完成了。从PDF提取图片,到智能判断旋转角度,再到批量处理和Windows集成,基本上覆盖了日常办公中扫描件处理的全流程。
这个脚本最好的地方在于,它完全用Python实现,代码透明,你可以根据自己的需求随意修改。比如,如果你想支持更多图片格式,就在 batch_process_images 函数里添加对应的扩展名;如果想提高检测精度,可以尝试更复杂的图像处理算法。
实际用下来,对于大多数扫描的文档、发票、合同,这个脚本的准确率还是挺高的,能节省大量的手动调整时间。特别是集成到右键菜单后,用起来就像系统自带功能一样顺手。
如果你在使用的过程中有任何问题,或者想到了什么改进的点,欢迎随时交流。自动化办公的乐趣,就在于把重复枯燥的工作交给程序,让自己有更多时间处理更有价值的事情。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。
更多推荐
所有评论(0)