import cv2
import logging
import numpy as np
from pytiled_parser import parse_tileset, Tile, Size
from pytiled_parser.common_types import OrderedPair
from pytiled_parser.tiled_object import TiledObject, Polygon
from pytiled_parser.layer import ObjectLayer
import xml.etree.ElementTree as ET
import traceback
from collections import OrderedDict
from pathlib import Path
import argparse
import sys

# Configure logging with more detailed format
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
    handlers=[
        logging.FileHandler('collision_detector.log'),
        logging.StreamHandler(sys.stdout)
    ]
)

logger = logging.getLogger(__name__)

def handle_error(func):
    """Decorator for error handling"""
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            error_msg = f"Error in {func.__name__}: {str(e)}\n{traceback.format_exc()}"
            logger.error(error_msg)
            raise
    return wrapper

@handle_error
def save_tileset(tileset, tsx_path, output):
    """Save tileset with error handling and validation"""
    try:
        # Validate paths
        tsx_path = Path(tsx_path)
        if output:
            output = Path(output)
            output.parent.mkdir(parents=True, exist_ok=True)

        # Parse and modify TSX
        tree = ET.parse(tsx_path)
        root = tree.getroot()

        if tileset.tiles is None:
            tileset.tiles = OrderedDict()

        for tile_id, tile in tileset.tiles.items():
            if tile.objects:
                tile_elem = root.find(f"./tile[@id='{tile_id}']")
                if tile_elem is None:
                    tile_elem = ET.SubElement(root, 'tile', id=str(tile_id))
                else:
                    for og in tile_elem.findall('objectgroup'):
                        tile_elem.remove(og)

                og_elem = ET.SubElement(tile_elem, 'objectgroup', draworder="index")
                
                for obj in tile.objects.tiled_objects:
                    obj_attribs = {
                        'id': str(obj.id),
                        'x': str(obj.coordinates.x),
                        'y': str(obj.coordinates.y),
                    }
                    
                    if hasattr(obj, 'polygon') and obj.polygon:
                        object_elem = ET.SubElement(og_elem, 'object', obj_attribs)
                        points_str = ' '.join([f"{pt.x},{pt.y}" for pt in obj.polygon.points])
                        ET.SubElement(object_elem, 'polygon', points=points_str)
                    else:
                        obj_attribs.update({
                            'width': str(obj.size.width),
                            'height': str(obj.size.height),
                        })
                        ET.SubElement(og_elem, 'object', obj_attribs)

        # Save the file
        output_file = output if (output and not output.is_dir()) else tsx_path
        tree.write(output_file, encoding='utf-8', xml_declaration=True)
        logger.info(f"Successfully saved TSX file to: {output_file}")

    except Exception as e:
        logger.error(f"Failed to save tileset: {e}\n{traceback.format_exc()}")
        raise

@handle_error
def process_map(image_path, tsx_path, sensitivity=100, output=None):
    """Process the map with comprehensive error handling and logging"""
    logger.info(f"Starting map processing with parameters: image={image_path}, tsx={tsx_path}, sensitivity={sensitivity}")
    
    try:
        # Load and validate tileset
        tileset = parse_tileset(tsx_path)
        if not tileset:
            raise ValueError(f"Failed to parse tileset: {tsx_path}")

        # Load and validate image
        tileset_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        if tileset_image is None:
            raise ValueError(f"Failed to load image: {image_path}")

        # Process tiles
        tile_width = tileset.tile_width
        tile_height = tileset.tile_height
        tiles_x = tileset_image.shape[1] // tile_width
        tiles_y = tileset_image.shape[0] // tile_height

        logger.info(f"Processing {tiles_x}x{tiles_y} tiles")

        for ty in range(tiles_y):
            for tx in range(tiles_x):
                process_tile(tileset, tileset_image, tx, ty, tile_width, tile_height, sensitivity)

        # Save results
        save_tileset(tileset, tsx_path, output)
        logger.info("Map processing completed successfully")

    except Exception as e:
        logger.error(f"Map processing failed: {e}\n{traceback.format_exc()}")
        raise

