# Copyright 2024-2025 The Alibaba Wan Team Authors. All rights reserved.
import math
import random
from typing import Dict, List

import cv2
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from pose2d_utils import AAPoseMeta


def draw_handpose(canvas, keypoints, hand_score_th=0.6):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """
    eps = 0.01

    H, W, C = canvas.shape
    stickwidth = max(int(min(H, W) / 200), 1)

    edges = [
        [0, 1],
        [1, 2],
        [2, 3],
        [3, 4],
        [0, 5],
        [5, 6],
        [6, 7],
        [7, 8],
        [0, 9],
        [9, 10],
        [10, 11],
        [11, 12],
        [0, 13],
        [13, 14],
        [14, 15],
        [15, 16],
        [0, 17],
        [17, 18],
        [18, 19],
        [19, 20],
    ]

    for ie, (e1, e2) in enumerate(edges):
        k1 = keypoints[e1]
        k2 = keypoints[e2]
        if k1 is None or k2 is None:
            continue
        if k1[2] < hand_score_th or k2[2] < hand_score_th:
            continue

        x1 = int(k1[0])
        y1 = int(k1[1])
        x2 = int(k2[0])
        y2 = int(k2[1])
        if x1 > eps and y1 > eps and x2 > eps and y2 > eps:
            cv2.line(
                canvas,
                (x1, y1),
                (x2, y2),
                matplotlib.colors.hsv_to_rgb([ie / float(len(edges)), 1.0, 1.0]) * 255,
                thickness=stickwidth,
            )

    for keypoint in keypoints:
        if keypoint is None:
            continue
        if keypoint[2] < hand_score_th:
            continue

        x, y = keypoint[0], keypoint[1]
        x = int(x)
        y = int(y)
        if x > eps and y > eps:
            cv2.circle(canvas, (x, y), stickwidth, (0, 0, 255), thickness=-1)
    return canvas


def draw_handpose_new(canvas, keypoints, stickwidth_type="v2", hand_score_th=0.6):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """
    eps = 0.01

    H, W, C = canvas.shape
    if stickwidth_type == "v1":
        stickwidth = max(int(min(H, W) / 200), 1)
    elif stickwidth_type == "v2":
        stickwidth = max(max(int(min(H, W) / 200) - 1, 1) // 2, 1)

    edges = [
        [0, 1],
        [1, 2],
        [2, 3],
        [3, 4],
        [0, 5],
        [5, 6],
        [6, 7],
        [7, 8],
        [0, 9],
        [9, 10],
        [10, 11],
        [11, 12],
        [0, 13],
        [13, 14],
        [14, 15],
        [15, 16],
        [0, 17],
        [17, 18],
        [18, 19],
        [19, 20],
    ]

    for ie, (e1, e2) in enumerate(edges):
        k1 = keypoints[e1]
        k2 = keypoints[e2]
        if k1 is None or k2 is None:
            continue
        if k1[2] < hand_score_th or k2[2] < hand_score_th:
            continue

        x1 = int(k1[0])
        y1 = int(k1[1])
        x2 = int(k2[0])
        y2 = int(k2[1])
        if x1 > eps and y1 > eps and x2 > eps and y2 > eps:
            cv2.line(
                canvas,
                (x1, y1),
                (x2, y2),
                matplotlib.colors.hsv_to_rgb([ie / float(len(edges)), 1.0, 1.0]) * 255,
                thickness=stickwidth,
            )

    for keypoint in keypoints:
        if keypoint is None:
            continue
        if keypoint[2] < hand_score_th:
            continue

        x, y = keypoint[0], keypoint[1]
        x = int(x)
        y = int(y)
        if x > eps and y > eps:
            cv2.circle(canvas, (x, y), stickwidth, (0, 0, 255), thickness=-1)
    return canvas


def draw_ellipse_by_2kp(img, keypoint1, keypoint2, color, threshold=0.6):
    H, W, C = img.shape
    stickwidth = max(int(min(H, W) / 200), 1)

    if keypoint1[-1] < threshold or keypoint2[-1] < threshold:
        return img

    Y = np.array([keypoint1[0], keypoint2[0]])
    X = np.array([keypoint1[1], keypoint2[1]])
    mX = np.mean(X)
    mY = np.mean(Y)
    length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
    angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
    polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
    cv2.fillConvexPoly(img, polygon, [int(float(c) * 0.6) for c in color])
    return img


def split_pose2d_kps_to_aa(kp2ds: np.ndarray) -> List[np.ndarray]:
    """Convert the 133 keypoints from pose2d to body and hands keypoints.

    Args:
        kp2ds (np.ndarray): [133, 2]

    Returns:
        List[np.ndarray]: _description_
    """
    kp2ds_body = (kp2ds[[0, 6, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 17, 20]] + kp2ds[[0, 5, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 18, 21]]) / 2
    kp2ds_lhand = kp2ds[91:112]
    kp2ds_rhand = kp2ds[112:133]
    return kp2ds_body.copy(), kp2ds_lhand.copy(), kp2ds_rhand.copy()


def draw_aapose_by_meta(img, meta: AAPoseMeta, threshold=0.5, stick_width_norm=200, draw_hand=True, draw_head=True):
    kp2ds = np.concatenate([meta.kps_body, meta.kps_body_p[:, None]], axis=1)
    kp2ds_lhand = np.concatenate([meta.kps_lhand, meta.kps_lhand_p[:, None]], axis=1)
    kp2ds_rhand = np.concatenate([meta.kps_rhand, meta.kps_rhand_p[:, None]], axis=1)
    pose_img = draw_aapose(img, kp2ds, threshold, kp2ds_lhand=kp2ds_lhand, kp2ds_rhand=kp2ds_rhand, stick_width_norm=stick_width_norm, draw_hand=draw_hand, draw_head=draw_head)
    return pose_img


def draw_aapose_by_meta_new(img, meta: AAPoseMeta, threshold=0.5, stickwidth_type="v2", draw_hand=True, draw_head=True):
    kp2ds = np.concatenate([meta.kps_body, meta.kps_body_p[:, None]], axis=1)
    kp2ds_lhand = np.concatenate([meta.kps_lhand, meta.kps_lhand_p[:, None]], axis=1)
    kp2ds_rhand = np.concatenate([meta.kps_rhand, meta.kps_rhand_p[:, None]], axis=1)
    pose_img = draw_aapose_new(img, kp2ds, threshold, kp2ds_lhand=kp2ds_lhand, kp2ds_rhand=kp2ds_rhand, stickwidth_type=stickwidth_type, draw_hand=draw_hand, draw_head=draw_head)
    return pose_img


def draw_hand_by_meta(img, meta: AAPoseMeta, threshold=0.5, stick_width_norm=200):
    kp2ds = np.concatenate([meta.kps_body, meta.kps_body_p[:, None] * 0], axis=1)
    kp2ds_lhand = np.concatenate([meta.kps_lhand, meta.kps_lhand_p[:, None]], axis=1)
    kp2ds_rhand = np.concatenate([meta.kps_rhand, meta.kps_rhand_p[:, None]], axis=1)
    pose_img = draw_aapose(img, kp2ds, threshold, kp2ds_lhand=kp2ds_lhand, kp2ds_rhand=kp2ds_rhand, stick_width_norm=stick_width_norm, draw_hand=True, draw_head=False)
    return pose_img


def draw_aaface_by_meta(img, meta: AAPoseMeta, threshold=0.5, stick_width_norm=200, draw_hand=False, draw_head=True):
    kp2ds = np.concatenate([meta.kps_body, meta.kps_body_p[:, None]], axis=1)
    # kp2ds_lhand = np.concatenate([meta.kps_lhand, meta.kps_lhand_p[:, None]], axis=1)
    # kp2ds_rhand = np.concatenate([meta.kps_rhand, meta.kps_rhand_p[:, None]], axis=1)
    pose_img = draw_M(img, kp2ds, threshold, kp2ds_lhand=None, kp2ds_rhand=None, stick_width_norm=stick_width_norm, draw_hand=draw_hand, draw_head=draw_head)
    return pose_img


def draw_aanose_by_meta(img, meta: AAPoseMeta, threshold=0.5, stick_width_norm=100, draw_hand=False):
    kp2ds = np.concatenate([meta.kps_body, meta.kps_body_p[:, None]], axis=1)
    # kp2ds_lhand = np.concatenate([meta.kps_lhand, meta.kps_lhand_p[:, None]], axis=1)
    # kp2ds_rhand = np.concatenate([meta.kps_rhand, meta.kps_rhand_p[:, None]], axis=1)
    pose_img = draw_nose(img, kp2ds, threshold, kp2ds_lhand=None, kp2ds_rhand=None, stick_width_norm=stick_width_norm, draw_hand=draw_hand)
    return pose_img


def gen_face_motion_seq(img, metas: List[AAPoseMeta], threshold=0.5, stick_width_norm=200):
    return


def draw_M(img, kp2ds, threshold=0.6, data_to_json=None, idx=-1, kp2ds_lhand=None, kp2ds_rhand=None, draw_hand=False, stick_width_norm=200, draw_head=True):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """

    new_kep_list = [
        "Nose",
        "Neck",
        "RShoulder",
        "RElbow",
        "RWrist",  # No.4
        "LShoulder",
        "LElbow",
        "LWrist",  # No.7
        "RHip",
        "RKnee",
        "RAnkle",  # No.10
        "LHip",
        "LKnee",
        "LAnkle",  # No.13
        "REye",
        "LEye",
        "REar",
        "LEar",
        "LToe",
        "RToe",
    ]
    # kp2ds_body = (kp2ds.copy()[[0, 6, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 17, 20]] + \
    #              kp2ds.copy()[[0, 5, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 18, 21]]) / 2
    kp2ds = kp2ds.copy()
    # import ipdb; ipdb.set_trace()
    kp2ds[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 18, 19], 2] = 0
    if not draw_head:
        kp2ds[[0, 14, 15, 16, 17], 2] = 0
    kp2ds_body = kp2ds
    # kp2ds_body = kp2ds_body[:18]

    # kp2ds_lhand = kp2ds.copy()[91:112]
    # kp2ds_rhand = kp2ds.copy()[112:133]

    limbSeq = [
        # [2, 3],
        # [2, 6],  # shoulders
        # [3, 4],
        # [4, 5],  # left arm
        # [6, 7],
        # [7, 8],  # right arm
        # [2, 9],
        # [9, 10],
        # [10, 11],  # right leg
        # [2, 12],
        # [12, 13],
        # [13, 14],  # left leg
        # [2, 1],
        [1, 15],
        [15, 17],
        [1, 16],
        [16, 18],  # face (nose, eyes, ears)
        # [14, 19],
        # [11, 20],  # foot
    ]

    colors = [
        # [255, 0, 0],
        # [255, 85, 0],
        # [255, 170, 0],
        # [255, 255, 0],
        # [170, 255, 0],
        # [85, 255, 0],
        # [0, 255, 0],
        # [0, 255, 85],
        # [0, 255, 170],
        # [0, 255, 255],
        # [0, 170, 255],
        # [0, 85, 255],
        # [0, 0, 255],
        # [85, 0, 255],
        [170, 0, 255],
        [255, 0, 255],
        [255, 0, 170],
        [255, 0, 85],
        # foot
        # [200, 200, 0],
        # [100, 100, 0],
    ]

    H, W, C = img.shape
    stickwidth = max(int(min(H, W) / stick_width_norm), 1)

    for _idx, ((k1_index, k2_index), color) in enumerate(zip(limbSeq, colors)):
        keypoint1 = kp2ds_body[k1_index - 1]
        keypoint2 = kp2ds_body[k2_index - 1]

        if keypoint1[-1] < threshold or keypoint2[-1] < threshold:
            continue

        Y = np.array([keypoint1[0], keypoint2[0]])
        X = np.array([keypoint1[1], keypoint2[1]])
        mX = np.mean(X)
        mY = np.mean(Y)
        length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
        angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
        polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
        cv2.fillConvexPoly(img, polygon, [int(float(c) * 0.6) for c in color])

    for _idx, (keypoint, color) in enumerate(zip(kp2ds_body, colors)):
        if keypoint[-1] < threshold:
            continue
        x, y = keypoint[0], keypoint[1]
        # cv2.circle(canvas, (int(x), int(y)), 4, color, thickness=-1)
        cv2.circle(img, (int(x), int(y)), stickwidth, color, thickness=-1)

    if draw_hand:
        img = draw_handpose(img, kp2ds_lhand, hand_score_th=threshold)
        img = draw_handpose(img, kp2ds_rhand, hand_score_th=threshold)

    kp2ds_body[:, 0] /= W
    kp2ds_body[:, 1] /= H

    if data_to_json is not None:
        if idx == -1:
            data_to_json.append(
                {
                    "image_id": "frame_{:05d}.jpg".format(len(data_to_json) + 1),
                    "height": H,
                    "width": W,
                    "category_id": 1,
                    "keypoints_body": kp2ds_body.tolist(),
                    "keypoints_left_hand": kp2ds_lhand.tolist(),
                    "keypoints_right_hand": kp2ds_rhand.tolist(),
                }
            )
        else:
            data_to_json[idx] = {
                "image_id": "frame_{:05d}.jpg".format(idx + 1),
                "height": H,
                "width": W,
                "category_id": 1,
                "keypoints_body": kp2ds_body.tolist(),
                "keypoints_left_hand": kp2ds_lhand.tolist(),
                "keypoints_right_hand": kp2ds_rhand.tolist(),
            }
    return img


def draw_nose(
    img,
    kp2ds,
    threshold=0.6,
    data_to_json=None,
    idx=-1,
    kp2ds_lhand=None,
    kp2ds_rhand=None,
    draw_hand=False,
    stick_width_norm=200,
):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """

    new_kep_list = [
        "Nose",
        "Neck",
        "RShoulder",
        "RElbow",
        "RWrist",  # No.4
        "LShoulder",
        "LElbow",
        "LWrist",  # No.7
        "RHip",
        "RKnee",
        "RAnkle",  # No.10
        "LHip",
        "LKnee",
        "LAnkle",  # No.13
        "REye",
        "LEye",
        "REar",
        "LEar",
        "LToe",
        "RToe",
    ]
    # kp2ds_body = (kp2ds.copy()[[0, 6, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 17, 20]] + \
    #              kp2ds.copy()[[0, 5, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 18, 21]]) / 2
    kp2ds = kp2ds.copy()
    kp2ds[1:, 2] = 0
    # kp2ds[0, 2] = 1
    kp2ds_body = kp2ds
    # kp2ds_body = kp2ds_body[:18]

    # kp2ds_lhand = kp2ds.copy()[91:112]
    # kp2ds_rhand = kp2ds.copy()[112:133]

    limbSeq = [
        # [2, 3],
        # [2, 6],  # shoulders
        # [3, 4],
        # [4, 5],  # left arm
        # [6, 7],
        # [7, 8],  # right arm
        # [2, 9],
        # [9, 10],
        # [10, 11],  # right leg
        # [2, 12],
        # [12, 13],
        # [13, 14],  # left leg
        # [2, 1],
        [1, 15],
        [15, 17],
        [1, 16],
        [16, 18],  # face (nose, eyes, ears)
        # [14, 19],
        # [11, 20],  # foot
    ]

    colors = [
        # [255, 0, 0],
        # [255, 85, 0],
        # [255, 170, 0],
        # [255, 255, 0],
        # [170, 255, 0],
        # [85, 255, 0],
        # [0, 255, 0],
        # [0, 255, 85],
        # [0, 255, 170],
        # [0, 255, 255],
        # [0, 170, 255],
        # [0, 85, 255],
        # [0, 0, 255],
        # [85, 0, 255],
        [170, 0, 255],
        # [255, 0, 255],
        # [255, 0, 170],
        # [255, 0, 85],
        # foot
        # [200, 200, 0],
        # [100, 100, 0],
    ]

    H, W, C = img.shape
    stickwidth = max(int(min(H, W) / stick_width_norm), 1)

    # for _idx, ((k1_index, k2_index), color) in enumerate(zip(limbSeq, colors)):
    #     keypoint1 = kp2ds_body[k1_index - 1]
    #     keypoint2 = kp2ds_body[k2_index - 1]

    #     if keypoint1[-1] < threshold or keypoint2[-1] < threshold:
    #         continue

    #     Y = np.array([keypoint1[0], keypoint2[0]])
    #     X = np.array([keypoint1[1], keypoint2[1]])
    #     mX = np.mean(X)
    #     mY = np.mean(Y)
    #     length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
    #     angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
    #     polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
    #     cv2.fillConvexPoly(img, polygon, [int(float(c) * 0.6) for c in color])

    for _idx, (keypoint, color) in enumerate(zip(kp2ds_body, colors)):
        if keypoint[-1] < threshold:
            continue
        x, y = keypoint[0], keypoint[1]
        # cv2.circle(canvas, (int(x), int(y)), 4, color, thickness=-1)
        cv2.circle(img, (int(x), int(y)), stickwidth, color, thickness=-1)

    if draw_hand:
        img = draw_handpose(img, kp2ds_lhand, hand_score_th=threshold)
        img = draw_handpose(img, kp2ds_rhand, hand_score_th=threshold)

    kp2ds_body[:, 0] /= W
    kp2ds_body[:, 1] /= H

    if data_to_json is not None:
        if idx == -1:
            data_to_json.append(
                {
                    "image_id": "frame_{:05d}.jpg".format(len(data_to_json) + 1),
                    "height": H,
                    "width": W,
                    "category_id": 1,
                    "keypoints_body": kp2ds_body.tolist(),
                    "keypoints_left_hand": kp2ds_lhand.tolist(),
                    "keypoints_right_hand": kp2ds_rhand.tolist(),
                }
            )
        else:
            data_to_json[idx] = {
                "image_id": "frame_{:05d}.jpg".format(idx + 1),
                "height": H,
                "width": W,
                "category_id": 1,
                "keypoints_body": kp2ds_body.tolist(),
                "keypoints_left_hand": kp2ds_lhand.tolist(),
                "keypoints_right_hand": kp2ds_rhand.tolist(),
            }
    return img


def draw_aapose(img, kp2ds, threshold=0.6, data_to_json=None, idx=-1, kp2ds_lhand=None, kp2ds_rhand=None, draw_hand=False, stick_width_norm=200, draw_head=True):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """

    new_kep_list = [
        "Nose",
        "Neck",
        "RShoulder",
        "RElbow",
        "RWrist",  # No.4
        "LShoulder",
        "LElbow",
        "LWrist",  # No.7
        "RHip",
        "RKnee",
        "RAnkle",  # No.10
        "LHip",
        "LKnee",
        "LAnkle",  # No.13
        "REye",
        "LEye",
        "REar",
        "LEar",
        "LToe",
        "RToe",
    ]
    # kp2ds_body = (kp2ds.copy()[[0, 6, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 17, 20]] + \
    #              kp2ds.copy()[[0, 5, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 18, 21]]) / 2
    kp2ds = kp2ds.copy()
    if not draw_head:
        kp2ds[[0, 14, 15, 16, 17], 2] = 0
    kp2ds_body = kp2ds

    # kp2ds_lhand = kp2ds.copy()[91:112]
    # kp2ds_rhand = kp2ds.copy()[112:133]

    limbSeq = [
        [2, 3],
        [2, 6],  # shoulders
        [3, 4],
        [4, 5],  # left arm
        [6, 7],
        [7, 8],  # right arm
        [2, 9],
        [9, 10],
        [10, 11],  # right leg
        [2, 12],
        [12, 13],
        [13, 14],  # left leg
        [2, 1],
        [1, 15],
        [15, 17],
        [1, 16],
        [16, 18],  # face (nose, eyes, ears)
        [14, 19],
        [11, 20],  # foot
    ]

    colors = [
        [255, 0, 0],
        [255, 85, 0],
        [255, 170, 0],
        [255, 255, 0],
        [170, 255, 0],
        [85, 255, 0],
        [0, 255, 0],
        [0, 255, 85],
        [0, 255, 170],
        [0, 255, 255],
        [0, 170, 255],
        [0, 85, 255],
        [0, 0, 255],
        [85, 0, 255],
        [170, 0, 255],
        [255, 0, 255],
        [255, 0, 170],
        [255, 0, 85],
        # foot
        [200, 200, 0],
        [100, 100, 0],
    ]

    H, W, C = img.shape
    stickwidth = max(int(min(H, W) / stick_width_norm), 1)

    for _idx, ((k1_index, k2_index), color) in enumerate(zip(limbSeq, colors)):
        keypoint1 = kp2ds_body[k1_index - 1]
        keypoint2 = kp2ds_body[k2_index - 1]

        if keypoint1[-1] < threshold or keypoint2[-1] < threshold:
            continue

        Y = np.array([keypoint1[0], keypoint2[0]])
        X = np.array([keypoint1[1], keypoint2[1]])
        mX = np.mean(X)
        mY = np.mean(Y)
        length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
        angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
        polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
        cv2.fillConvexPoly(img, polygon, [int(float(c) * 0.6) for c in color])

    for _idx, (keypoint, color) in enumerate(zip(kp2ds_body, colors)):
        if keypoint[-1] < threshold:
            continue
        x, y = keypoint[0], keypoint[1]
        # cv2.circle(canvas, (int(x), int(y)), 4, color, thickness=-1)
        cv2.circle(img, (int(x), int(y)), stickwidth, color, thickness=-1)

    if draw_hand:
        img = draw_handpose(img, kp2ds_lhand, hand_score_th=threshold)
        img = draw_handpose(img, kp2ds_rhand, hand_score_th=threshold)

    kp2ds_body[:, 0] /= W
    kp2ds_body[:, 1] /= H

    if data_to_json is not None:
        if idx == -1:
            data_to_json.append(
                {
                    "image_id": "frame_{:05d}.jpg".format(len(data_to_json) + 1),
                    "height": H,
                    "width": W,
                    "category_id": 1,
                    "keypoints_body": kp2ds_body.tolist(),
                    "keypoints_left_hand": kp2ds_lhand.tolist(),
                    "keypoints_right_hand": kp2ds_rhand.tolist(),
                }
            )
        else:
            data_to_json[idx] = {
                "image_id": "frame_{:05d}.jpg".format(idx + 1),
                "height": H,
                "width": W,
                "category_id": 1,
                "keypoints_body": kp2ds_body.tolist(),
                "keypoints_left_hand": kp2ds_lhand.tolist(),
                "keypoints_right_hand": kp2ds_rhand.tolist(),
            }
    return img


def draw_aapose_new(img, kp2ds, threshold=0.6, data_to_json=None, idx=-1, kp2ds_lhand=None, kp2ds_rhand=None, draw_hand=False, stickwidth_type="v2", draw_head=True):
    """
    Draw keypoints and connections representing hand pose on a given canvas.

    Args:
        canvas (np.ndarray): A 3D numpy array representing the canvas (image) on which to draw the hand pose.
        keypoints (List[Keypoint]| None): A list of Keypoint objects representing the hand keypoints to be drawn
                                          or None if no keypoints are present.

    Returns:
        np.ndarray: A 3D numpy array representing the modified canvas with the drawn hand pose.

    Note:
        The function expects the x and y coordinates of the keypoints to be normalized between 0 and 1.
    """

    new_kep_list = [
        "Nose",
        "Neck",
        "RShoulder",
        "RElbow",
        "RWrist",  # No.4
        "LShoulder",
        "LElbow",
        "LWrist",  # No.7
        "RHip",
        "RKnee",
        "RAnkle",  # No.10
        "LHip",
        "LKnee",
        "LAnkle",  # No.13
        "REye",
        "LEye",
        "REar",
        "LEar",
        "LToe",
        "RToe",
    ]
    # kp2ds_body = (kp2ds.copy()[[0, 6, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 17, 20]] + \
    #              kp2ds.copy()[[0, 5, 6, 8, 10, 5, 7, 9, 12, 14, 16, 11, 13, 15, 2, 1, 4, 3, 18, 21]]) / 2
    kp2ds = kp2ds.copy()
    if not draw_head:
        kp2ds[[0, 14, 15, 16, 17], 2] = 0
    kp2ds_body = kp2ds

    # kp2ds_lhand = kp2ds.copy()[91:112]
    # kp2ds_rhand = kp2ds.copy()[112:133]

    limbSeq = [
        [2, 3],
        [2, 6],  # shoulders
        [3, 4],
        [4, 5],  # left arm
        [6, 7],
        [7, 8],  # right arm
        [2, 9],
        [9, 10],
        [10, 11],  # right leg
        [2, 12],
        [12, 13],
        [13, 14],  # left leg
        [2, 1],
        [1, 15],
        [15, 17],
        [1, 16],
        [16, 18],  # face (nose, eyes, ears)
        [14, 19],
        [11, 20],  # foot
    ]

    colors = [
        [255, 0, 0],
        [255, 85, 0],
        [255, 170, 0],
        [255, 255, 0],
        [170, 255, 0],
        [85, 255, 0],
        [0, 255, 0],
        [0, 255, 85],
        [0, 255, 170],
        [0, 255, 255],
        [0, 170, 255],
        [0, 85, 255],
        [0, 0, 255],
        [85, 0, 255],
        [170, 0, 255],
        [255, 0, 255],
        [255, 0, 170],
        [255, 0, 85],
        # foot
        [200, 200, 0],
        [100, 100, 0],
    ]

    H, W, C = img.shape
    H, W, C = img.shape

    if stickwidth_type == "v1":
        stickwidth = max(int(min(H, W) / 200), 1)
    elif stickwidth_type == "v2":
        stickwidth = max(int(min(H, W) / 200) - 1, 1)
    else:
        raise

    for _idx, ((k1_index, k2_index), color) in enumerate(zip(limbSeq, colors)):
        keypoint1 = kp2ds_body[k1_index - 1]
        keypoint2 = kp2ds_body[k2_index - 1]

        if keypoint1[-1] < threshold or keypoint2[-1] < threshold:
            continue

        Y = np.array([keypoint1[0], keypoint2[0]])
        X = np.array([keypoint1[1], keypoint2[1]])
        mX = np.mean(X)
        mY = np.mean(Y)
        length = ((X[0] - X[1]) ** 2 + (Y[0] - Y[1]) ** 2) ** 0.5
        angle = math.degrees(math.atan2(X[0] - X[1], Y[0] - Y[1]))
        polygon = cv2.ellipse2Poly((int(mY), int(mX)), (int(length / 2), stickwidth), int(angle), 0, 360, 1)
        cv2.fillConvexPoly(img, polygon, [int(float(c) * 0.6) for c in color])

    for _idx, (keypoint, color) in enumerate(zip(kp2ds_body, colors)):
        if keypoint[-1] < threshold:
            continue
        x, y = keypoint[0], keypoint[1]
        # cv2.circle(canvas, (int(x), int(y)), 4, color, thickness=-1)
        cv2.circle(img, (int(x), int(y)), stickwidth, color, thickness=-1)

    if draw_hand:
        img = draw_handpose_new(img, kp2ds_lhand, stickwidth_type=stickwidth_type, hand_score_th=threshold)
        img = draw_handpose_new(img, kp2ds_rhand, stickwidth_type=stickwidth_type, hand_score_th=threshold)

    kp2ds_body[:, 0] /= W
    kp2ds_body[:, 1] /= H

    if data_to_json is not None:
        if idx == -1:
            data_to_json.append(
                {
                    "image_id": "frame_{:05d}.jpg".format(len(data_to_json) + 1),
                    "height": H,
                    "width": W,
                    "category_id": 1,
                    "keypoints_body": kp2ds_body.tolist(),
                    "keypoints_left_hand": kp2ds_lhand.tolist(),
                    "keypoints_right_hand": kp2ds_rhand.tolist(),
                }
            )
        else:
            data_to_json[idx] = {
                "image_id": "frame_{:05d}.jpg".format(idx + 1),
                "height": H,
                "width": W,
                "category_id": 1,
                "keypoints_body": kp2ds_body.tolist(),
                "keypoints_left_hand": kp2ds_lhand.tolist(),
                "keypoints_right_hand": kp2ds_rhand.tolist(),
            }
    return img


def draw_bbox(img, bbox, color=(255, 0, 0)):
    img = load_image(img)
    bbox = [int(bbox_tmp) for bbox_tmp in bbox]
    cv2.rectangle(img, (bbox[0], bbox[1]), (bbox[2], bbox[3]), color, 2)
    return img


def draw_kp2ds(img, kp2ds, threshold=0, color=(255, 0, 0), skeleton=None, reverse=False):
    img = load_image(img, reverse)

    if skeleton is not None:
        if skeleton == "coco17":
            skeleton_list = [
                [6, 8],
                [8, 10],
                [5, 7],
                [7, 9],
                [11, 13],
                [13, 15],
                [12, 14],
                [14, 16],
                [5, 6],
                [6, 12],
                [12, 11],
                [11, 5],
            ]
            color_list = [
                (255, 0, 0),
                (0, 255, 0),
                (0, 0, 255),
                (255, 255, 0),
                (255, 0, 255),
                (0, 255, 255),
            ]
        elif skeleton == "cocowholebody":
            skeleton_list = [
                [6, 8],
                [8, 10],
                [5, 7],
                [7, 9],
                [11, 13],
                [13, 15],
                [12, 14],
                [14, 16],
                [5, 6],
                [6, 12],
                [12, 11],
                [11, 5],
                [15, 17],
                [15, 18],
                [15, 19],
                [16, 20],
                [16, 21],
                [16, 22],
                [91, 92, 93, 94, 95],
                [91, 96, 97, 98, 99],
                [91, 100, 101, 102, 103],
                [91, 104, 105, 106, 107],
                [91, 108, 109, 110, 111],
                [112, 113, 114, 115, 116],
                [112, 117, 118, 119, 120],
                [112, 121, 122, 123, 124],
                [112, 125, 126, 127, 128],
                [112, 129, 130, 131, 132],
            ]
            color_list = [
                (255, 0, 0),
                (0, 255, 0),
                (0, 0, 255),
                (255, 255, 0),
                (255, 0, 255),
                (0, 255, 255),
            ]
        else:
            color_list = [color]
        for _idx, _skeleton in enumerate(skeleton_list):
            for i in range(len(_skeleton) - 1):
                cv2.line(
                    img,
                    (int(kp2ds[_skeleton[i], 0]), int(kp2ds[_skeleton[i], 1])),
                    (int(kp2ds[_skeleton[i + 1], 0]), int(kp2ds[_skeleton[i + 1], 1])),
                    color_list[_idx % len(color_list)],
                    3,
                )

    for _idx, kp2d in enumerate(kp2ds):
        if kp2d[2] > threshold:
            cv2.circle(img, (int(kp2d[0]), int(kp2d[1])), 3, color, -1)
            # cv2.putText(img,
            #         str(_idx),
            #         (int(kp2d[0, i, 0])*1,
            #             int(kp2d[0, i, 1])*1),
            #         cv2.FONT_HERSHEY_SIMPLEX,
            #         0.75,
            #         color,
            #         2
            #         )

    return img


def draw_mask(img, mask, background=0, return_rgba=False):
    img = load_image(img)
    h, w, _ = img.shape
    if type(background) == int:  # noqa
        background = np.ones((h, w, 3)).astype(np.uint8) * 255 * background
    backgournd = cv2.resize(background, (w, h))
    img_rgba = np.concatenate([img, mask], -1)
    return alphaMerge(img_rgba, background, 0, 0, return_rgba=True)


def draw_pcd(pcd_list, save_path=None):
    fig = plt.figure()
    ax = fig.add_subplot(111, projection="3d")

    color_list = ["r", "g", "b", "y", "p"]

    for _idx, _pcd in enumerate(pcd_list):
        ax.scatter(_pcd[:, 0], _pcd[:, 1], _pcd[:, 2], c=color_list[_idx], marker="o")

    ax.set_xlabel("X")
    ax.set_ylabel("Y")
    ax.set_zlabel("Z")

    if save_path is not None:
        plt.savefig(save_path)
    else:
        plt.savefig("tmp.png")


def load_image(img, reverse=False):
    if type(img) == str:  # noqa
        img = cv2.imread(img)
    if reverse:
        img = img.astype(np.float32)
        img = img[:, :, ::-1]
        img = img.astype(np.uint8)
    return img


def draw_skeleten(meta):
    kps = []
    for i, kp in enumerate(meta["keypoints_body"]):
        if kp is None:
            # if kp is None:
            kps.append([0, 0, 0])
        else:
            kps.append([*kp, 1])
    kps = np.array(kps)

    kps[:, 0] *= meta["width"]
    kps[:, 1] *= meta["height"]
    pose_img = np.zeros([meta["height"], meta["width"], 3], dtype=np.uint8)

    pose_img = draw_aapose(
        pose_img,
        kps,
        draw_hand=True,
        kp2ds_lhand=meta["keypoints_left_hand"],
        kp2ds_rhand=meta["keypoints_right_hand"],
    )
    return pose_img


def draw_skeleten_with_pncc(pncc: np.ndarray, meta: Dict) -> np.ndarray:
    """
    Args:
        pncc: [H,W,3]
        meta: required keys: keypoints_body: [N, 3] keypoints_left_hand, keypoints_right_hand
    Return:
        np.ndarray [H, W, 3]
    """
    # preprocess keypoints
    kps = []
    for i, kp in enumerate(meta["keypoints_body"]):
        if kp is None:
            # if kp is None:
            kps.append([0, 0, 0])
        elif i in [14, 15, 16, 17]:
            kps.append([0, 0, 0])
        else:
            kps.append([*kp])
    kps = np.stack(kps)

    kps[:, 0] *= pncc.shape[1]
    kps[:, 1] *= pncc.shape[0]

    # draw neck
    canvas = np.zeros_like(pncc)
    if kps[0][2] > 0.6 and kps[1][2] > 0.6:
        canvas = draw_ellipse_by_2kp(canvas, kps[0], kps[1], [0, 0, 255])

    # draw pncc
    mask = (pncc > 0).max(axis=2)
    canvas[mask] = pncc[mask]
    pncc = canvas

    # draw other skeleten
    kps[0] = 0

    meta["keypoints_left_hand"][:, 0] *= meta["width"]
    meta["keypoints_left_hand"][:, 1] *= meta["height"]

    meta["keypoints_right_hand"][:, 0] *= meta["width"]
    meta["keypoints_right_hand"][:, 1] *= meta["height"]
    pose_img = draw_aapose(
        pncc,
        kps,
        draw_hand=True,
        kp2ds_lhand=meta["keypoints_left_hand"],
        kp2ds_rhand=meta["keypoints_right_hand"],
    )
    return pose_img


FACE_CUSTOM_STYLE = {
    "eyeball": {"indexs": [68, 69], "color": [255, 255, 255], "connect": False},
    "left_eyebrow": {"indexs": [17, 18, 19, 20, 21], "color": [0, 255, 0]},
    "right_eyebrow": {"indexs": [22, 23, 24, 25, 26], "color": [0, 0, 255]},
    "left_eye": {"indexs": [36, 37, 38, 39, 40, 41], "color": [255, 255, 0], "close": True},
    "right_eye": {"indexs": [42, 43, 44, 45, 46, 47], "color": [255, 0, 255], "close": True},
    "mouth_outside": {"indexs": list(range(48, 60)), "color": [100, 255, 50], "close": True},
    "mouth_inside": {"indexs": [60, 61, 62, 63, 64, 65, 66, 67], "color": [255, 100, 50], "close": True},
}


def draw_face_kp(img, kps, thickness=2, style=FACE_CUSTOM_STYLE):
    """
    Args:
        img: [H, W, 3]
        kps: [70, 2]
    """
    img = img.copy()
    for key, item in style.items():
        pts = np.array(kps[item["indexs"]]).astype(np.int32)
        connect = item.get("connect", True)
        color = item["color"]
        close = item.get("close", False)
        if connect:
            cv2.polylines(img, [pts], close, color, thickness=thickness)
        else:
            for kp in pts:
                kp = np.array(kp).astype(np.int32)
                cv2.circle(img, kp, thickness * 2, color=color, thickness=-1)
    return img


def draw_traj(metas: List[AAPoseMeta], threshold=0.6):
    colors = [
        [255, 0, 0],
        [255, 85, 0],
        [255, 170, 0],
        [255, 255, 0],
        [170, 255, 0],
        [85, 255, 0],
        [0, 255, 0],
        [0, 255, 85],
        [0, 255, 170],
        [0, 255, 255],
        [0, 170, 255],
        [0, 85, 255],
        [0, 0, 255],
        [85, 0, 255],
        [170, 0, 255],
        [255, 0, 255],
        [255, 0, 170],
        [255, 0, 85],
        [100, 255, 50],
        [255, 100, 50],
        # foot
        [200, 200, 0],
        [100, 100, 0],
    ]
    limbSeq = [
        [1, 2],
        [1, 5],  # shoulders
        [2, 3],
        [3, 4],  # left arm
        [5, 6],
        [6, 7],  # right arm
        [1, 8],
        [8, 9],
        [9, 10],  # right leg
        [1, 11],
        [11, 12],
        [12, 13],  # left leg
        # face (nose, eyes, ears)
        [13, 18],
        [10, 19],  # foot
    ]

    face_seq = [[1, 0], [0, 14], [14, 16], [0, 15], [15, 17]]
    kp_body = np.array([meta.kps_body for meta in metas])
    kp_body_p = np.array([meta.kps_body_p for meta in metas])

    face_seq = random.sample(face_seq, 2)

    kp_lh = np.array([meta.kps_lhand for meta in metas])
    kp_rh = np.array([meta.kps_rhand for meta in metas])

    kp_lh_p = np.array([meta.kps_lhand_p for meta in metas])
    kp_rh_p = np.array([meta.kps_rhand_p for meta in metas])

    # kp_lh = np.concatenate([kp_lh, kp_lh_p], axis=-1)
    # kp_rh = np.concatenate([kp_rh, kp_rh_p], axis=-1)

    new_limbSeq = []
    key_point_list = []
    for _idx, ((k1_index, k2_index)) in enumerate(limbSeq):
        vis = (kp_body_p[:, k1_index] > threshold) * (kp_body_p[:, k2_index] > threshold) * 1
        if vis.sum() * 1.0 / vis.shape[0] > 0.4:
            new_limbSeq.append([k1_index, k2_index])

    for _idx, ((k1_index, k2_index)) in enumerate(limbSeq):
        keypoint1 = kp_body[:, k1_index - 1]
        keypoint2 = kp_body[:, k2_index - 1]
        interleave = random.randint(4, 7)
        randind = random.randint(0, interleave - 1)
        # randind = random.rand(range(interleave), sampling_num)

        Y = np.array([keypoint1[:, 0], keypoint2[:, 0]])
        X = np.array([keypoint1[:, 1], keypoint2[:, 1]])

        vis = (keypoint1[:, -1] > threshold) * (keypoint2[:, -1] > threshold) * 1

        # for randidx in randind:
        t = randind / interleave
        x = (1 - t) * Y[0, :] + t * Y[1, :]
        y = (1 - t) * X[0, :] + t * X[1, :]

        # np.array([1])
        x = x.astype(int)
        y = y.astype(int)

        new_array = np.array([x, y, vis]).T

        key_point_list.append(new_array)

    indx_lh = random.randint(0, kp_lh.shape[1] - 1)
    lh = kp_lh[:, indx_lh, :]
    lh_p = kp_lh_p[:, indx_lh : indx_lh + 1]
    lh = np.concatenate([lh, lh_p], axis=-1)

    indx_rh = random.randint(0, kp_rh.shape[1] - 1)
    rh = kp_rh[:, random.randint(0, kp_rh.shape[1] - 1), :]
    rh_p = kp_rh_p[:, indx_rh : indx_rh + 1]
    rh = np.concatenate([rh, rh_p], axis=-1)

    lh[-1, :] = (lh[-1, :] > threshold) * 1
    rh[-1, :] = (rh[-1, :] > threshold) * 1

    # print(rh.shape, new_array.shape)
    # exit()
    key_point_list.append(lh.astype(int))
    key_point_list.append(rh.astype(int))

    key_points_list = np.stack(key_point_list)
    num_points = len(key_points_list)
    sample_colors = random.sample(colors, num_points)

    stickwidth = max(int(min(metas[0].width, metas[0].height) / 150), 2)

    image_list_ori = []
    for i in range(key_points_list.shape[-2]):
        _image_vis = np.zeros((metas[0].width, metas[0].height, 3))
        points = key_points_list[:, i, :]
        for idx, point in enumerate(points):
            x, y, vis = point
            if vis == 1:
                cv2.circle(_image_vis, (x, y), stickwidth, sample_colors[idx], thickness=-1)

        image_list_ori.append(_image_vis)

    return image_list_ori

    return [np.zeros([meta.width, meta.height, 3], dtype=np.uint8) for meta in metas]


if __name__ == "__main__":
    meta = {
        "image_id": "00472.jpg",
        "height": 540,
        "width": 414,
        "category_id": 1,
        "keypoints_body": [
            [0.5084776947463768, 0.11350188078703703],
            [0.504467655495169, 0.20419560185185184],
            [0.3982016153381642, 0.198046875],
            [0.3841664779589372, 0.34869068287037036],
            [0.3901815368357488, 0.4670536747685185],
            [0.610733695652174, 0.2103443287037037],
            [0.6167487545289855, 0.3517650462962963],
            [0.6448190292874396, 0.4762767650462963],
            [0.4523371452294686, 0.47320240162037036],
            [0.4503321256038647, 0.6776475694444445],
            [0.47639738073671495, 0.8544234664351852],
            [0.5766483620169082, 0.47320240162037036],
            [0.5666232638888888, 0.6761103877314815],
            [0.534542949879227, 0.863646556712963],
            [0.4864224788647343, 0.09505570023148148],
            [0.5285278910024155, 0.09351851851851851],
            [0.46236224335748793, 0.10581597222222222],
            [0.5586031853864735, 0.10274160879629629],
            [0.4994551064311594, 0.9405056423611111],
            [0.4152442821557971, 0.9312825520833333],
        ],
        "keypoints_left_hand": [
            [267.78515625, 263.830078125, 1.2840936183929443],
            [265.294921875, 269.640625, 1.2546794414520264],
            [263.634765625, 277.111328125, 1.2863062620162964],
            [262.8046875, 285.412109375, 1.267038345336914],
            [261.14453125, 292.8828125, 1.280144453048706],
            [273.595703125, 281.26171875, 1.2592815160751343],
            [271.10546875, 291.22265625, 1.3256099224090576],
            [265.294921875, 294.54296875, 1.2368024587631226],
            [261.14453125, 294.54296875, 0.9771889448165894],
            [274.42578125, 282.091796875, 1.250044584274292],
            [269.4453125, 291.22265625, 1.2571144104003906],
            [264.46484375, 292.8828125, 1.177802324295044],
            [260.314453125, 292.052734375, 0.9283463358879089],
            [273.595703125, 282.091796875, 1.1834490299224854],
            [269.4453125, 290.392578125, 1.188171625137329],
            [265.294921875, 290.392578125, 1.192609429359436],
            [261.974609375, 289.5625, 0.9366656541824341],
            [271.935546875, 281.26171875, 1.0946396589279175],
            [268.615234375, 287.072265625, 0.9906131029129028],
            [265.294921875, 287.90234375, 1.0219476222991943],
            [262.8046875, 287.072265625, 0.9240120053291321],
        ],
        "keypoints_right_hand": [
            [161.53515625, 258.849609375, 1.2069408893585205],
            [168.17578125, 263.0, 1.1846840381622314],
            [173.986328125, 269.640625, 1.1435924768447876],
            [173.986328125, 277.94140625, 1.1802611351013184],
            [173.986328125, 286.2421875, 1.2599592208862305],
            [165.685546875, 275.451171875, 1.0633569955825806],
            [167.345703125, 286.2421875, 1.1693341732025146],
            [169.8359375, 291.22265625, 1.2698509693145752],
            [170.666015625, 294.54296875, 1.0619274377822876],
            [160.705078125, 276.28125, 1.0995020866394043],
            [163.1953125, 287.90234375, 1.2735884189605713],
            [166.515625, 291.22265625, 1.339503526687622],
            [169.005859375, 294.54296875, 1.0835273265838623],
            [157.384765625, 277.111328125, 1.0866981744766235],
            [161.53515625, 287.072265625, 1.2468621730804443],
            [164.025390625, 289.5625, 1.2817761898040771],
            [166.515625, 292.052734375, 1.099466323852539],
            [155.724609375, 277.111328125, 1.1065717935562134],
            [159.044921875, 285.412109375, 1.1924479007720947],
            [160.705078125, 287.072265625, 1.1304771900177002],
            [162.365234375, 287.90234375, 1.0040509700775146],
        ],
    }
    demo_meta = AAPoseMeta(meta)
    res = draw_traj([demo_meta] * 5)
    cv2.imwrite("traj.png", res[0][..., ::-1])
