DoniaHub
DoniaHub
python_hideman/invisibility_cloak.py
Download ZIP
"""
Invisibility Cloak
Powered by doniahub
Link: doniahub.cc
"""

import cv2
import mediapipe as mp
import numpy as np
import time


def calculate_distance(point1, point2):
    return np.hypot(point1.x - point2.x, point1.y - point2.y)


def generate_empty_background(frame, raw_seg_mask):
    h, w, _ = frame.shape
    if raw_seg_mask is None:
        return frame.copy()

    person_mask = (raw_seg_mask > 0.10).astype(np.uint8) * 255

    if np.sum(person_mask > 0) < 1000:
        return frame.copy()

    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (35, 35))
    dilated_person = cv2.dilate(person_mask, kernel, iterations=2)

    scale = 0.5
    small_frame = cv2.resize(frame, (int(w * scale), int(h * scale)))
    small_mask = cv2.resize(dilated_person, (int(w * scale), int(h * scale)))
    small_inpaint = cv2.inpaint(small_frame, small_mask, inpaintRadius=5, flags=cv2.INPAINT_TELEA)
    inpainted_bg = cv2.resize(small_inpaint, (w, h))

    clean_bg = frame.copy()
    clean_bg[dilated_person == 255] = inpainted_bg[dilated_person == 255]
    print("[INFO] doniahub.cc - Generated AI-erased background plate")
    return clean_bg


def draw_hud(frame, invisible_mode, background_frame, gesture_detected, fps):
    h, w, _ = frame.shape

    overlay = frame.copy()
    cv2.rectangle(overlay, (10, 10), (410, 160), (20, 20, 20), -1)
    cv2.addWeighted(overlay, 0.75, frame, 0.25, 0, frame)

    status_text = "CLOAK: ACTIVE [INVISIBLE]" if invisible_mode else "CLOAK: INACTIVE [NORMAL]"
    status_color = (0, 255, 0) if invisible_mode else (0, 0, 255)
    cv2.putText(frame, status_text, (25, 42), cv2.FONT_HERSHEY_SIMPLEX, 0.7, status_color, 2)

    bg_captured = background_frame is not None
    bg_text = "Background: READY" if bg_captured else "Background: INITIALIZING..."
    bg_color = (0, 220, 0) if bg_captured else (0, 140, 255)
    cv2.putText(frame, bg_text, (25, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.50, bg_color, 1)

    gesture_text = "Gesture: PINCH (CLOAK TOGGLED)" if gesture_detected else "Gesture: Open Hand (Ready)"
    cv2.putText(frame, gesture_text, (25, 105), cv2.FONT_HERSHEY_SIMPLEX, 0.52, (255, 255, 255), 1)

    cv2.putText(frame, f"FPS: {int(fps)} | doniahub.cc", (25, 135), cv2.FONT_HERSHEY_SIMPLEX, 0.50, (200, 200, 200), 1)

    if background_frame is not None:
        thumb_w, thumb_h = 160, 90
        thumb_x = w - thumb_w - 20
        thumb_y = 15
        thumb_resized = cv2.resize(background_frame, (thumb_w, thumb_h))
        frame[thumb_y:thumb_y + thumb_h, thumb_x:thumb_x + thumb_w] = thumb_resized
        cv2.rectangle(frame, (thumb_x, thumb_y), (thumb_x + thumb_w, thumb_y + thumb_h), (0, 255, 0), 2)
        cv2.putText(frame, "doniahub.cc", (thumb_x + 10, thumb_y + thumb_h + 18),
                    cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 255, 0), 1)

    cv2.rectangle(overlay, (0, h - 35), (w, h), (15, 15, 15), -1)
    cv2.addWeighted(overlay, 0.8, frame, 0.2, 0, frame)
    cv2.putText(frame, "doniahub.cc | Pinch: Toggle Invisibility | 'b': Re-Erase BG | 'c': Capture Clean Room | 'q': Quit",
                (20, h - 12), cv2.FONT_HERSHEY_SIMPLEX, 0.50, (0, 255, 255), 1, cv2.LINE_AA)


def build_perfect_cloak_mask(frame, raw_seg_mask, hand_landmarks_list):
    h, w, _ = frame.shape

    seg_mask = (raw_seg_mask > 0.12).astype(np.uint8) * 255

    hand_erase_mask = np.zeros((h, w), dtype=np.uint8)
    if hand_landmarks_list:
        for hand_landmarks in hand_landmarks_list:
            pts = []
            for lm in hand_landmarks.landmark:
                cx, cy = int(lm.x * w), int(lm.y * h)
                pts.append([cx, cy])
                cv2.circle(hand_erase_mask, (cx, cy), 42, 255, -1)
            if len(pts) > 0:
                hull = cv2.convexHull(np.array(pts, dtype=np.int32))
                cv2.fillConvexPoly(hand_erase_mask, hull, 255)

    combined_mask = cv2.bitwise_or(seg_mask, hand_erase_mask)

    close_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
    closed_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_CLOSE, close_kernel)

    dilate_kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (25, 25))
    dilated_mask = cv2.dilate(closed_mask, dilate_kernel, iterations=2)

    return dilated_mask