def main():
    parser = argparse.ArgumentParser(description="Process tileset and add colliders")
    parser.add_argument("image_path", help="Path to the tileset image")
    parser.add_argument("tsx_path", help="Path to the TSX file")
    parser.add_argument("sensitivity", type=int, help="Edge detection sensitivity")
    parser.add_argument("--output", help="Output directory path (optional)")

    try:
        args = parser.parse_args()
        process_map(args.image_path, args.tsx_path, args.sensitivity, args.output)
    except Exception as e:
        logger.error(f"Program execution failed: {e}\n{traceback.format_exc()}")
        sys.exit(1)

if __name__ == "__main__":
    main()

bu kodu düzeltir misin process tile yerine ne kullanırım

düzgün çalışan örnek kod:
import cv2
import logging
import numpy as np
from pytiled_parser import parse_tileset, Tile,Size
from pytiled_parser.common_types import OrderedPair
from pytiled_parser.tiled_object import TiledObject, Polygon
from pytiled_parser.layer import ObjectLayer
import xml.etree.ElementTree as ET
import traceback
from collections import OrderedDict
from pathlib import Path

# Configure logging
logging.basicConfig(filename='process_map.log', level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(message)s')

def save_tileset(tileset, tsx_path,output):
    try:
        # TSX dosyasını aç ve kök düğümü al
        tree = ET.parse(tsx_path)
        root = tree.getroot()

        # `tiles` özelliği yoksa başlat
        if tileset.tiles is None:
            tileset.tiles = OrderedDict()

        # Her bir tile için işlem yap
        for tile_id, tile in tileset.tiles.items():
            if tile.objects:
                # Mevcut `tile` elemanını bul veya oluştur
                tile_elem = root.find(f"./tile[@id='{tile_id}']")
                if tile_elem is None:
                    tile_elem = ET.SubElement(root, 'tile', id=str(tile_id))
                else:
                    # Önceki `objectgroup` elemanlarını kaldır
                    for og in tile_elem.findall('objectgroup'):
                        tile_elem.remove(og)

                # Yeni `objectgroup` oluştur
                og_elem = ET.SubElement(tile_elem, 'objectgroup', draworder="index")
                for obj in tile.objects.tiled_objects:  # Düzeltilmiş erişim
                    obj_attribs = {
                        'id': str(obj.id),
                        'x': str(obj.coordinates.x),  # Access x from coordinates
                        'y': str(obj.coordinates.y),  # Access y from coordinates
                    }
                    if hasattr(obj, 'polygon') and obj.polygon:  # Check if the polygon attribute exists
                        # Polygon collider için işlem
                        object_elem = ET.SubElement(og_elem, 'object', obj_attribs)
                        points_str = ' '.join(['{0},{1}'.format(pt.x, pt.y) for pt in obj.polygon.points])
                        ET.SubElement(object_elem, 'polygon', points=points_str)
                    else:
                        # Rectangle collider için işlem
                        obj_attribs.update({
                            'width': str(obj.size.width),  # Access width from size
                            'height': str(obj.size.height),  # Access height from size
                        })
                        ET.SubElement(og_elem, 'object', obj_attribs)

       # Güncellenmiş TSX dosyasını kaydet
        output_file = output if (output and not Path(output).is_dir()) else tsx_path

        tree.write(output_file, encoding='utf-8', xml_declaration=True)
        logging.info(f"TSX dosyası başarıyla kaydedildi: {output_file}")

    except ET.ParseError as e:
        logging.error(f"TSX dosyası okunurken/parçalanırken hata: {e}")
    except Exception as e:
        logging.error(f"save_tileset fonksiyonunda beklenmeyen bir hata oluştu: {e}")


def process_map(image_path, tsx_path, sensitivity=100,output=None):
    """
    Processes the tileset image, detects colliders for each tile,
    and updates the TSX file with collider data.
    """
    try:
        logging.info("Starting image processing...")

        # Parse the TSX file
        tileset = parse_tileset(tsx_path)
        if not tileset:
            logging.error(f"Tileset could not be parsed: {tsx_path}")
            return

        # Ensure tiles dictionary is initialized
        if tileset.tiles is None:
            tileset.tiles = {}

        # Load the tileset image
        tileset_image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        if tileset_image is None:
            logging.error(f"Tileset image could not be loaded: {image_path}")
            return

        tile_width = tileset.tile_width
        tile_height = tileset.tile_height

        # Calculate the number of tiles in x and y directions
        tiles_x = tileset_image.shape[1] // tile_width
        tiles_y = tileset_image.shape[0] // tile_height

        for ty in range(tiles_y):
            for tx in range(tiles_x):
                # Extract the tile image
                tile_image = tileset_image[
                    ty * tile_height : (ty + 1) * tile_height,
                    tx * tile_width : (tx + 1) * tile_width
                ]

                # Process the tile image
                edges = cv2.Canny(tile_image, threshold1=sensitivity, threshold2=sensitivity * 2)

                # Find contours in the tile
                contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

                if contours:
                    # Compute the global tile ID
                    tile_id = ty * tiles_x + tx

                    # Get or create the tile in the tileset
                    tile = tileset.tiles.get(tile_id)
                    if not tile:
                        tile = Tile(id=tile_id)
                        tileset.tiles[tile_id] = tile

                    # Create an ObjectLayer for the tile if it doesn't exist
                    logging.info(tile)
                    if tile.objects is None:
                        tile.objects = ObjectLayer(
                            name="Colliders",
                            draw_order="index",
                            tiled_objects=[]  # Corrected parameter name
                        )

                    # Add collision objects to the tile's ObjectLayer
                    for cnt in contours:
                        # Calculate contour area and tile area
                        contour_area = cv2.contourArea(cnt)
                        tile_area = tile_width * tile_height
                        area_ratio = contour_area / tile_area

                        try:
                            if area_ratio > 0.5:
                                # Create a polygon collider
                                points = cnt.reshape(-1, 2)
                                points[:, 0] = np.clip(points[:, 0], 0, tile_width)
                                points[:, 1] = np.clip(points[:, 1], 0, tile_height)
                            
                                polygon_points = [OrderedPair(x=float(pt[0]), y=float(pt[1])) for pt in points]


                                collider = TiledObject(
                                id=len(tile.objects.tiled_objects) + 1,
                                coordinates=OrderedPair(x=0, y=0),  # Coordinates (x, y)
                                size=Size(width=0, height=0),  # Size (width, height) default to 0
                                rotation=0,  # Default rotation
                                visible=True,  # Default visible
                                name="",  # Empty name
                                class_="",  # Empty class
                                properties={},  # Empty properties dictionary
                                polygon=Polygon(points=polygon_points)  # Polygon points
                                    )
                            else:
                                # Create a rectangle collider
                                x, y, w, h = cv2.boundingRect(cnt)
                                x = max(0, min(x, tile_width))
                                y = max(0, min(y, tile_height))
                                w = min(w, tile_width - x)
                                h = min(h, tile_height - y)
                                
                         # Assuming 'coordinates' is an OrderedPair (x, y), and 'size' is a Size (width, height)
                                collider = TiledObject(
                                    id=len(tile.objects.tiled_objects) + 1,  # Unique ID
                                    coordinates=OrderedPair(x=float(x), y=float(y)),  # Coordinates (x, y)
                                    size=Size(width=float(w), height=float(h)),  # Size (width, height)
                                    rotation=0,  # Default rotation
                                    visible=True,  # Default visible
                                    name="",  # Empty name
                                    class_="",  # Empty class
                                    properties={}  # Empty properties dictionary
                                )

                            
                            # Append the collider to the tile's object layer
                            tile.objects.tiled_objects.append(collider)
                        except Exception as e:
                            # Log the error along with the line number
                            logging.error(f"Error while processing tile ({tx}, {ty}): {e}")
                            logging.error(f"Error details: {traceback.format_exc()}")

        # Save the modified tileset
        save_tileset(tileset, tsx_path,output)

        logging.info("Collider settings successfully added.")

    except Exception as e:
        # Log the general error
        logging.error(f"An unexpected error occurred: {e}")
        logging.error(f"Error details: {traceback.format_exc()}")

# User input for sensitivity value
image_path = "harita.png"  # Path to the tileset image
tsx_path = r"C:\Users\Casper\Downloads\project-bolt-sb1-rfjg3a (1)\project\project_map\tsx\fabrika.tsx"
sensitivity = int(input("Enter sensitivity value (e.g., 100): "))
output = input("Enter output directory path: ")
# Process the map and add colliders to the TSX file
process_map(
    r"C:\Users\Casper\Downloads\project-bolt-sb1-rfjg3a (1)\project\project_map\fabrika (2).png",#bu parametreleri çağrırark çalışacak
    tsx_path,#bu parametreleri çağrırark çalışacak
    sensitivity,#bu parametreleri çağrırark çalışacak
    output
)