
【教学类-97-10】20251126虚拟人物照片转简笔画效果
https://mp.csdn.net/mp_blog/creation/editor/155297482

'''
对“切边原图”操作,制作白色斑点狗——黑白化+背景透明+切边+统一大小
豆包,阿夏
20251117
'''
import os
from PIL import Image
import numpy as np
def convert_to_pure_bw(image_path, output_path, threshold=128):
"""
将图片转为纯黑白两色(黑色0,0,0和白色255,255,255)
参数:
image_path: 输入图片路径
output_path: 输出图片路径
threshold: 二值化阈值
"""
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') # 转回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 crop_transparent_edges(image, margin=0):
"""
裁剪掉图片的透明边缘,并保留指定的间距
参数:
image: PIL Image对象(RGBA模式)
margin: 要保留的间距(磅/像素)
返回:
裁剪后的PIL Image对象
"""
# 转换为numpy数组
data = np.array(image)
# 获取alpha通道
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)
# 裁剪图片
cropped_image = image.crop((left, top, right + 1, bottom + 1))
return cropped_image
def make_background_transparent(bw_image_path, output_path, tolerance=30, margin=0):
"""
将黑白图片的白色背景变为透明并裁剪透明边缘
参数:
bw_image_path: 黑白图片路径
output_path: 输出图片路径
tolerance: 颜色容差,控制背景识别的灵敏度
margin: 裁剪后保留的间距(磅/像素)
"""
# 打开黑白图片并转换为RGBA模式
with Image.open(bw_image_path) as img:
# 转换为RGBA模式
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)
)
# 将白色背景像素的alpha通道设为0(透明)
data[white_mask.T] = (255, 255, 255, 0)
# 转换回Image
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)):
"""
将图片调整为统一大小,保持宽高比,在空白处填充透明
参数:
image_path: 输入图片路径
output_path: 输出图片路径
target_size: 目标尺寸 (宽, 高)
"""
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)):
"""
将图片拉伸到精确的目标尺寸
参数:
image_path: 输入图片路径
output_path: 输出图片路径
target_size: 目标尺寸 (宽, 高)
"""
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, bw_threshold=128, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""
处理单张图片:黑白化 → 透明化 → 保持比例统一尺寸
参数:
image_path: 输入图片路径
output_path: 输出图片路径
bw_threshold: 黑白化阈值
transparency_tolerance: 透明化容差
margin: 裁剪边距
target_size: 目标尺寸
"""
try:
# 步骤1: 转为黑白图片(临时文件)
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
# 步骤2: 背景透明化(临时文件)
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(bw_path, temp_transparent_path, transparency_tolerance, margin)
# 步骤3: 保持比例统一尺寸
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_stretch(image_path, output_path, bw_threshold=128, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""
处理单张图片:黑白化 → 透明化 → 精确拉伸到目标尺寸
参数:
image_path: 输入图片路径
output_path: 输出图片路径
bw_threshold: 黑白化阈值
transparency_tolerance: 透明化容差
margin: 裁剪边距
target_size: 目标尺寸
"""
try:
# 步骤1: 转为黑白图片(临时文件)
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
# 步骤2: 背景透明化(临时文件)
temp_transparent_path = output_path.replace('.png', '_trans_temp.png')
make_background_transparent(bw_path, temp_transparent_path, transparency_tolerance, margin)
# 步骤3: 精确拉伸到目标尺寸
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_images(input_dir, output_dir_keep_ratio, output_dir_stretch,
bw_threshold=128, transparency_tolerance=30, margin=0, target_size=(200, 200)):
"""
批量处理文件夹中的所有图片,生成两个版本的输出
参数:
input_dir: 输入文件夹路径
output_dir_keep_ratio: 保持比例的输出文件夹路径
output_dir_stretch: 拉伸撑满的输出文件夹路径
bw_threshold: 黑白化阈值
transparency_tolerance: 透明化容差
margin: 裁剪边距
target_size: 目标尺寸
"""
# 创建输出文件夹(如果不存在)
os.makedirs(output_dir_keep_ratio, exist_ok=True)
os.makedirs(output_dir_stretch, exist_ok=True)
# 支持的图片格式
supported_formats = ('.png', '.jpg', '.jpeg', '.gif', '.bmp', '.tiff')
# 遍历输入文件夹中的所有文件
processed_count = 0
for filename in os.listdir(input_dir):
# 检查文件是否为支持的图片格式
if filename.lower().endswith(supported_formats):
input_path = os.path.join(input_dir, filename)
# 构建输出文件路径,统一保存为PNG格式
output_filename = os.path.splitext(filename)[0] + '.png'
# 保持比例版本
output_path_keep_ratio = os.path.join(output_dir_keep_ratio, output_filename)
# 拉伸撑满版本
output_path_stretch = os.path.join(output_dir_stretch, output_filename)
# 处理保持比例版本
success_keep_ratio = process_single_image_keep_ratio(
input_path, output_path_keep_ratio,
bw_threshold, transparency_tolerance,
margin, target_size
)
# 处理拉伸撑满版本
success_stretch = process_single_image_stretch(
input_path, output_path_stretch,
bw_threshold, transparency_tolerance,
margin, target_size
)
if success_keep_ratio and success_stretch:
print(f"已处理: {filename} -> 保持比例 & 拉伸版本")
processed_count += 1
else:
print(f"处理失败或部分失败: {filename}")
print(f"批量处理完成!成功处理 {processed_count} 张图片,生成两个版本")
if __name__ == "__main__":
# 输入文件夹
path=r'C:Usersjg2yXRZOneDrive桌面20251120模拟小孩照片并长大照片'
# a = '00原图2'
a = '04原图切边'
input_directory = path + fr'{a}'
# 输出文件夹 - 保持比例版本
output_directory_keep_ratio = os.path.join(path, f"07{a[2:]}_黑白二色_透明背景_原比例")
# 输出文件夹 - 拉伸撑满版本
output_directory_stretch = os.path.join(path, f"08{a[2:]}_黑白二色_透明背景_拉伸")
# 检查输入文件夹是否存在
if not os.path.exists(input_directory):
print(f"错误: 文件夹 '{input_directory}' 不存在")
else:
# 执行批量处理
batch_process_images(
input_directory,
output_directory_keep_ratio,
output_directory_stretch,
bw_threshold=128, # 黑白化阈值(0-255)
transparency_tolerance=30, # 透明化容差
margin=0, # 裁剪边距
# target_size=(150, 250) # 目标尺寸
# target_size=(300, 500) # 目标尺寸
target_size=(1200, 1000) # 目标尺寸
)
print("所有图片处理完成!")
print(f"保持比例版本保存在: {output_directory_keep_ratio}")
print(f"拉伸撑满版本保存在: {output_directory_stretch}")
、


