find-object-art/check_zip_psd.py

481 lines
20 KiB
Python
Raw Permalink Normal View History

2025-04-17 08:08:36 +00:00
###-----检测psd是否合格通过的psd压缩-----
import os
import time
import config
from psd_tools import PSDImage
from PIL import Image
import json
import utils
import sys
curr = os.path.dirname(sys.argv[0])
if curr != "":
os.system("cd " + curr)
def check_psd(psd_path):
psd_name = os.path.split(psd_path)[1]
names = psd_name.split('_')
log(f'开始检测:{psd_name}')
error_log = []
2025-05-15 10:18:56 +00:00
if len(names) != 3 and len(names) != 4 and len(names) != 5 and len(names) != 6:
2025-04-17 08:08:36 +00:00
error_log.append(config.error_log.psd_name_error.value)
# elif not names[0][0].islower():
# error_log.append(f'{psd_name}:{config.error_log.psd_name_error.value}')
# elif not names[1][0].islower():
# error_log.append(f'{psd_name}:{config.error_log.psd_name_error.value}')
# elif not names[2][0].isdigit():
# error_log.append(f'{psd_name}:{config.error_log.psd_name_error.value}')
2025-05-06 10:12:24 +00:00
try:
psd_point_num = int(names[1][:2])
except Exception as e:
print(e)
error_log.append(config.error_log.psd_name_error.value)
return
2025-04-17 08:08:36 +00:00
psd = PSDImage.open(psd_path)
color_mode = get_psd_color_mode(psd_path)
if color_mode == 'CMYK':
error_log.append(f'{psd_name}:{config.error_log.psd_cmyk.value}')
print("PSD 文件的色彩空间是 CMYK")
# elif color_mode == 'RGB':
# print("PSD 文件的色彩空间是 RGB")
psd_width = psd.width
psd_height = psd.height
2025-04-22 06:10:30 +00:00
# print(f'{psd.size}')
2025-04-22 06:10:58 +00:00
if (psd_width != config.psd_standard_width and psd_width != config.psd_standard_width2) or (psd_height != config.psd_standard_height and psd_height != config.psd_standard_height2):
error_log.append(f'{psd_name}:{config.error_log.psd_size_error.value}')
2025-04-22 06:10:30 +00:00
titem_name = 'titem'
tfull_name = 'tfull'
tmask_name = 'tmask'
tlight_name = 'tlight'
tshadow_name = 'tshadow'
group_name = 'group_'
2025-04-17 08:08:36 +00:00
all_items = []
all_full = {}
all_mask = []
2025-04-18 07:22:32 +00:00
all_tlight = []
all_tshadow = []
2025-04-22 06:10:30 +00:00
all_group = []
2025-05-06 10:12:24 +00:00
all_group_num = []
2025-04-22 06:10:30 +00:00
# all_color_it_full = []
# all_color_it_item = []
2025-10-30 07:47:59 +00:00
all_combination_full = {}
all_combination_items = []
all_combination_mask = []
2025-04-17 08:08:36 +00:00
is_exist_base = False
is_exist_titem_group = False
all_occlusion_degree = []
for layer in psd:
if ' ' in layer.name:
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.name_contains_spaces.value}')
if layer.name == 'nouse' and layer.is_group():
continue
2025-04-22 06:10:30 +00:00
if layer.name == titem_name and layer.is_group():
2025-04-17 08:08:36 +00:00
is_exist_titem_group = True
for item_layer in layer:
if ' ' in item_layer.name:
error_log.append(f'{psd_name}:{item_layer.name}:{config.error_log.name_contains_spaces.value}')
2025-10-30 07:47:59 +00:00
item_names = item_layer.name.split('_')
last_name = item_names[-1]
2025-04-22 06:10:30 +00:00
if titem_name in item_layer.name:
if item_layer.name in all_items: ##or item_layer.name in all_color_it_item:
2025-04-17 08:08:36 +00:00
error_log.append(f'{psd_name}:{item_layer.name}:{config.error_log.exit_repeat_layer.value}')
2025-10-30 07:47:59 +00:00
elif int(last_name) >= 1000:
all_combination_items.append(item_layer.name)
2025-04-17 08:08:36 +00:00
else:
2025-04-22 06:10:30 +00:00
# if 'titemcolor' in item_layer.name:
# all_color_it_item.append(item_layer.name)
# else:
all_items.append(item_layer.name)
2025-04-17 08:08:36 +00:00
else:
error_log.append(f'{psd_name}:{item_layer.name}:{config.error_log.psd_item_name_error.value}')
2025-04-22 06:10:30 +00:00
elif group_name in layer.name:
2025-05-06 10:12:24 +00:00
try:
group_num_str = layer.name.replace(group_name, "")
group_num = int(group_num_str)
except Exception as e:
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.group_name_error.value}')
print(e)
continue
2025-10-30 07:47:59 +00:00
# if group_num <= 0 or group_num > psd_point_num:
# error_log.append(f'{psd_name}:{layer.name}:{config.error_log.group_name_error.value}')
# continue
# else:
all_group_num.append(group_num)
2025-05-06 10:12:24 +00:00
2025-04-22 06:10:30 +00:00
if not layer.is_group():
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.group_name_error.value}')
elif layer.name in all_group:
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.exit_repeat_group.value}')
2025-04-18 07:22:32 +00:00
else:
2025-04-22 06:10:30 +00:00
all_group.append(layer.name)
is_exit_full = False
2025-10-30 07:47:59 +00:00
layer_names = layer.name.split('_')
layer_last_name = layer_names[-1]
2025-04-22 06:10:30 +00:00
for child_layer in layer:
2025-10-30 07:47:59 +00:00
if group_num_str not in child_layer.name and int(layer_last_name) < 1000:
2025-05-06 10:12:24 +00:00
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.child_layer_not_in_group.value}')
2025-04-22 06:10:30 +00:00
if child_layer.is_group():
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.child_layer_is_group.value}')
else:
2025-10-30 07:47:59 +00:00
full_names = child_layer.name.split('_')
last_name = full_names[-1]
2025-04-22 06:10:30 +00:00
if tfull_name in child_layer.name:
if child_layer.name in all_full.keys():
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.exit_repeat_layer.value}')
2025-10-30 07:47:59 +00:00
elif int(last_name) >= 1000:
all_combination_full[child_layer.name] = (child_layer.left, child_layer.top, child_layer.width, child_layer.height)
is_exit_full = True
2025-04-22 06:10:30 +00:00
else:
all_full[child_layer.name] = (child_layer.left, child_layer.top, child_layer.width, child_layer.height)
is_exit_full = True
elif tmask_name in child_layer.name:
if child_layer.name in all_mask:
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.exit_repeat_layer.value}')
2025-10-30 07:47:59 +00:00
elif int(last_name) >= 1000:
all_combination_mask.append(child_layer.name)
2025-04-22 06:10:30 +00:00
else:
all_mask.append(child_layer.name)
elif tshadow_name in child_layer.name:
if child_layer.name in all_tshadow:
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.exit_repeat_layer.value}')
else:
all_tshadow.append(child_layer.name)
elif tlight_name in child_layer.name:
if child_layer.name in all_tlight:
error_log.append(f'{psd_name}:{child_layer.name}:{config.error_log.exit_repeat_layer.value}')
else:
all_tlight.append(child_layer.name)
else:
error_log.append(
f'{psd_name}:{child_layer.name}:{config.error_log.child_layer_unknown.value}')
if not is_exit_full:
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.group_tfull_not_exit.value}')
2025-04-17 08:08:36 +00:00
elif layer.name == 'base':
if is_exist_base:
error_log.append(f'{psd_name}:{config.error_log.exit_more_base.value}')
else:
is_exist_base = True
else:
error_log.append(f'{psd_name}:{layer.name}:{config.error_log.layer_not_need.value}')
2025-10-30 07:47:59 +00:00
2025-04-17 08:08:36 +00:00
if not is_exist_base:
error_log.append(f'{psd_name}:{config.error_log.psd_not_exit_base.value}')
if not is_exist_titem_group:
error_log.append(f'{psd_name}:{config.error_log.item_group_not_exit.value}')
2025-05-06 10:12:24 +00:00
for i in range(1,psd_point_num + 1):
if i not in all_group_num:
error_log.append(f'{psd_name}:{i}:{config.error_log.group_lack.value}')
2025-04-17 08:08:36 +00:00
all_item_count = len(all_items)
2025-04-22 06:10:30 +00:00
if len(all_full) != all_item_count:
#or ( len(all_color_it_item) != 0 and len(all_color_it_item) != all_item_count) or (
#len(all_color_it_full) != 0 and len(all_color_it_full) != all_item_count):
2025-04-17 08:08:36 +00:00
error_log.append(f'{psd_name}:{config.error_log.item_or_full_num_error.value}')
for item in all_items:
full_name = item.replace('item', 'full')
if full_name not in all_full.keys():
error_log.append(f'{psd_name}:{item}:{config.error_log.psd_not_full.value}')
2025-04-22 06:10:30 +00:00
# if len(all_color_it_item) > 0 and len(all_color_it_full) > 0:
# for item in all_items:
# fullcolor_name = item.replace('item', 'fullcolor')
# itemcolor_name = item.replace('item', 'itemcolor')
# if fullcolor_name not in all_color_it_full:
# error_log.append(f'{psd_name}:{item}:{config.error_log.psd_not_fullcolor.value}')
# if itemcolor_name not in all_color_it_item:
# error_log.append(f'{psd_name}:{item}:{config.error_log.psd_not_itemcolor.value}')
2025-04-17 08:08:36 +00:00
# if not os.path.exists('./test'):
# os.mkdir('./test')
2025-04-22 06:10:30 +00:00
def find_layer_by_name(layers, target_name):
"""递归搜索图层中指定名称的图层"""
for layer in layers:
if layer.name == target_name:
return layer
if layer.is_group(): # 检查是否为组
found = find_layer_by_name(layer, target_name)
if found:
return found
return None
2025-04-17 08:08:36 +00:00
# 计算mask和full的遮罩百分比
for cur_mask in all_mask:
2025-04-22 06:10:30 +00:00
# img_mask = psd.composite(layer_filter=lambda mask_layer: mask_layer.name == cur_mask)
layer_mask = find_layer_by_name(psd,cur_mask)
2025-04-17 08:08:36 +00:00
full_name = cur_mask.replace('mask', 'full')
cur_full_rect = None
if full_name in all_full.keys():
cur_full_rect = all_full[full_name]
2025-04-22 06:10:30 +00:00
# img_full = psd.composite(layer_filter=lambda full_layer: full_layer.name == full_name)
layer_full = find_layer_by_name(psd, full_name)
if layer_mask is None or layer_full is None:
error_log.append(f'{psd_name}:{cur_mask}:{config.error_log.not_find_mask_full.value}')
break
img_mask = psd.composite(layer_mask)
img_full = psd.composite(layer_full)
2025-04-17 08:08:36 +00:00
# img_mask.save(f'./test/{cur_mask}.png')
# img_full.save(f'./test/{full_name}.png')
per = get_item_mask_contact_ratio(img_full, img_mask, cur_full_rect)
all_occlusion_degree.append(f'{cur_mask}遮挡{full_name}百分比:{per}\n')
# print(f'{cur_mask}遮挡{full_name}百分比:{per}')
else:
error_log.append(f'{psd_name}:{cur_mask}:{config.error_log.psd_mask_not_full.value}')
2025-10-30 07:47:59 +00:00
for cur_mask in all_combination_mask:
# img_mask = psd.composite(layer_filter=lambda mask_layer: mask_layer.name == cur_mask)
layer_mask = find_layer_by_name(psd, cur_mask)
full_name = cur_mask.replace('mask', 'full')
cur_full_rect = None
if full_name in all_combination_full.keys():
cur_full_rect = all_combination_full[full_name]
# img_full = psd.composite(layer_filter=lambda full_layer: full_layer.name == full_name)
layer_full = find_layer_by_name(psd, full_name)
if layer_mask is None or layer_full is None:
error_log.append(f'{psd_name}:{cur_mask}:{config.error_log.not_find_mask_full.value}')
break
img_mask = psd.composite(layer_mask)
img_full = psd.composite(layer_full)
# img_mask.save(f'./test/{cur_mask}.png')
# img_full.save(f'./test/{full_name}.png')
per = get_item_mask_contact_ratio(img_full, img_mask, cur_full_rect)
all_occlusion_degree.append(f'{cur_mask}遮挡{full_name}百分比:{per}\n')
# print(f'{cur_mask}遮挡{full_name}百分比:{per}')
else:
error_log.append(f'{psd_name}:{cur_mask}:{config.error_log.psd_mask_not_full.value}')
2025-04-17 08:08:36 +00:00
is_error = len(error_log) > 0
is_error_text = '' if is_error else ''
if is_error:
error_psd_dic[psd_name] = error_log
log(f'{psd_name}:检测完毕,是否检测通过:{is_error_text}')
for clog in error_log:
log(clog)
if not is_error:
occlusion_degree_file_path = f'./OcclusionDegree/{psd_name}.txt'
if not os.path.exists('./OcclusionDegree/'):
os.mkdir('./OcclusionDegree/')
if not os.path.exists(occlusion_degree_file_path):
with open(occlusion_degree_file_path, 'w') as occlusion_degree_file:
for text in all_occlusion_degree:
occlusion_degree_file.writelines(text)
pass
return len(error_log) == 0
def get_item_mask_contact_ratio(full_image, mask_image, full_rect):
# full_image = full_image.convert('RGBA')
# mask_image = mask_image.convert('RGBA')
x, y, w, h = full_rect
full_pix_count = 0
mask_pix_count = 0
for cur_x in range(x, x + w):
for cur_y in range(y, y + h):
full_pix = full_image.getpixel((cur_x, cur_y))
mask_pix = mask_image.getpixel((cur_x, cur_y))
if not full_pix == (255, 255, 255, 0):
full_pix_count += 1
if not mask_pix == (255, 255, 255, 0) and not mask_pix == full_pix:
mask_pix_count += 1
# print(f'mask_pix_count::: {mask_pix_count}')
# print(f'full_pix_count {full_pix_count}')
percentage = "{:.2%}".format(float(mask_pix_count) / float(full_pix_count))
return percentage
def log(content, new_line=True):
curr_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
if new_line:
print(curr_time + ' ' + content)
else:
print(curr_time + ' ' + content, end=' ')
###生成version文件
def make_version_file(remake):
version_file = 'version.json';
if remake:
if os.path.exists(version_file):
os.remove(version_file)
if not os.path.exists(version_file):
with open(version_file, 'w') as f:
json.dump({}, f, sort_keys=True, indent=4)
###加载版本文件
def load_version():
global versions
make_version_file(False);
with open('version.json', 'r') as f:
try:
versions = json.load(f)
except:
# 删除版本文件并重新生成
make_version_file(True);
def write_version():
with open('version.json', 'w') as f:
json.dump(versions, f, sort_keys=True, indent=4)
def convert_2_zip_file(path, is_name_end_zip=False):
file_info_arr = os.path.split(path)
file_path = file_info_arr[0]
psd_name = os.path.split(path)[1].replace('.psd', '')
psd_type = psd_name.split('_')[0]
if is_name_end_zip:
psd_path = os.path.join('./', file_info_arr[1])
os.chdir(file_path)
# 检测成功压缩到zip
utils.zip_file(psd_path, f'./{psd_name}.zip')
os.remove(psd_path)
os.chdir('../')
os.chdir('../')
else:
zip_out_path = os.path.join('./Level', psd_type)
if not os.path.exists(zip_out_path):
os.mkdir(zip_out_path)
psd_path = os.path.join('./', file_info_arr[1])
os.chdir(file_path)
# 检测成功压缩到zip
utils.zip_file(psd_path, f'../{zip_out_path}/{psd_name}.zip')
os.remove(psd_path)
os.chdir('../')
def get_psd_color_mode(psd_path):
with open(psd_path, "rb") as f:
# 读取文件头信息
header = f.read(24)
# 获取颜色模式字节
color_mode_byte = header[10]
if color_mode_byte == 0:
return "Bitmap"
elif color_mode_byte == 2:
return "RGB"
elif color_mode_byte == 4:
return "CMYK"
else:
return "Unknown"
if __name__ == '__main__':
global all_psd_count
all_psd_count = 0
global pass_psd_count
pass_psd_count = 0
global no_change_psd_count
no_change_psd_count = 0
global error_psd_dic
error_psd_dic = {}
global zip_fail_count
zip_fail_count = 0
global re_zip_count
re_zip_count = 0
load_version()
psd_name_list = []
is_force = sys.argv[1] if len(sys.argv) > 1 else False
# is_force = True
for root, dirs, files in os.walk('./'):
for name in files:
cur_file_path = os.path.join(root, name)
if name.endswith('.psd'):
all_psd_count += 1
if name.endswith('.psd') or name.endswith('.zip'):
file_info_arr = os.path.split(cur_file_path)
file_path = file_info_arr[0]
psd_name = file_info_arr[1]
if psd_name not in psd_name_list:
psd_name_list.append(psd_name)
else:
log("存在重复名称关卡:{}".format(psd_name))
else:
continue
if name.endswith('.psd') or (name.endswith('.zip') and is_force):
file_info_arr = os.path.split(cur_file_path)
file_path = file_info_arr[0]
psd_name = file_info_arr[1]
hash_old = ""
if psd_name in versions:
hash_old = versions[psd_name]
is_zip = name.endswith('.zip')
if os.path.exists(cur_file_path):
hash_now = ""
if name.endswith('.psd'):
hash_now = utils.calc_hash(cur_file_path)
if hash_now != hash_old or is_force:
psd_file_path = cur_file_path
if is_zip:
utils.unzip_file(cur_file_path)
psd_file_path = "{}.psd".format(os.path.splitext(psd_file_path)[0])
hash_now = utils.calc_hash(psd_file_path)
try:
is_passed = check_psd(psd_file_path)
if is_passed:
# 检测成功压缩到zip
convert_2_zip_file(psd_file_path, is_zip)
if is_zip:
re_zip_count += 1
versions[psd_name] = hash_now
write_version()
if name.endswith('.psd'):
pass_psd_count += 1
except Exception as e:
zip_fail_count += 1
log(f'检测或者压缩失败::{psd_file_path},{e}')
error_psd_dic[psd_name] = [f'{psd_file_path},{e}']
elif hash_now == hash_old:
no_change_psd_count += 1
convert_2_zip_file(cur_file_path, is_zip)
log(f'psd压缩完毕总共psd数量{all_psd_count}')
log(f'成功压缩新增psd个数{pass_psd_count}')
log(f'成功压缩无改动psd个数{no_change_psd_count}')
log(f'未检测通过psd个数{len(error_psd_dic)}')
log(f'重新压缩psd数量{re_zip_count}')
log(f'压缩失败个数:{zip_fail_count}')
if len(error_psd_dic) > 0:
log(f'以下psd出现问题')
for error_psd_name, error_psd_log in error_psd_dic.items():
log(f'psd名称{error_psd_name}')
for error_log in error_psd_log:
log(error_log)