opencode/generate_dzi.py

115 lines
3.7 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Генератор Deep Zoom Image (DZI) тайлов из PNG для быстрого просмотра больших чертежей.
Использует PIL не требует внешних зависимостей.
Использование:
python generate_dzi.py <png_file> [--tile-size 256] [--format png]
Результат:
<png_stem>.dzi XML-дескриптор
<png_stem>_files/ папка с тайлами level/col_row.png
"""
import sys
import os
import math
from pathlib import Path
from PIL import Image
def generate_dzi(png_path: Path, tile_size: int = 256, fmt: str = "png"):
"""Генерирует DZI тайлы из PNG."""
png_path = Path(png_path)
if not png_path.exists():
print(f"[ERR] Файл не найден: {png_path}")
sys.exit(1)
base = png_path.stem
files_dir = png_path.parent / f"{base}_files"
files_dir.mkdir(exist_ok=True)
print(f"[INFO] Открываем {png_path}...")
img = Image.open(png_path)
orig_w, orig_h = img.size
print(f"[INFO] Размер: {orig_w}x{orig_h}")
# DZI uses power-of-2 levels, starting from 1x1 at level 0
max_dim = max(orig_w, orig_h)
max_level = math.ceil(math.log2(max_dim))
print(f"[INFO] Уровней: {max_level + 1} (0..{max_level})")
# Process from largest to smallest (build pyramid top-down)
current = img.convert("RGBA" if fmt == "png" else "RGB")
for level in range(max_level, -1, -1):
# Calculate size at this level
scale = 2 ** (max_level - level)
level_w = math.ceil(orig_w / scale)
level_h = math.ceil(orig_h / scale)
# Resize current image to level size
if current.size != (level_w, level_h):
current = current.resize((level_w, level_h), Image.LANCZOS)
# Save tiles
cols = math.ceil(level_w / tile_size)
rows = math.ceil(level_h / tile_size)
level_dir = files_dir / str(level)
level_dir.mkdir(exist_ok=True)
for row in range(rows):
for col in range(cols):
x = col * tile_size
y = row * tile_size
w = min(tile_size, level_w - x)
h = min(tile_size, level_h - y)
tile = current.crop((x, y, x + w, y + h))
tile_path = level_dir / f"{col}_{row}.{fmt}"
if fmt == "png":
tile.save(tile_path, "PNG", compress_level=3)
else:
tile.save(tile_path, "JPEG", quality=85)
print(f" Level {level}: {level_w}x{level_h}, {cols}x{rows} tiles")
# Write DZI descriptor
dzi_path = png_path.parent / f"{base}.dzi"
dzi_xml = f'''<?xml version="1.0" encoding="UTF-8"?>
<Image xmlns="http://schemas.microsoft.com/deepzoom/2008"
Format="{fmt}"
Overlap="0"
TileSize="{tile_size}">
<Size Width="{orig_w}"
Height="{orig_h}"/>
</Image>'''
dzi_path.write_text(dzi_xml, encoding="utf-8")
print(f"[OK] DZI создан: {dzi_path}")
print(f" Тайлы: {files_dir}")
print(f" Уровней: {max_level + 1}, TileSize: {tile_size}")
return dzi_path, files_dir
def main():
if len(sys.argv) < 2:
print("Usage: python generate_dzi.py <png_file> [tile_size] [format]")
sys.exit(1)
png = Path(sys.argv[1])
tile_size = int(sys.argv[2]) if len(sys.argv) > 2 else 256
fmt = sys.argv[3] if len(sys.argv) > 3 else "png"
generate_dzi(png, tile_size, fmt)
if __name__ == "__main__":
main()