opencode/generate_dzi.py
Кирилл Блинов 95093736da Add dimension QC, DZI generator, web viewer, and fix RAG query bug
- dimension_qc_checker.py: rules-based QC for dimension chains, overlaps, crowding
- generate_dzi.py: Deep Zoom Image tile pyramid generator for OpenSeadragon
- generate_web_viewer.py: OpenSeadragon viewer with SVG overlays and issue feedback buttons
- rag_query.py: fix LightRAG remove_think_tags crash on None response from LLM
- .gitignore: add *.pdf, *.db, backend/uploads/, backend/outputs/
2026-06-01 12:30:07 +03:00

115 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()