

背景需求
之前做了斑点狗,练习画“点”(小面),效果还可以,我想再试试“斑马”:线
【教学类-99-01】20251118”斑点狗(对称)主题:动物花花衣
https://mp.csdn.net/mp_blog/creation/editor/154956031

一个简单的斑马,多个非常浅,非常浅的灰色条纹,粗线条轮廓,线稿,极简主义,线条清晰,白色背景,无颜色,无阴影,无细节。侧面图
下载后右图

下载后左图

把之前所有代码合并一起

'''
斑点狗图片处理完整流程
Deepseek & 豆包 & 阿夏
20251118
'''
import os
import shutil
import glob
import numpy as np
from PIL import Image, ImageDraw
from pathlib import Path
from docx import Document
from docx.shared import Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx2pdf import convert
from PyPDF2 import PdfMerger
from scipy.spatial import KDTree
# ==================== 全局路径配置 ====================
PATH = r'D:\20251118斑马' # 根目录
MB = '斑马' # 项目名称
# 各步骤文件夹名称配置
FOLDER_NAMES = {
'00右图': '00右图',
'01左图': '01左图',
'02合并向右': '02合并向右',
'03纯白背景图': '03纯白背景图',
'04原图切边': '04原图切边',
'05保留灰斑_透明背景_原比例': '05原图切边_保留灰斑_透明背景_原比例',
'06保留灰斑_透明背景_拉伸': '06原图切边_保留灰斑_透明背景_拉伸',
'07黑白二色_透明背景_原比例': '07原图切边_黑白二色_透明背景_原比例',
'08黑白二色_透明背景_拉伸': '08原图切边_黑白二色_透明背景_拉伸',
'09学号_头右_无灰点': '09学号,头右、无灰点',
'10学号_头右_有灰点': '10学号,头右、有灰点'
}
# ==================== 步骤1: 图片合并与翻转 ====================
def process_images():
"""步骤1: 脑袋向左的狗水平翻转向右,合并左右图"""
output_dir = os.path.join(PATH, FOLDER_NAMES['02合并向右'])
os.makedirs(output_dir, exist_ok=True)
# 处理左图文件夹 - 水平翻转
source_dir_123 = os.path.join(PATH, FOLDER_NAMES['01左图'])
if os.path.exists(source_dir_123):
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.gif', '*.tiff']
for extension in image_extensions:
pattern = os.path.join(source_dir_123, extension)
for image_path in glob.glob(pattern):
try:
with Image.open(image_path) as img:
flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)
filename = os.path.basename(image_path)
output_path = os.path.join(output_dir, filename)
counter = 1
name, ext = os.path.splitext(filename)
while os.path.exists(output_path):
new_filename = f"{name}_flipped_{counter}{ext}"
output_path = os.path.join(output_dir, new_filename)
counter += 1
flipped_img.save(output_path)
print(f"已处理并保存: {filename} -> {os.path.basename(output_path)}")
except Exception as e:
print(f"处理图片 {image_path} 时出错: {e}")
# 处理右图文件夹 - 直接复制
source_dir_234 = os.path.join(PATH, FOLDER_NAMES['00右图'])
if os.path.exists(source_dir_234):
image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.gif', '*.tiff']
for extension in image_extensions:
pattern = os.path.join(source_dir_234, extension)
for image_path in glob.glob(pattern):
try:
filename = os.path.basename(image_path)
output_path = os.path.join(output_dir, filename)
counter = 1
name, ext = os.path.splitext(filename)
while os.path.exists(output_path):
new_filename = f"{name}_copy_{counter}{ext}"
output_path = os.path.join(output_dir, new_filename)
counter += 1
shutil.copy2(image_path, output_path)
print(f"已复制: {filename} -> {os.path.basename(output_path)}")
except Exception as e:
print(f"复制图片 {image_path} 时出错: {e}")
print("步骤1完成:图片合并与翻转")
# ==================== 步骤2: 纯白背景处理 ====================
def keep_black_and_spots(image_path, output_path=None, bg_threshold=230):
"""保留黑线和灰色斑点,其他灰色和白色都转为白色"""
try:
img = Image.open(str(image_path)).convert("RGB")
img_np = np.array(img)
gray = np.dot(img_np[..., :3], [0.2989, 0.5870, 0.1140]).astype(np.uint8)
bg_mask = gray >= bg_threshold
img_np[bg_mask] = [255, 255, 255]
processed_img = Image.fromarray(img_np)
if output_path is None:
input_path = Path(image_path)
output_path = input_path.parent / f"{input_path.stem}_white_bg{input_path.suffix}"
processed_img.save(str(output_path))
print(f"处理完成:{image_path} -> {output_path},尺寸:{processed_img.size}")
except Exception as e:
print(f"警告:无法处理图片 {image_path},错误:{str(e)}")
def batch_keep_black_and_spots(input_dir, output_dir=None, bg_threshold=230):
"""批量处理纯白背景"""
input_dir = Path(input_dir)
if not input_dir.exists():
print(f"错误:目录 {input_dir} 不存在")
return
image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
image_files = [f for f in input_dir.glob("*.*") if f.suffix.lower() in image_extensions]
if not image_files:
print(f"警告:在目录 {input_dir} 中未找到图片文件")
return
if output_dir is not None:
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
for img_file in image_files:
if output_dir is not None:
output_path = output_dir / img_file.name
else:
output_path = None
keep_black_and_spots(img_file, output_path, bg_threshold)
def step2_white_background():
"""步骤2: 纯白背景处理"""
input_dir = Path(PATH) / FOLDER_NAMES['02合并向右']
output_dir = Path(PATH) / FOLDER_NAMES['03纯白背景图']
batch_keep_black_and_spots(
input_dir=input_dir,
output_dir=output_dir,
bg_threshold=230
)
print("步骤2完成:纯白背景处理")
# ==================== 步骤3: 图片切边 ====================
def crop_white_margin(image_path, output_path=None, white_threshold=245):
"""裁剪图片白色边距"""
try:
img = Image.open(str(image_path)).convert("RGB")
img_np = np.array(img)
gray = np.dot(img_np[..., :3], [0.2989, 0.5870, 0.1140])
gray = gray.astype(np.uint8)
non_white_pixels = np.where(gray < white_threshold)
if len(non_white_pixels[0]) == 0:
print(f"警告:图片 {image_path} 全是白色,无需裁剪")
return
y_min = np.min(non_white_pixels[0])
y_max = np.max(non_white_pixels[0])
x_min = np.min(non_white_pixels[1])
x_max = np.max(non_white_pixels[1])
cropped_img_np = img_np[y_min:y_max+1, x_min:x_max+1]
cropped_img = Image.fromarray(cropped_img_np)
if output_path is None:
input_path = Path(image_path)
output_path = input_path.parent / f"{input_path.stem}_cropped{input_path.suffix}"
cropped_img.save(str(output_path))
print(f"裁剪完成:{image_path} -> {output_path}")
except Exception as e:
print(f"警告:无法处理图片 {image_path},错误:{str(e)}")
def batch_crop_white_margin(input_dir, output_dir=None, white_threshold=245):
"""批量切边"""
input_dir = Path(input_dir)
if not input_dir.exists():
print(f"错误:目录 {input_dir} 不存在")
return
image_extensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff']
image_files = [f for f in input_dir.glob("*.*") if f.suffix.lower() in image_extensions]
if not image_files:
print(f"警告:在目录 {input_dir} 中未找到图片文件")
return
if output_dir is not None:
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
for img_file in image_files:
if output_dir is not None:
output_path = output_dir / img_file.name
else:
output_path = None
crop_white_margin(img_file, output_path, white_threshold)
def step3_crop_images():
"""步骤3: 图片切边"""
input_dir = Path(PATH) / FOLDER_NAMES['03纯白背景图']
output_dir = Path(PATH) / FOLDER_NAMES['04原图切边']
batch_crop_white_margin(
input_dir=input_dir,
output_dir=output_dir,
white_threshold=230
)
print("步骤3完成:图片切边")
# ==================== 步骤4: 灰色斑点狗处理 ====================
def crop_transparent_edges(image, margin=0):
"""裁剪透明边缘"""
data = np.array(image)
alpha = data[:, :, 3]
non_transparent = np.where(alpha > 0)
if len(non_transparent[0]) == 0:
return image
top = np.min(non_transparent[0])
bottom = np.max(non_transparent[0])
left = np.min(non_transparent[1])
right = np.max(non_transparent[1])
top = max(0, top - margin)
bottom = min(image.height - 1, bottom + margin)
left = max(0, left - margin)
right = min(image.width - 1, right + margin)
return image.crop((left, top, right + 1, bottom + 1))
def make_background_transparent(image_path, output_path, tolerance=30, margin=0):
"""白色背景变透明"""
with Image.open(image_path) as img:
img = img.convert('RGBA')
data = np.array(img)
red, green, blue, alpha = data.T
white_mask = (
(red >= 255 - tolerance) & (red <= 255) &
(green >= 255 - tolerance) & (green <= 255) &
(blue >= 255 - tolerance) & (blue <= 255)
)
data[white_mask.T] = (255, 255, 255, 0)
result = Image.fromarray(data)
cropped_result = crop_transparent_edges(result, margin)
cropped_result.save(output_path, 'PNG')
def resize_to_uniform_size(image_path, output_path, target_size=(200, 200)):
"""保持比例调整尺寸"""
with Image.open(image_path) as img:
if img.mode != 'RGBA':
img = img.convert('RGBA')
new_img = Image.new('RGBA', target_size, (255, 255, 255, 0))
img_ratio = img.width / img.height
target_ratio = target_size[0] / target_size[1]
if img_ratio > target_ratio:
new_width = target_size[0]
new_height = int(target_size[0] / img_ratio)
else:
new_height = target_size[1]
new_width = int(target_size[1] * img_ratio)
resized_img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
x = (target_size[0] - new_width) // 2
y = (target_size[1] - new_height) // 2
new_img.paste(resized_img, (x, y), resized_img)
new_img.save(output_path, 'PNG')
def resize_to_exact_size(image_path, output_path, target_size=(200, 200)):
"""拉伸到精确尺寸"""
with Image.open(image_path) as img:
if img.mode != 'RGBA':
img = img.convert('RGBA')
resized_img = img.resize(target_size, Image.Resampling.LANCZOS)
resized_img.save(output_path, 'PNG')
def process_single_image_keep_ratio(image_path, output_path, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""处理单张图片-保持比例"""
try:
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(image_path, temp_transparent_path, transparency_tolerance, margin)
resize_to_uniform_size(temp_transparent_path, output_path, target_size)
if os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return True
except Exception as e:
print(f"处理图片 {image_path} 时出错: {str(e)}")
if 'temp_transparent_path' in locals() and os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return False
def process_single_image_stretch(image_path, output_path, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""处理单张图片-拉伸"""
try:
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(image_path, temp_transparent_path, transparency_tolerance, margin)
resize_to_exact_size(temp_transparent_path, output_path, target_size)
if os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return True
except Exception as e:
print(f"处理图片 {image_path} 时出错: {str(e)}")
if 'temp_transparent_path' in locals() and os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return False
def batch_process_gray_images():
"""步骤4: 批量处理灰色斑点狗"""
input_directory = os.path.join(PATH, FOLDER_NAMES['04原图切边'])
output_directory_keep_ratio = os.path.join(PATH, FOLDER_NAMES['05保留灰斑_透明背景_原比例'])
output_directory_stretch = os.path.join(PATH, FOLDER_NAMES['06保留灰斑_透明背景_拉伸'])
os.makedirs(output_directory_keep_ratio, exist_ok=True)
os.makedirs(output_directory_stretch, exist_ok=True)
supported_formats = ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff')
processed_count = 0
for filename in os.listdir(input_directory):
if filename.lower().endswith(supported_formats):
input_path = os.path.join(input_directory, filename)
output_filename = os.path.splitext(filename)[0] + '.png'
output_path_keep_ratio = os.path.join(output_directory_keep_ratio, output_filename)
output_path_stretch = os.path.join(output_directory_stretch, output_filename)
success_keep = process_single_image_keep_ratio(
input_path, output_path_keep_ratio, 30, 0, (250, 200)
)
success_stretch = process_single_image_stretch(
input_path, output_path_stretch, 30, 0, (250, 200)
)
if success_keep and success_stretch:
print(f"已处理: {filename} -> 保持比例 & 拉伸版本")
processed_count += 1
else:
print(f"处理失败: {filename}")
print(f"步骤4完成:灰色斑点狗处理,成功处理 {processed_count} 张图片")
# ==================== 步骤5: 白色斑点狗处理 ====================
def convert_to_pure_bw(image_path, output_path, threshold=128):
"""转为纯黑白图片"""
try:
img = Image.open(image_path)
img = img.convert('L')
binary_img = img.point(lambda x: 0 if x < threshold else 255, '1')
binary_img = binary_img.convert('RGB')
binary_img.save(output_path)
print(f"黑白图生成: {os.path.basename(image_path)}")
return output_path
except Exception as e:
print(f"处理图片 {image_path} 时出错: {str(e)}")
return None
def process_single_image_bw_keep_ratio(image_path, output_path, bw_threshold=128, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""处理单张黑白图片-保持比例"""
try:
temp_bw_path = output_path.replace('.png', '_bw_temp.png')
bw_path = convert_to_pure_bw(image_path, temp_bw_path, bw_threshold)
if not bw_path:
return False
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(bw_path, temp_transparent_path, transparency_tolerance, margin)
resize_to_uniform_size(temp_transparent_path, output_path, target_size)
if os.path.exists(temp_bw_path):
os.remove(temp_bw_path)
if os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return True
except Exception as e:
print(f"处理图片 {image_path} 时出错: {str(e)}")
for temp_path in [temp_bw_path, temp_transparent_path]:
if 'temp_path' in locals() and os.path.exists(temp_path):
os.remove(temp_path)
return False
def process_single_image_bw_stretch(image_path, output_path, bw_threshold=128, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""处理单张黑白图片-拉伸"""
try:
temp_bw_path = output_path.replace('.png', '_bw_temp.png')
bw_path = convert_to_pure_bw(image_path, temp_bw_path, bw_threshold)
if not bw_path:
return False
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(bw_path, temp_transparent_path, transparency_tolerance, margin)
resize_to_exact_size(temp_transparent_path, output_path, target_size)
if os.path.exists(temp_bw_path):
os.remove(temp_bw_path)
if os.path.exists(temp_transparent_path):
os.remove(temp_transparent_path)
return True
except Exception as e:
print(f"处理图片 {image_path} 时出错: {str(e)}")
for temp_path in [temp_bw_path, temp_transparent_path]:
if 'temp_path' in locals() and os.path.exists(temp_path):
os.remove(temp_path)
return False
def batch_process_white_images():
"""步骤5: 批量处理白色斑点狗"""
input_directory = os.path.join(PATH, FOLDER_NAMES['04原图切边'])
output_directory_keep_ratio = os.path.join(PATH, FOLDER_NAMES['07黑白二色_透明背景_原比例'])
output_directory_stretch = os.path.join(PATH, FOLDER_NAMES['08黑白二色_透明背景_拉伸'])
os.makedirs(output_directory_keep_ratio, exist_ok=True)
os.makedirs(output_directory_stretch, exist_ok=True)
supported_formats = ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff')
processed_count = 0
for filename in os.listdir(input_directory):
if filename.lower().endswith(supported_formats):
input_path = os.path.join(input_directory, filename)
output_filename = os.path.splitext(filename)[0] + '.png'
output_path_keep_ratio = os.path.join(output_directory_keep_ratio, output_filename)
output_path_stretch = os.path.join(output_directory_stretch, output_filename)
success_keep = process_single_image_bw_keep_ratio(
input_path, output_path_keep_ratio, 128, 30, 0, (250, 200)
)
success_stretch = process_single_image_bw_stretch(
input_path, output_path_stretch, 128, 30, 0, (250, 200)
)
if success_keep and success_stretch:
print(f"已处理: {filename} -> 保持比例 & 拉伸版本")
processed_count += 1
else:
print(f"处理失败: {filename}")
print(f"步骤5完成:白色斑点狗处理,成功处理 {processed_count} 张图片")
# ==================== 步骤6: 圆形标记处理 ====================
def get_sheep_contour(img_rgba):
"""提取轮廓点"""
alpha = np.array(img_rgba.split()[3])
sheep_mask = alpha > 0
contour_points = []
try:
import cv2
mask_uint8 = (sheep_mask * 255).astype(np.uint8)
contours, _ = cv2.findContours(mask_uint8, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
for point in cnt:
x, y = point[0]
contour_points.append((x, y))
except ImportError:
dx = np.diff(sheep_mask, axis=1)
dy = np.diff(sheep_mask, axis=0)
y_left, x_left = np.where(dx == 1)
y_right, x_right = np.where(dx == -1)
x_top, y_top = np.where(dy == 1)
x_bottom, y_bottom = np.where(dy == -1)
contour_points.extend([(x+1, y) for x, y in zip(x_left, y_left)])
contour_points.extend([(x, y) for x, y in zip(x_right, y_right)])
contour_points.extend([(x, y+1) for x, y in zip(x_top, y_top)])
contour_points.extend([(x, y) for x, y in zip(x_bottom, y_bottom)])
if not contour_points:
return None
return KDTree(contour_points)
def is_circle_conflict(circle_center, circle_radius, contour_kdtree):
"""判断圆形是否冲突"""
if contour_kdtree is None:
return False
min_distance, _ = contour_kdtree.query(circle_center, k=1)
return min_distance < circle_radius
def find_valid_circle_by_priority(img_rgba, contour_kdtree, priority, edge_margin=5):
"""查找有效圆形"""
width, height = img_rgba.size
diameter_candidates = range(50, 15, -1)
offset = 5
corners_calc = {
"左下角": lambda r: (edge_margin + r + offset, height - edge_margin - r - offset),
"右下角": lambda r: (width - edge_margin - r - offset, height - edge_margin - r - offset),
"左上角": lambda r: (edge_margin + r, edge_margin + r),
"右上角": lambda r: (width - edge_margin - r, edge_margin + r)
}
for diameter in diameter_candidates:
radius = diameter // 2
for corner_name in priority:
x, y = corners_calc[corner_name](radius)
if (radius <= x <= width - radius) and (radius <= y <= height - radius):
if not is_circle_conflict((x, y), radius, contour_kdtree):
return {
"diameter": diameter,
"radius": radius,
"corner": corner_name,
"center": (x, y),
"scheme_name": priority[0]
}
return None
def select_best_circle(circle_infos):
"""筛选最优圆形"""
if not circle_infos:
return None
unique_circles = []
seen = set()
for info in circle_infos:
key = (info["diameter"], info["corner"])
if key not in seen:
seen.add(key)
unique_circles.append(info)
unique_circles.sort(key=lambda x: -x["diameter"])
best_circle = unique_circles[0]
return best_circle
def process_single_folder(input_folder, output_folder, edge_margin=5):
"""处理单个文件夹的圆形标记"""
supported_formats = (".png", ".gif")
os.makedirs(output_folder, exist_ok=True)
priority_schemes = [
("优先左下角", ["左下角", "右下角", "左上角", "右上角"]),
("优先右下角", ["右下角", "左下角", "左上角", "右上角"]),
("优先左上角", ["左上角", "右上角", "左下角", "右下角"]),
("优先右上角", ["右上角", "左上角", "左下角", "右下角"])
]
processed_count = 0
for root, dirs, files in os.walk(input_folder):
for filename in files:
if filename.lower().endswith(supported_formats):
file_path = os.path.join(root, filename)
try:
with Image.open(file_path) as img:
print(f"\n处理图片:{file_path}")
img_rgba_original = img.convert("RGBA")
contour_kdtree = get_sheep_contour(img_rgba_original)
base_name, ext = os.path.splitext(filename)
all_circle_infos = []
for scheme_name, priority in priority_schemes:
circle_info = find_valid_circle_by_priority(
img_rgba_original, contour_kdtree, priority, edge_margin
)
if circle_info:
all_circle_infos.append(circle_info)
print(f" - {scheme_name}:{circle_info['corner']},直径{circle_info['diameter']}px")
else:
print(f" - {scheme_name}:无可用圆形")
best_circle = select_best_circle(all_circle_infos)
if not best_circle:
output_filename = f"{base_name}_no_circle{ext}"
output_path = os.path.join(output_folder, output_filename)
img_rgba_original.save(output_path)
print(f" 结果:无可用圆形,保留原图")
continue
img_draw = img_rgba_original.copy()
draw = ImageDraw.Draw(img_draw)
diameter = best_circle["diameter"]
radius = best_circle["radius"]
x, y = best_circle["center"]
left = x - radius
top = y - radius
right = x + radius
bottom = y + radius
draw.ellipse(
[left, top, right, bottom],
fill="white",
outline="black",
width=2
)
output_filename = f"{base_name}_best_d{str(diameter).zfill(2)}_{best_circle['corner']}{ext}"
output_path = os.path.join(output_folder, output_filename)
img_draw.save(output_path)
print(f" 结果:保留最优方案 - 位置:{best_circle['corner']},直径:{diameter}px")
processed_count += 1
except Exception as e:
print(f"处理失败:{str(e)}")
return processed_count
def step6_circle_marking():
"""步骤6: 圆形标记处理"""
EDGE_MARGIN = 5
folder_mappings = [
{
"input": os.path.join(PATH, FOLDER_NAMES['06保留灰斑_透明背景_拉伸']),
"output": os.path.join(PATH, FOLDER_NAMES['10学号_头右_有灰点'])
},
{
"input": os.path.join(PATH, FOLDER_NAMES['08黑白二色_透明背景_拉伸']),
"output": os.path.join(PATH, FOLDER_NAMES['09学号_头右_无灰点'])
}
]
total_processed = 0
for mapping in folder_mappings:
input_folder = mapping["input"]
output_folder = mapping["output"]
print(f"\n{'='*60}")
print(f"开始处理文件夹:{input_folder}")
if not os.path.exists(input_folder):
print(f"警告:输入文件夹不存在,跳过 - {input_folder}")
continue
count = process_single_folder(input_folder, output_folder, EDGE_MARGIN)
total_processed += count
print(f"\n完成处理文件夹:{input_folder},共处理 {count} 个文件")
print(f"\n步骤6完成:圆形标记处理,总共处理了 {total_processed} 个文件")
# ==================== 步骤7: Word文档生成 ====================
def check_image_validity(image_path):
"""检查图片有效性"""
try:
with Image.open(image_path) as img:
img.verify()
return True
except Exception as e:
print(f"图片损坏: {image_path}, 错误: {e}")
return False
def get_valid_images(folder_path):
"""获取有效图片列表"""
valid_images = []
if not os.path.exists(folder_path):
print(f"文件夹不存在: {folder_path}")
return valid_images
for file in os.listdir(folder_path):
if file.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
img_path = os.path.join(folder_path, file)
if check_image_validity(img_path):
valid_images.append(img_path)
print(f"从 {folder_path} 找到 {len(valid_images)} 张有效图片")
return valid_images
def clear_cell_content(cell):
"""清空单元格内容"""
for paragraph in cell.paragraphs:
p = paragraph._element
p.getparent().remove(p)
cell.add_paragraph()
def process_image_group(images1, images2, template_path, output_folder, group_index, img_height=9.35):
"""处理图片组生成Word文档"""
try:
doc = Document(template_path)
except Exception as e:
print(f"打开模板失败: {e}")
return None
if len(doc.tables) == 0:
print("模板中未找到表格,处理失败")
return None
table = doc.tables[0]
if len(table.rows) < 2 or len(table.columns) < 2:
print(f"模板表格尺寸错误(当前{len(table.rows)}行{len(table.columns)}列),需至少2行2列")
return None
# 处理有灰点图片(左对齐)
for i, img_path in enumerate(images1[:2]):
row = i
col = 0
cell = table.cell(row, col)
clear_cell_content(cell)
para = cell.paragraphs[0] if cell.paragraphs else cell.add_paragraph()
para.alignment = WD_ALIGN_PARAGRAPH.LEFT
para.paragraph_format.space_before = 0
para.paragraph_format.space_after = 0
para.paragraph_format.line_spacing = 1.0
run = para.add_run()
try:
run.add_picture(img_path, height=Cm(img_height))
print(f"✅ 插入有灰点图片: 行{row+1}列{col+1} -> {os.path.basename(img_path)}")
except Exception as e:
print(f"❌ 插入有灰点图片失败: {e}")
# 处理无灰点图片(水平翻转+右对齐)
for i, img_path in enumerate(images2[:2]):
row = i
col = 1
cell = table.cell(row, col)
clear_cell_content(cell)
para = cell.paragraphs[0] if cell.paragraphs else cell.add_paragraph()
para.alignment = WD_ALIGN_PARAGRAPH.RIGHT
para.paragraph_format.space_before = 0
para.paragraph_format.space_after = 0
para.paragraph_format.line_spacing = 1.0
run = para.add_run()
try:
temp_img_name = f"temp_flip_{os.path.basename(img_path)}"
temp_img_path = os.path.join(output_folder, temp_img_name)
with Image.open(img_path) as img:
flipped_img = img.transpose(Image.FLIP_LEFT_RIGHT)
flipped_img.save(temp_img_path)
run.add_picture(temp_img_path, height=Cm(img_height))
os.remove(temp_img_path)
print(f"✅ 插入无灰点图片(水平翻转): 行{row+1}列{col+1} -> {os.path.basename(img_path)}")
except Exception as e:
print(f"❌ 插入无灰点图片失败: {e}")
if os.path.exists(temp_img_path):
os.remove(temp_img_path)
docx_path = os.path.join(output_folder, f"group_{group_index:03d}.docx")
try:
doc.save(docx_path)
print(f"📄 生成Word文档: {os.path.basename(docx_path)}")
return docx_path
except Exception as e:
print(f"❌ 保存Word文档失败: {e}")
return None
def step7_generate_word_pdf():
"""步骤7: 生成Word文档和PDF"""
folder123 = os.path.join(PATH, FOLDER_NAMES['10学号_头右_有灰点'])
folder234 = os.path.join(PATH, FOLDER_NAMES['09学号_头右_无灰点'])
template_path = os.path.join(PATH, f"{MB}.docx")
images1 = get_valid_images(folder123)
images2 = get_valid_images(folder234)
total_images = len(images1)
output_pdf = os.path.join(PATH, f"{MB}(A4一页2张)共{total_images}图.pdf")
if len(images1) == 0 or len(images2) == 0:
print("❌ 至少一个文件夹无有效图片,程序退出")
return
if len(images1) < 2 or len(images2) < 2:
print("❌ 每个文件夹至少需要2张图片(一组),程序退出")
return
temp_folder = os.path.join(PATH, "临时文件夹")
if os.path.exists(temp_folder):
shutil.rmtree(temp_folder)
os.makedirs(temp_folder, exist_ok=True)
docx_files = []
max_groups = min(len(images1) // 2, len(images2) // 2)
print(f"🔢 共可处理 {max_groups} 组图片")
for group_idx in range(max_groups):
print(f"\n--- 处理第 {group_idx+1}/{max_groups} 组 ---")
group1 = images1[group_idx*2 : (group_idx+1)*2]
group2 = images2[group_idx*2 : (group_idx+1)*2]
docx_path = process_image_group(group1, group2, template_path, temp_folder, group_idx, 9.35)
if docx_path:
docx_files.append(docx_path)
if len(docx_files) == 0:
print("❌ 未生成任何Word文档,程序退出")
shutil.rmtree(temp_folder)
return
print(f"\n--- 开始转换PDF(共 {len(docx_files)} 个Word文档)---")
pdf_files = []
for docx_path in docx_files:
pdf_path = docx_path.replace(".docx", ".pdf")
try:
convert(docx_path, pdf_path)
pdf_files.append(pdf_path)
print(f"✅ Word转PDF成功: {os.path.basename(pdf_path)}")
except Exception as e:
print(f"❌ Word转PDF失败: {e}")
if len(pdf_files) == 0:
print("❌ 未生成任何PDF文件,程序退出")
shutil.rmtree(temp_folder)
return
print(f"\n--- 开始合并PDF(共 {len(pdf_files)} 个PDF文件)---")
try:
merger = PdfMerger()
pdf_files.sort()
for pdf_path in pdf_files:
merger.append(pdf_path)
print(f"➕ 添加PDF: {os.path.basename(pdf_path)}")
merger.write(output_pdf)
merger.close()
print(f"\n🎉 PDF合并完成!最终文件: {output_pdf}")
except Exception as e:
print(f"❌ PDF合并失败: {e}")
shutil.rmtree(temp_folder)
return
print(f"\n--- 清理临时文件 ---")
try:
shutil.rmtree(temp_folder)
print(f"🗑️ 临时文件夹已删除")
except Exception as e:
print(f"⚠️ 清理临时文件夹失败: {e}")
print(f"\n🌟 步骤7完成:Word文档和PDF生成")
# ==================== 主程序 ====================
def main():
"""主程序:按顺序执行所有步骤"""
print("=" * 60)
print("斑点狗图片处理完整流程")
print("=" * 60)
# 步骤1: 图片合并与翻转
print("\n步骤1: 图片合并与翻转")
process_images()
# 步骤2: 纯白背景处理
print("\n步骤2: 纯白背景处理")
step2_white_background()
# 步骤3: 图片切边
print("\n步骤3: 图片切边")
step3_crop_images()
# 步骤4: 灰色斑点狗处理
print("\n步骤4: 灰色斑点狗处理")
batch_process_gray_images()
# 步骤5: 白色斑点狗处理
print("\n步骤5: 白色斑点狗处理")
batch_process_white_images()
# 步骤6: 圆形标记处理
print("\n步骤6: 圆形标记处理")
step6_circle_marking()
# 步骤7: Word文档生成
print("\n步骤7: Word文档生成")
step7_generate_word_pdf()
print("\n" + "=" * 60)
print("所有处理步骤完成!")
print("=" * 60)
if __name__ == "__main__":
main()

但是电脑上看图片没有黄色条纹。具体还是等黑白打印。

斑马可以在单元格的左中右,昨天做的斑点狗是左图居右,右图居左,两只狗在互相“嗅气味”。基于原图尺寸,所有打印纸的左右两侧有很多的空白

我想了想,还是用分开(左图居左、右图居右)的方法,让中间的空隙里可以写文字,好像两匹马在说话(小朋友让我写下自己给斑点狗取的名字),圆圈里可以放线条(草)
其他说明:部分斑马的头上还有黑色条纹

所有学号圆圈都在右侧

打印和裁剪


幼儿操作:
20251119 小2班 12人
会写学号的只有1号和3号,
2号、19号、14号,不熟练

聪明的1号,把下划线也画了

小班就有镜像书写情况

2号从下向上画的2

其他老师帮助写的学号




1号已经完成























作品展示
1号、2号、13号没有拍照,直接拿回去了。
















被折叠的 条评论
为什么被折叠?