'''
照片转黑白线条
豆包,阿夏
20251128
'''
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os # 处理文件/文件夹
from pathlib import Path # 更便捷的路径操作
def photo_to_stick_figure(image_path, output_path, radius=1):
"""
将单张照片转为黑白简笔画线条(兼容中文路径)
:param image_path: 输入照片路径(支持中文/英文)
:param output_path: 输出简笔画路径
:param radius: 最小值滤波半径(1-3像素最佳)
"""
# ========== 读取图片(兼容中文路径) ==========
try:
img_bytes = np.fromfile(image_path, dtype=np.uint8)
img = cv2.imdecode(img_bytes, cv2.IMREAD_COLOR)
if img is None:
raise FileNotFoundError(f"无法读取图片(文件损坏/格式错误):{image_path}")
except Exception as e:
print(f"【失败】{image_path} → {e}")
return False # 返回失败标识
# 转RGB(OpenCV默认BGR)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 2. 去色(灰度图)
gray = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
# 3. 反相
inverted = 255 - gray
# 4. 最小值滤波(腐蚀操作)
kernel = np.ones((radius*2+1, radius*2+1), np.uint8)
min_filtered = cv2.erode(inverted, kernel)
# 5. 颜色减淡混合(复刻PS效果)
gray_float = gray.astype(np.float32)
min_float = min_filtered.astype(np.float32)
blend = np.divide(gray_float, 255 - min_float, out=np.zeros_like(gray_float), where=(255 - min_float)!=0) * 255
blend = np.clip(blend, 0, 255).astype(np.uint8)
# 6. 增强对比度(自动阈值)
_, final = cv2.threshold(blend, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# ========== 保存图片(兼容中文路径) ==========
try:
# 自动匹配原文件后缀(png/jpg)
suffix = Path(output_path).suffix.lower()
encode_param = [int(cv2.IMWRITE_PNG_COMPRESSION), 0] if suffix == '.png' else [int(cv2.IMWRITE_JPEG_QUALITY), 95]
result_bytes = cv2.imencode(suffix, cv2.cvtColor(final, cv2.COLOR_RGB2BGR), encode_param)[1]
result_bytes.tofile(output_path)
print(f"【成功】{image_path} → {output_path}")
return True # 返回成功标识
except Exception as e:
print(f"【失败】保存{output_path} → {e}")
return False
def batch_process(input_dir, output_dir, radius=1):
"""
批量处理文件夹内所有图片
:param input_dir: 输入文件夹路径(如r"XXX123")
:param output_dir: 输出文件夹路径(如r"XXX234")
:param radius: 简笔画线条精细度
"""
# 1. 检查输入文件夹是否存在
input_path = Path(input_dir)
if not input_path.exists():
print(f"错误:输入文件夹不存在 → {input_dir}")
return
# 2. 创建输出文件夹(不存在则自动创建)
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
# 3. 定义支持的图片格式(可扩展)
support_formats = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
# 4. 遍历输入文件夹内所有文件
total = 0 # 总文件数
success = 0 # 成功数
fail = 0 # 失败数
for file in input_path.iterdir():
# 跳过文件夹,只处理文件
if file.is_file():
# 过滤非图片文件
if file.suffix.lower() in support_formats:
total += 1
# 构建输出文件路径(同名保存)
output_file = output_path / file.name
# 调用单张处理函数
if photo_to_stick_figure(str(file), str(output_file), radius):
success += 1
else:
fail += 1
# 5. 输出批量处理统计结果
print("
=== 批量处理完成 ===")
print(f"总文件数:{total} | 成功:{success} | 失败:{fail}")
if fail > 0:
print("请检查失败文件的路径/格式是否正确!")
# ------------------- 批量调用示例 -------------------
if __name__ == "__main__":
# 替换为你的实际文件夹路径(支持中文!)
path=r'C:Usersjg2yXRZOneDrive桌面20251120模拟小孩照片并长大照片'
num=10
batch_process(
input_dir=path+r" 0原图", # 输入文件夹:123
output_dir=path+fr" 0PS修图{num}", # 输出文件夹:234
radius=num # 线条精细度:1=精细,2=中等,3=粗犷
)









它不是对原图做 “滤镜式处理”,而是基于 AI 模型的绘画能力:
先识别原图的内容(小女孩、衣服图案、发型);再用 “简笔画风格”重新生成线条(比如主动简化背景、规整衣服图案的线条、优化头发的轮廓感);相当于 “AI 照着照片画了一幅简笔画”,线条更干净、风格更统一。PS 的滤镜、代码的灰度 / 反相 / 滤波,都是对原图像素做数学运算:
原图的杂色、背景纹理会被保留(比如背景的柜子、颜色渐变会变成杂乱的线条);只能 “提取原图已有的边缘”,无法主动 “简化 / 美化线条”(比如衣服图案的细节会因原图像素不足而模糊);相当于 “给照片加了个‘线稿滤镜’”,效果依赖原图的清晰度,且无法主动优化风格。