def main():
    cap = cv2.VideoCapture(0)
    if not cap.isOpened():
        print("[ERROR] Could not open webcam.")
        return

    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

    mp_hands = mp.solutions.hands
    mp_drawing = mp.solutions.drawing_utils
    mp_selfie_segmentation = mp.solutions.selfie_segmentation

    hands = mp_hands.Hands(
        static_image_mode=False,
        max_num_hands=2,
        min_detection_confidence=0.6,
        min_tracking_confidence=0.6
    )

    segmenter = mp_selfie_segmentation.SelfieSegmentation(model_selection=1)

    landmark_style = mp_drawing.DrawingSpec(color=(0, 255, 128), thickness=2, circle_radius=4)
    connection_style = mp_drawing.DrawingSpec(color=(255, 0, 200), thickness=3)

    background_frame = None
    invisible_mode = False
    was_pinching = False
    last_toggle_time = 0.0
    debounce_delay = 0.65

    prev_time = time.time()
    fps = 0.0

    print("[INFO] doniahub.cc Application running.")

    try:
        while cap.isOpened():
            ret, frame = cap.read()
            if not ret:
                break

            frame = cv2.flip(frame, 1)
            h, w, _ = frame.shape

            curr_time = time.time()
            fps = 1.0 / (curr_time - prev_time) if (curr_time - prev_time) > 0 else 0
            prev_time = curr_time

            rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

            seg_results = segmenter.process(rgb_frame)
            hand_results = hands.process(rgb_frame)

            if background_frame is None and seg_results.segmentation_mask is not None:
                background_frame = generate_empty_background(frame, seg_results.segmentation_mask)

            gesture_active_this_frame = False
            pinch_center_px = None

            if hand_results.multi_hand_landmarks:
                for hand_landmarks in hand_results.multi_hand_landmarks:
                    thumb_tip = hand_landmarks.landmark[mp_hands.HandLandmark.THUMB_TIP]
                    index_tip = hand_landmarks.landmark[mp_hands.HandLandmark.INDEX_FINGER_TIP]

                    dist = calculate_distance(thumb_tip, index_tip)

                    if dist < 0.055:
                        gesture_active_this_frame = True
                        pinch_center_px = (
                            int((thumb_tip.x + index_tip.x) * 0.5 * w),
                            int((thumb_tip.y + index_tip.y) * 0.5 * h)
                        )
                        break

            if gesture_active_this_frame and not was_pinching:
                if (curr_time - last_toggle_time) > debounce_delay:
                    if background_frame is not None:
                        invisible_mode = not invisible_mode
                    last_toggle_time = curr_time
            was_pinching = gesture_active_this_frame

            if invisible_mode and background_frame is not None and seg_results.segmentation_mask is not None:
                if background_frame.shape != frame.shape:
                    background_frame = cv2.resize(background_frame, (w, h))

                cloak_mask = build_perfect_cloak_mask(
                    frame=frame,
                    raw_seg_mask=seg_results.segmentation_mask,
                    hand_landmarks_list=hand_results.multi_hand_landmarks
                )

                output_frame = frame.copy()
                mask_bool = (cloak_mask == 255)
                output_frame[mask_bool] = background_frame[mask_bool]
            else:
                output_frame = frame.copy()

            if hand_results.multi_hand_landmarks:
                for hand_landmarks in hand_results.multi_hand_landmarks:
                    mp_drawing.draw_landmarks(
                        output_frame,
                        hand_landmarks,
                        mp_hands.HAND_CONNECTIONS,
                        landmark_drawing_spec=landmark_style,
                        connection_drawing_spec=connection_style
                    )

            if pinch_center_px is not None:
                cv2.circle(output_frame, pinch_center_px, 18, (0, 255, 255), 2)
                cv2.circle(output_frame, pinch_center_px, 6, (0, 0, 255), -1)

            draw_hud(
                output_frame,
                invisible_mode=invisible_mode,
                background_frame=background_frame,
                gesture_detected=gesture_active_this_frame,
                fps=fps
            )

            cv2.imshow("doniahub.cc - Invisibility Cloak", output_frame)

            key = cv2.waitKey(1) & 0xFF
            if key == ord('q') or key == 27:
                break
            elif key == ord('t'):
                invisible_mode = not invisible_mode
            elif key == ord('b') or key == ord('r'):
                if seg_results.segmentation_mask is not None:
                    background_frame = generate_empty_background(frame, seg_results.segmentation_mask)
            elif key == ord('c'):
                background_frame = frame.copy()

    finally:
        cap.release()
        cv2.destroyAllWindows()
        hands.close()
        segmenter.close()


if __name__ == "__main__":
    main()