[DEBUG] Channel 1 shape: (2048, 2048)
(1336.0, 1495.0, 0.0) [<class 'float'>, <class 'float'>, <class 'float'>]
(1240.0, 1508.0, 0.0) [<class 'float'>, <class 'float'>, <class 'float'>]
DEBUG:rasterio.env:Exiting env context: <rasterio.env.Env object at 0x33fe16970>
DEBUG:rasterio.env:Cleared existing <rasterio._env.GDALEnv object at 0x33fe16e50> options
DEBUG:rasterio._env:Stopped GDALEnv <rasterio._env.GDALEnv object at 0x33fe16e50>.
DEBUG:rasterio.env:Exiting outermost env
DEBUG:rasterio.env:Exited env context: <rasterio.env.Env object at 0x33fe16970>
[DEBUG] Channel 0 shape: (2048, 2048)
[DEBUG] Channel 1 shape: (2048, 2048)
Error executing job with overrides: ['+config=configs/multilabel_circle_net.yaml']
Traceback (most recent call last):
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 706, in <module>
    main()
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/main.py", line 94, in decorated_main
    _run_hydra(
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/utils.py", line 394, in _run_hydra
    _run_app(
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/utils.py", line 457, in _run_app
    run_and_report(
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/utils.py", line 223, in run_and_report
    raise ex
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/utils.py", line 220, in run_and_report
    return func()
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/utils.py", line 458, in <lambda>
    lambda: hydra.run(
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/_internal/hydra.py", line 132, in run
    _ = ret.return_value
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/core/utils.py", line 260, in return_value
    raise self._return_value
  File "/opt/anaconda3/envs/sam3/lib/python3.9/site-packages/hydra/core/utils.py", line 186, in run_job
    ret.return_value = task_function(task_cfg)
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 703, in main
    CenternetPipeline(cfg).train()
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 585, in train
    model = self.build_model(config, self.loaders)
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 222, in build_model
    best_model = nas.search(
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/xview3/nas.py", line 38, in search
    scores = [self.evaluation_function(model, train_loader, val_loader, device) for model in population]
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/xview3/nas.py", line 38, in <listcomp>
    scores = [self.evaluation_function(model, train_loader, val_loader, device) for model in population]
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 556, in evaluate_architecture
    encoded_targets = self.encode_batch_targets(batch, device)
  File "/Users/samerkharboush/Downloads/SAR-MPS-OptunaNasFbnYoloTransformers/train_multilabel.py", line 454, in encode_batch_targets
    centers = np.array([np.array(c[:2]) if isinstance(c, (list, tuple)) else np.array(c) for c in centers])
ValueError: setting an array element with a sequence. The requested array has an inhomogeneous shape after 1 dimensions. The detected shape was (4,) + inhomogeneous part.
(sam3) samerkharboush@Samers-MBP SAR-MPS-OptunaNasFbnYoloTransformers % 


please review, understand those files of my project maybe they are useful to understand to fix and solve this issue

#random_crop_dataset.py
import random
from typing import List, Callable, Dict, Tuple, Any, Union, Optional

import albumentations as A
import numpy as np
import math

from sklearn.neighbors import BallTree
from sklearn.utils import compute_sample_weight

from .keypoint_dataset import KeypointsDataset, LoadImageResult

__all__ = ["RandomCropFromImageDataset", "capped_randnorm"]


def capped_randnorm(low, high):
    mean = random.normalvariate((low + high) * 0.5, sigma=math.sqrt(high - low))
    return np.clip(mean, low, high)


def encode_labels(is_vessel, is_fishing):
    v = np.zeros_like(is_vessel, dtype=int)
    v[is_vessel == 0] = 1
    v[is_vessel == 1] = 2
    v[is_fishing == 0] = 2
    v[is_fishing == 1] = 3

    return v


class RandomCropFromImageDataset(KeypointsDataset):
    crop_around_ship_p: float
    crop_size: Tuple[int, int]
    balance_near_shore: Union[None, bool, float]
    shoreline_contours: List[np.ndarray]

    def __init__(
        self,
        scenes: List[str],
        shoreline_contours: List[np.ndarray],
        locations: List[str],
        centers: List[np.ndarray],
        confidences: List[np.ndarray],
        is_vessel: List[np.ndarray],
        is_fishing: List[np.ndarray],
        is_near_shore: List[np.ndarray],
        lengths: List[np.ndarray],
        scene_sizes: List[Tuple[int, int]],
        individual_transforms: Dict[str, A.Compose],
        normalization: Dict[str, A.ImageOnlyTransform],
        input_channels: List[str],
        transform: A.Compose,
        read_image_fn: Callable,
        crop_size: Tuple[int, int],
        crop_around_ship_p: float = 0.9,
        balance_near_shore: Union[None, bool, float] = None,
        balance_crowd: Union[None, bool, float] = False,
        balance_by_type=False,
        channels_last: bool = False,
    ):
        super().__init__(
            scenes=scenes,
            locations=locations,
            centers=centers,
            confidences=confidences,
            is_vessel=is_vessel,
            is_fishing=is_fishing,
            is_near_shore=is_near_shore,
            lengths=lengths,
            scene_sizes=scene_sizes,
            individual_transforms=individual_transforms,
            normalization=normalization,
            input_channels=input_channels,
            transform=transform,
            read_image_fn=read_image_fn,
            channels_last=channels_last,
        )
        self.crop_size = crop_size
        self.crop_around_ship_p = crop_around_ship_p
        self.shoreline_contours = shoreline_contours

        all_weights = []
        for index in range(len(scenes)):
            weights = np.ones(len(centers[index]), dtype=np.float32)
            num_balancing = 0
            if balance_near_shore and len(np.bincount(is_near_shore[index])) > 1:
                shore_balance_weights = compute_sample_weight("balanced", is_near_shore[index])
                shore_balance_weights = shore_balance_weights / shore_balance_weights.sum()
                weights += shore_balance_weights
                num_balancing += 1

            if balance_crowd:
                tree = BallTree(centers[index], leaf_size=8, metric="chebyshev")
                nbhs = tree.query_radius(centers[index], r=max(crop_size) // 2, count_only=True)
                inv_frequency = np.reciprocal(nbhs.astype(np.float32))
                crowd_balance_weights = inv_frequency / inv_frequency.sum()
                weights += crowd_balance_weights
                num_balancing += 1

            if balance_by_type:
                class_labels = encode_labels(is_vessel[index], is_fishing[index])
                type_balance_weights = compute_sample_weight("balanced", class_labels)
                weights += type_balance_weights
                num_balancing += 1

            if num_balancing:
                weights /= num_balancing

            all_weights.append(weights)
        self.weights = all_weights

    def __len__(self):
        return len(self.scenes)
    def __getitem__(self, index: int) -> Dict[str, Any]:
        scene_rows, scene_cols = self.scene_sizes[index]
        shoreline_contours = self.shoreline_contours[index]
        weights = self.weights[index]

        targets: Optional[LoadImageResult] = None
        should_crop = True
        while should_crop:
            centers = self.centers[index]
            if len(centers) and random.random() < self.crop_around_ship_p:
                if len(weights) != len(centers):
                    print("Size mismatch", index, self.scene_ids[index], len(weights), len(centers))
                random_ship_index = random.choices(range(len(centers)), weights)[0]
                col, row = centers[random_ship_index]

                dx = int(capped_randnorm(-self.crop_size[0] // 4, self.crop_size[0] // 4))
                dy = int(capped_randnorm(-self.crop_size[1] // 4, self.crop_size[1] // 4))

                start_row = max(0, row + dx - self.crop_size[0] // 2)
                start_col = max(0, col + dy - self.crop_size[1] // 2)

                end_row = min(scene_rows, start_row + self.crop_size[0])
                end_col = min(scene_cols, start_col + self.crop_size[1])

                crop_coords = (start_row, end_row), (start_col, end_col)
            else:
                if len(shoreline_contours) and random.random() < 0.5:
                    row, col = random.choice(shoreline_contours)
                    row, col = int(row), int(col)

                    dx = int(capped_randnorm(-self.crop_size[0] // 4, self.crop_size[0] // 4))
                    dy = int(capped_randnorm(-self.crop_size[1] // 4, self.crop_size[1] // 4))

                    start_row = max(0, row + dx - self.crop_size[0] // 2)
                    start_col = max(0, col + dy - self.crop_size[1] // 2)

                    end_row = min(scene_rows, start_row + self.crop_size[0])
                    end_col = min(scene_cols, start_col + self.crop_size[1])

                    crop_coords = (start_row, end_row), (start_col, end_col)
                else:
                    start_row = random.randint(0, scene_rows - self.crop_size[0])
                    start_col = random.randint(0, scene_cols - self.crop_size[1])
                    crop_coords = (start_row, start_row + self.crop_size[0]), (start_col, start_col + self.crop_size[1])

            targets = self.load_image(index, crop_coords=crop_coords)
            if targets is None:
                continue

            if "sar" in targets.individual_channels:
                missing_pixels = ~np.isfinite(targets.individual_channels["sar"])
            else:
                missing_pixels = ~np.isfinite(targets.individual_channels["vv"])

            missing_pixels_ratio = float(missing_pixels.sum()) / np.prod(missing_pixels.shape)
            should_crop = missing_pixels_ratio > 0.8


        targets = self.load_image(index, crop_coords=crop_coords)
        targets = self.validate_keypoints(targets)
        data = self.apply_transformations(targets)

        image = data["image"]
        keypoints = np.asarray(data["keypoints"], dtype=np.float32).reshape((-1, 3)) 
        is_vessel = np.asarray(data["is_vessel"], dtype=np.int64).reshape((-1))
        is_fishing = np.asarray(data["is_fishing"], dtype=np.int64).reshape((-1))
        is_near_shore = np.asarray(data["is_near_shore"], dtype=np.int64).reshape((-1))
        confidences = np.asarray(data["confidences"], dtype=object).reshape((-1))

        if len(keypoints) != len(is_vessel):
            raise RuntimeError("Number of is_vessel does not equal to number of keypoints")
        if len(keypoints) != len(is_fishing):
            raise RuntimeError("Number of is_fishing does not equal to number of keypoints")

        centers = keypoints[:, :2].reshape((-1, 2))
        lengths = keypoints[:, 2].reshape((-1))

        return self.build_sample(
            image=image,
            centers=centers,
            confidences=confidences,
            is_vessel=is_vessel,
            is_fishing=is_fishing,
            is_near_shore=is_near_shore,
            lengths=lengths,
            index=index,
            folder=self.folders[index],
            image_id=self.scene_ids[index],
            location=self.locations[index],
            crop_coords=crop_coords,
        )
    
    def validate_keypoints(self, targets):
        if targets is None:
            return None
        validated_centers = []
        validated_lengths = []
        for center, length in zip(targets.centers, targets.lengths):
            validated_centers.append((float(center[0]), float(center[1])))
            validated_lengths.append(float(length))
        return targets._replace(
            centers=validated_centers,
            lengths=validated_lengths
        )

#keypoint_dataset.py
import os
from collections import namedtuple
from typing import List, Callable, Dict, Tuple, Any

import albumentations as A
import numpy as np
from pytorch_toolbelt.utils import fs
from torch.utils.data import Dataset
from torch.utils.data.dataloader import default_collate

__all__ = ["KeypointsDataset", "LoadImageResult"]

LoadImageResult = namedtuple(
    "LoadImageResult",
    ("individual_channels", "centers", "confidences", "is_vessel", "is_fishing", "lengths", "is_near_shore"),
)


class KeypointsDataset(Dataset):
    scenes: List[str]
    scene_ids: List[str]
    locations: List[str]

    centers: List[np.ndarray]
    confidences: List[np.ndarray]
    is_vessel: List[np.ndarray]
    is_fishing: List[np.ndarray]
    is_near_shore: List[np.ndarray]

    lengths: List[np.ndarray]
    scene_sizes: List[Tuple[int, int]]

    individual_transforms: Dict[str, A.Compose]
    input_channels: List[str]
    transform: A.Compose

    read_image_fn: Callable
    channels_last: bool

    def __init__(
        self,
        scenes: List[str],
        locations: List[str],
        centers: List[np.ndarray],
        confidences: List[np.ndarray],
        is_vessel: List[np.ndarray],
        is_fishing: List[np.ndarray],
        is_near_shore: List[np.ndarray],
        lengths: List[np.ndarray],
        scene_sizes: List[Tuple[int, int]],
        individual_transforms: Dict[str, A.Compose],
        normalization: Dict[str, A.ImageOnlyTransform],
        input_channels: List[str],
        transform: A.Compose,
        read_image_fn: Callable,
        channels_last=False,
    ):
        if (
            len(scenes) != len(centers)
            or len(scenes) != len(locations)
            or len(scenes) != len(confidences)
            or len(scenes) != len(lengths)
            or len(scenes) != len(is_vessel)
            or len(scenes) != len(is_fishing)
            or len(scenes) != len(is_near_shore)
            or len(scenes) != len(scene_sizes)
        ):
            raise ValueError(
                f"Number of images ({len(scenes)}) must be equal number of keypoints ({len(centers)}) and number of labels ({len(is_vessel)})"
            )
        self.channels_last = channels_last
        self.scenes = scenes
        self.locations = locations
        self.scene_ids = list(map(fs.id_from_fname, scenes))
        self.folders = list(fs.id_from_fname(os.path.dirname(x)) for x in scenes)
        self.centers = centers
        self.confidences = confidences
        self.lengths = lengths
        self.scene_sizes = scene_sizes
        self.is_vessel = is_vessel
        self.is_fishing = is_fishing
        self.is_near_shore = is_near_shore
        self.transform = transform
        self.read_image_fn = read_image_fn

        self.individual_transforms = individual_transforms
        self.normalization = normalization
        self.input_channels = input_channels

    def load_image(self, index: int, crop_coords=None) -> LoadImageResult:
        individual_channels = self.read_image_fn(self.scenes[index], channels=self.input_channels, crop_coords=crop_coords)

        centers = self.centers[index]
        confidences = self.confidences[index]
        is_vessel = self.is_vessel[index]
        is_fishing = self.is_fishing[index]
        lengths = self.lengths[index]
        is_near_shore = self.is_near_shore[index]

        # Remove invisible keypoints & shift visible ones to crop origin
        if crop_coords is not None:
            (y1, y2), (x1, x2) = crop_coords
            mask = (y1 <= centers[:, 1]) & (centers[:, 1] < y2) & (x1 <= centers[:, 0]) & (centers[:, 0] < x2)
            centers = centers[mask] - np.array([x1, y1]).reshape((1, 2))
            confidences = confidences[mask]
            is_fishing = is_fishing[mask]
            is_near_shore = is_near_shore[mask]
            is_vessel = is_vessel[mask]
            lengths = lengths[mask]

        return LoadImageResult(
            individual_channels=individual_channels,
            centers=centers,
            confidences=confidences,
            is_vessel=is_vessel,
            is_fishing=is_fishing,
            lengths=lengths,
            is_near_shore=is_near_shore,
        )

    def apply_transformations(self, data: LoadImageResult) -> Dict[str, Any]:
        # Apply channel-specific augmentations
        channels = []
        for channel_name in self.input_channels:
            img = data.individual_channels[channel_name]

            if channel_name in self.individual_transforms:
                img = self.individual_transforms[channel_name](image=img)["image"]


            img = self.normalization[channel_name](image=img)["image"]

            channels.append(img)

        # Check the shapes of all channels
        
        for idx, ch in enumerate(channels):
            print(f"[DEBUG] Channel {idx} shape: {ch.shape}")

            # Apply common augmentations
        image = np.dstack(channels)
        keypoints = [(float(cx), float(cy), float(length)) for (cx, cy), length in zip(data.centers, data.lengths)]

        
        for kp in keypoints:
            print(kp, [type(k) for k in kp])

         # Check if keypoint lengths match labels
        if (
            len(keypoints) != len(data.confidences)
            or len(keypoints) != len(data.is_vessel)
            or len(keypoints) != len(data.is_fishing)
            or len(keypoints) != len(data.is_near_shore)
        ):
            raise RuntimeError("Detected mismatch in input data. Length of keypoints array not equal to present labels")
        
        transformed = self.transform(
            image=image,
            keypoints=keypoints,
            is_vessel=data.is_vessel,
            is_fishing=data.is_fishing,
            is_near_shore=data.is_near_shore,
            confidences=data.confidences,
        )

        # Return a dictionary instead of a tuple
        return {
            'image': transformed['image'],
            'keypoints': transformed['keypoints'],
            'is_vessel': transformed['is_vessel'],
            'is_fishing': transformed['is_fishing'],
            'is_near_shore': transformed['is_near_shore'],
            'confidences': transformed['confidences'],
        }

    def __len__(self):
        return len(self.scenes)

    def __getitem__(self, index: int) -> Dict[str, Any]:
        raise NotImplementedError()

    def get_collate_fn(self) -> Callable:
        return default_collate

    def build_sample(
        self,
        image,
        centers,
        confidences,
        is_vessel,
        is_fishing,
        is_near_shore,
        lengths,
        index,
        folder: str,
        image_id: str,
        location: str,
        crop_coords: Tuple[Tuple[int, int], Tuple[int, int]],
    ) -> Dict[str, Any]:
        raise NotImplementedError()

#io.py
import os
from typing import List, Dict, Tuple, Optional, Union

import cv2
import numpy as np

__all__ = [
    "stack_multichannel_image",
    "read_tiff",
    "read_multichannel_image",
    "read_tiff_with_scaling",
]

from xview3.constants import NODATA_VV_DB

channel_name_to_file_name = {
    "vv": "VV_dB.tif",  # VV-polarization SAR image (dB), 10-meter pixels
    "vh": "VH_dB.tif",  # VH-polarization SAR image (dB), 10-meter pixels
    "bathymetry": "bathymetry.tif",  # bathymetry and elevation image (meters), 500-meter pixels
    "wind_direction": "owiWindDirection.tif",  # wind direction (degrees clockwise from north), 500-meter pixels
    "wind_quality": "owiWindQuality.tif",  # wind quality mask (0 good, 1 medium, 2 low, 3 poor), 500-meter pixels
    "wind_speed": "owiWindSpeed.tif",  # wind speed (meters per second), 500-meter pixels
    "mask": "owiMask.tif",  # land/ice mask (0 valid, 1 land, 2 ice), 500-meter pixels
}


def read_tiff(image_fname: str, crop_coords: Optional[Tuple[Tuple[int, int], Tuple[int, int]]] = None) -> np.ndarray:
    import rasterio
    from rasterio.windows import Window

    window = None
    if crop_coords is not None:
        (row_start, row_stop), (col_start, col_stop) = crop_coords
        window = Window.from_slices((row_start, row_stop), (col_start, col_stop))

    if not os.path.isfile(image_fname):
        raise FileNotFoundError(image_fname)

    with rasterio.open(image_fname) as f:
        return f.read(1, window=window)


def read_owi_mask_with_scaling(
    image_fname,
    crop_coords: Tuple[Tuple[int, int], Tuple[int, int]],
    s: Union[float, Tuple[float, float]] = 1.0 / 50.0,
) -> np.ndarray:
    """

    :param image:
    :param crop_coords:   (row_start, row_stop), (col_start, col_stop)
    :param s:
    :return:
    """
    if isinstance(s, float):
        sx = sy = s
    else:
        sx, sy = s

    image = read_tiff(image_fname)
    (row_start, row_stop), (col_start, col_stop) = crop_coords

    w = col_stop - col_start
    h = row_stop - row_start
    dsize = w, h

    src = np.array(
        [
            [col_start * sx, row_start * sy],
            [col_stop * sx, row_start * sy],
            [col_start * sx, row_stop * sy],
        ],
        dtype=np.float32,
    )
    # dst = np.array([[0, 0], [w - 1, 0], [0, h - 1]], dtype=np.float32)
    dst = np.array([[0, 0], [w, 0], [0, h]], dtype=np.float32)

    M = cv2.getAffineTransform(np.expand_dims(src, 1), np.expand_dims(dst, 1))

    water = image == 0
    land = (image == 1) | (image == 5)
    ice = (image == 2) | (image == 6)

    patch = cv2.warpAffine(
        np.dstack([water, land, ice]).astype(np.float32),
        M,
        dsize=dsize,
        flags=cv2.INTER_CUBIC,
        borderMode=cv2.BORDER_CONSTANT,
        borderValue=0,
    )
    return patch


def read_tiff_with_scaling(
    image_fname: str,
    crop_coords: Tuple[Tuple[int, int], Tuple[int, int]],
    s: Union[float, Tuple[float, float]] = 1.0 / 50.0,
) -> np.ndarray:
    """

    :param image_fname:
    :param crop_coords:   (row_start, row_stop), (col_start, col_stop)
    :param s:
    :return:
    """
    if isinstance(s, float):
        sx = sy = s
    else:
        sx, sy = s

    image = read_tiff(image_fname)
    (row_start, row_stop), (col_start, col_stop) = crop_coords

    w = col_stop - col_start
    h = row_stop - row_start
    dsize = w, h

    src = np.array(
        [
            [col_start * sx, row_start * sy],
            [col_stop * sx, row_start * sy],
            [col_start * sx, row_stop * sy],
        ],
        dtype=np.float32,
    )
    # dst = np.array([[0, 0], [w - 1, 0], [0, h - 1]], dtype=np.float32)
    dst = np.array([[0, 0], [w, 0], [0, h]], dtype=np.float32)

    M = cv2.getAffineTransform(np.expand_dims(src, 1), np.expand_dims(dst, 1))

    patch = cv2.warpAffine(
        image,
        M,
        dsize=dsize,
        flags=cv2.INTER_LANCZOS4,
        borderMode=cv2.BORDER_CONSTANT,
        borderValue=NODATA_VV_DB,
    )
    return patch


def read_multichannel_image(scene_id, channels: List[str], crop_coords=None):
    individual_channels = {}
    for channel in channels:
        if channel in {"diff(vv,vh)", "mean(vv,vh)"}:
            continue

        if channel in {"vv", "vh"}:
            channel_image_fname = os.path.join(scene_id, channel_name_to_file_name[channel])
            img = read_tiff(channel_image_fname, crop_coords=crop_coords).astype(np.float32, copy=False)
        elif channel == "sar":
            img = np.dstack(
                [
                    read_tiff(os.path.join(scene_id, channel_name_to_file_name["vh"]), crop_coords=crop_coords).astype(
                        np.float32, copy=False
                    ),
                    read_tiff(os.path.join(scene_id, channel_name_to_file_name["vv"]), crop_coords=crop_coords).astype(
                        np.float32, copy=False
                    ),
                ]
            )
        elif channel == "mask":
            channel_image_fname = os.path.join(scene_id, channel_name_to_file_name[channel])
            img = read_owi_mask_with_scaling(channel_image_fname, crop_coords=crop_coords)
        else:
            channel_image_fname = os.path.join(scene_id, channel_name_to_file_name[channel])
            img = read_tiff_with_scaling(channel_image_fname, crop_coords=crop_coords)
        img[img == NODATA_VV_DB] = float("nan")
        individual_channels[channel] = img

    return individual_channels


def stack_multichannel_image(individual_channels: Dict[str, np.ndarray], input_channels: List[str]):
    channels = []
    for channel_name in input_channels:
        img = individual_channels[channel_name]
        channels.append(img)

    image = np.dstack(channels)
    return image

#fixed_crop_dataset.py
from typing import List, Callable, Dict, Tuple, Any

import albumentations as A
import numpy as np

from .keypoint_dataset import KeypointsDataset

__all__ = ["FixedCropFromImageDataset"]


class FixedCropFromImageDataset(KeypointsDataset):
    crop_coords = None

    def __init__(
        self,
        scenes: List[str],
        locations: List[str],
        centers: List[np.ndarray],
        confidences: List[np.ndarray],
        is_vessel: List[np.ndarray],
        is_fishing: List[np.ndarray],
        is_near_shore: List[np.ndarray],
        crop_coords: List[Tuple],
        lengths: List[np.ndarray],
        scene_sizes: List[Tuple[int, int]],
        individual_transforms: Dict[str, A.Compose],
        normalization: Dict[str, A.ImageOnlyTransform],
        input_channels: List[str],
        transform: A.Compose,
        read_image_fn: Callable,
        channels_last=False,
    ):
        super().__init__(
            scenes=scenes,
            locations=locations,
            centers=centers,
            confidences=confidences,
            is_vessel=is_vessel,
            is_fishing=is_fishing,
            is_near_shore=is_near_shore,
            lengths=lengths,
            scene_sizes=scene_sizes,
            individual_transforms=individual_transforms,
            input_channels=input_channels,
            normalization=normalization,
            transform=transform,
            read_image_fn=read_image_fn,
            channels_last=channels_last,
        )
        self.crop_coords = crop_coords

    def __len__(self):
        return len(self.scenes)

    def __getitem__(self, index: int) -> Dict[str, Any]:
        crop_coords = self.crop_coords[index]

        data = self.load_image(index, crop_coords=crop_coords)
        data = self.apply_transformations(data)

        image = data["image"]
        keypoints = np.asarray(data["keypoints"], dtype=np.float32).reshape((-1, 3))
        is_vessel = np.asarray(data["is_vessel"], dtype=np.int64).reshape((-1))
        is_fishing = np.asarray(data["is_fishing"], dtype=np.int64).reshape((-1))
        is_near_shore = np.asarray(data["is_near_shore"], dtype=np.int64).reshape((-1))
        confidences = np.asarray(data["confidences"], dtype=object).reshape((-1))

        image_id = self.scene_ids[index]
        folder = self.folders[index]

        if len(keypoints) != len(is_vessel):
            raise RuntimeError("Number of is_vessel does not equal to number of labels")
        if len(keypoints) != len(is_fishing):
            raise RuntimeError("Number of is_fishing does not equal to number of labels")
        if len(keypoints) != len(is_near_shore):
            raise RuntimeError("Number of is_near_shore does not equal to number of labels")
        if len(keypoints) != len(is_near_shore):
            raise RuntimeError("Number of is_near_shore does not equal to number of labels")
        if len(keypoints) != len(confidences):
            raise RuntimeError("Number of confidences does not equal to number of labels")

        centers = keypoints[:, :2].reshape((-1, 2))
        lengths = keypoints[:, 2].reshape((-1))
        return self.build_sample(
            image=image,
            centers=centers,
            confidences=confidences,
            is_vessel=is_vessel,
            is_fishing=is_fishing,
            lengths=lengths,
            folder=folder,
            index=index,
            image_id=image_id,
            is_near_shore=is_near_shore,
            location=self.locations[index],
            crop_coords=crop_coords,
        )