ginka-generator/shared/visual.py
2026-05-27 22:29:44 +08:00

156 lines
4.5 KiB
Python

import argparse
import os
import cv2
import json
import numpy as np
from tqdm import tqdm
from .image import matrix_to_image_cv
# -------------------------
# 加载 tile 图块
# -------------------------
def load_tiles(tile_folder):
tile_dict = {}
for file in os.listdir(tile_folder):
name, _ = os.path.splitext(file)
img = cv2.imread(os.path.join(tile_folder, file), cv2.IMREAD_UNCHANGED)
if img is None:
print(f"[WARN] Tile image {file} 读取失败,跳过")
continue
tile_dict[name] = img
print(f"加载了 {len(tile_dict)} 个图块")
return tile_dict
# -------------------------
# 主处理逻辑
# -------------------------
def normalize_filter_reasons(train_data):
reasons = train_data.get("filterReasons", [])
if isinstance(reasons, str):
return [reasons]
if not isinstance(reasons, list):
return []
return [str(reason) for reason in reasons if reason]
def draw_filter_reasons(img, reasons):
if not reasons:
return img
font = cv2.FONT_HERSHEY_SIMPLEX
font_scale = 0.45
thickness = 1
padding = 8
line_gap = 6
text_sizes = [cv2.getTextSize(text, font, font_scale, thickness)[0] for text in reasons]
line_height = max(height for _, height in text_sizes)
box_width = min(max(width for width, _ in text_sizes) + padding * 2, img.shape[1])
box_height = min(
padding * 2 + len(reasons) * line_height + max(0, len(reasons) - 1) * line_gap,
img.shape[0]
)
overlay = img.copy()
cv2.rectangle(overlay, (0, 0), (box_width, box_height), (0, 0, 0), -1)
cv2.addWeighted(overlay, 0.72, img, 0.28, 0, img)
y = padding + line_height
for text in reasons:
cv2.putText(
img,
text,
(padding, y),
font,
font_scale,
(255, 255, 255),
thickness,
cv2.LINE_AA
)
y += line_height + line_gap
return img
def render_dataset_images(json_path, tile_dict, output_folder, tile_size=32):
if not json_path:
return
if not os.path.exists(json_path):
print(f"[WARN] 数据集 {json_path} 不存在,跳过")
return
os.makedirs(output_folder, exist_ok=True)
with open(json_path, "r", encoding="utf-8") as f:
dataset = json.load(f)
data = dataset.get("data", {})
for map_id, train_data in tqdm(data.items(), desc=os.path.basename(json_path)):
map_matrix = np.array(train_data["map"])
try:
img = matrix_to_image_cv(map_matrix, tile_dict, tile_size)
except Exception as e:
print(f"[ERROR] 地图 {map_id} 转换失败: {e}")
continue
reasons = normalize_filter_reasons(train_data)
if reasons:
img = draw_filter_reasons(img, reasons)
out_path = os.path.join(output_folder, f"{map_id.replace('::', '-')}.png")
cv2.imwrite(out_path, img)
print(f"{json_path} 地图处理完毕!")
def convert_dataset_to_images(
json_path,
tile_folder,
output_folder,
tile_size=32,
filtered_json_path=None
):
# 加载 tiles
tile_dict = load_tiles(tile_folder)
render_dataset_images(json_path, tile_dict, output_folder, tile_size)
if filtered_json_path:
filtered_output_folder = os.path.join(output_folder, "filtered")
render_dataset_images(
filtered_json_path,
tile_dict,
filtered_output_folder,
tile_size
)
def parse_args():
parser = argparse.ArgumentParser(description="Convert dataset maps to preview images")
parser.add_argument("--json-path", default="data/result.json")
parser.add_argument("--tile-folder", default="tiles")
parser.add_argument("--output-folder", default="map_images")
parser.add_argument("--tile-size", type=int, default=32)
parser.add_argument("--filtered-json-path", default="data/result.filtered.json")
return parser.parse_args()
# -------------------------
# 执行
# -------------------------
if __name__ == "__main__":
args = parse_args()
filtered_json_path = args.filtered_json_path
default_filtered_path = os.path.join("data", "result.filtered.json")
if filtered_json_path is None and os.path.exists(default_filtered_path):
filtered_json_path = default_filtered_path
convert_dataset_to_images(
json_path=args.json_path,
tile_folder=args.tile_folder,
output_folder=args.output_folder,
tile_size=args.tile_size,
filtered_json_path=filtered_json_path
)