CoolFace
Apppublic

shivanis14/SeniorSafetyMonitoringSystem

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
orb_motion_detection.py310 linesDownload Raw Back to root
1import cv2, os, time, math2import numpy as np3from skimage.metrics import structural_similarity as ssim4import matplotlib.pyplot as plt5 6def compute_optical_flow(prev_gray, curr_gray):7    flow = cv2.calcOpticalFlowFarneback(prev_gray, curr_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)8    magnitude, _ = cv2.cartToPolar(flow[..., 0], flow[..., 1])9    #print(f"DEBUG : max and min values are {np.max(magnitude)} {np.min(magnitude)}")10    return np.max(magnitude)11 12def compute_orb_distance(prev_frame, curr_frame, match_threshold = 40):13    # Initialize ORB detector14    orb = cv2.ORB_create()15 16    # Find the keypoints and descriptors with ORB17    kp1, des1 = orb.detectAndCompute(prev_frame, None)18    kp2, des2 = orb.detectAndCompute(curr_frame, None)19 20    # Create BFMatcher object21    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)22 23    # Match descriptors24    orig_matches = bf.match(des1, des2)25 26    matches = [match for match in orig_matches if match.distance < match_threshold]27 28    # Sort them in the order of their distance (descriptor similarity)29    matches = sorted(matches, key=lambda x: x.distance)30 31    # Calculate average descriptor distance of top 10% matches32    num_matches = len(matches)  # Use 10% of matches33    if num_matches == 0:34        return 035 36    max_descriptor_distance = max(match.distance for match in matches[:num_matches])37 38    # Calculate Euclidean distances (physical movement) for top matches39    euclidean_distances = []40    for match in matches[:num_matches]:41        # Get keypoint coordinates from both frames42        pt1 = np.array(kp1[match.queryIdx].pt)  # Coordinates in prev_frame43        pt2 = np.array(kp2[match.trainIdx].pt)  # Coordinates in curr_frame44 45        # Compute Euclidean distance between matched keypoints46        euclidean_distance = np.sqrt((pt1[0] - pt2[0])**2 + (pt1[1] - pt2[1])**2)47        #print(f"DEBUG!! euclidean_distance is {euclidean_distance} between {pt1} and {pt2}")48        euclidean_distances.append(euclidean_distance)49 50    # Average Euclidean distance (keypoint movement)51    max_movement_distance = np.max(euclidean_distances)52 53    # Normalize max descriptor distance (for 256-bit ORB descriptors)54    normalized_descriptor_distance = max_descriptor_distance / 25655 56    # Return both descriptor similarity and keypoint movement57    #print(f"DEBUG!! max_descriptor_distance : {max_descriptor_distance}")58    return max_movement_distance59 60 61def compute_ssim(prev_frame, curr_frame):62    return ssim(prev_frame, curr_frame, data_range=255)63 64def compute_pixel_diff(prev_frame, curr_frame):65    diff = cv2.absdiff(prev_frame, curr_frame)66    return np.mean(diff)67 68def preprocess_frame(frame, width=640, height=360):69    target_size = (width, height)70    resized_frame = cv2.resize(frame, target_size, interpolation=cv2.INTER_AREA)  # Use INTER_AREA for shrinking71    return resized_frame72 73def smooth_curve(data, window_size=5):74    return np.convolve(data, np.ones(window_size)/window_size, mode='valid')75 76def find_timestamp_clusters(fast_motion_timestamps, min_time_gap=5):77    clusters = []  # List to hold the clusters of timestamps78    current_cluster = []  # Temporary list to hold the current cluster79 80    for i, timestamp in enumerate(fast_motion_timestamps):81        # If it's the first timestamp, start a new cluster82        if i == 0:83            current_cluster.append(timestamp)84        else:85            # Check the time difference between the current and previous timestamp86            if timestamp - fast_motion_timestamps[i-1] <= min_time_gap:87                # If the difference is less than or equal to the min_time_gap, add it to the current cluster88                current_cluster.append(timestamp)89            else:90                # If the difference is greater than min_time_gap, finish the current cluster and start a new one91                clusters.append(current_cluster)92                current_cluster = [timestamp]93    94    # Add the last cluster to the clusters list95    if current_cluster:96        clusters.append(current_cluster)97    98    return clusters99 100 101def detect_fast_motion(video_path, output_dir, end_time, start_time, window_size=3, motion_threshold=0.6, step = 2):102    cap = cv2.VideoCapture(video_path)103    fps = cap.get(cv2.CAP_PROP_FPS)104    height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)105    width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)106 107    orb_scores = []108    #optical_flow_scores = []109    ssim_scores = []110    #pixel_diff_scores = []111    timestamps = []112    frame_list = []113    114    prev_frame = None115    frame_count = 0116 117    while cap.isOpened():118        ret, orig_frame = cap.read()119        if not ret:120            break121        #print(f"DEBUG!! frame : {frame_count} time : {frame_count/fps}")122 123        if height == 360 and width == 640:124            frame = orig_frame125        else:126            frame = preprocess_frame(orig_frame, width = 640, height = 360)127 128 129        if frame_count > end_time * fps:130            break131 132        if frame_count < start_time * fps or frame_count % step != 0:133            frame_count += 1134            continue135 136        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)137        138        if prev_frame is not None:139            #optical_flow_scores.append(compute_optical_flow(prev_frame, gray))140            orb_scores.append(compute_orb_distance(prev_frame, gray))141            ssim_scores.append(compute_ssim(prev_frame, gray))142            #pixel_diff_scores.append(compute_pixel_diff(prev_frame, gray))143            #print(f"DEBUG : time : {frame_count/fps} end_time : {end_time} start_time : {start_time}")144            timestamps.append(frame_count/fps)145        else:146            #optical_flow_scores.append(0)147            orb_scores.append(0)148            ssim_scores.append(1)149            timestamps.append(start_time)150            151        frame_list.append(frame)152        prev_frame = gray153        frame_count += 1154        155        #if frame_count % 100 == 0:156        #    print(f"Processed {frame_count} frames")157    158    cap.release()159    160    new_fps = len(timestamps)/ (max(timestamps) - min(timestamps))161    print(f"fps : {fps} frame_height : {height} frame_width : {width} New fps is {new_fps}")162    # Normalize scores by image diagonal * time between frame : https://chatgpt.com/share/66f684b9-dd4c-8010-bf9c-421c3c6ef84a163 164    #optical_flow_scores = np.array(optical_flow_scores) / (np.sqrt(gray.shape[0]**2 + gray.shape[1]**2) / new_fps)165    ssim_scores = (1 - np.array(ssim_scores)) * new_fps   # Invert SSIM scores166    orb_scores = (np.array(orb_scores) * new_fps)/(np.sqrt(640**2 + 360**2))167 168    # Smooth both SSIM and ORB scores169    smoothed_ssim_scores = smooth_curve(ssim_scores, window_size=window_size)170    smoothed_orb_scores = smooth_curve(orb_scores, window_size=window_size)171 172    #pixel_diff_scores = np.array(pixel_diff_scores) / np.max(pixel_diff_scores)173    174    # Combine metrics175    combined_scores = (0.3 * orb_scores) + (0.7 * ssim_scores)176    smoothed_combined_scores = (0.3 * smoothed_orb_scores) + (0.7 * smoothed_ssim_scores)177 178    # Adjust X-axis to reflect the center of the window used for smoothing179    adjusted_timestamps = timestamps[window_size // 2 : -(window_size // 2)]180 181    # Detect fast motion using sliding window182    fast_motion_timestamps = []183    fast_motion_frames = []184    fast_motion_mags = []185 186    #for i in range(len(combined_scores) - window_size + 1):187    #    window = combined_scores[i:i + window_size]188    #    if np.mean(window) > motion_threshold:189    #        #print(f"DEBUG!! mean : {np.mean(window)} i : {i + (start_time * fps)} i+window_size : {i+window_size + (start_time * fps)} window : {window}")190    #        #fast_motion_frames.extend(range(i + int(start_time * fps), i + window_size + int(start_time * fps)))191    #        fast_motion_mags.extend(combined_scores[i:i + window_size])192    #        fast_motion_timestamps.extend(timestamps[i:i + window_size])193 194    ids = []195    for i in range(len(combined_scores)):196        if combined_scores[i] > motion_threshold:197            fast_motion_mags.append(combined_scores[i])198            fast_motion_timestamps.append(timestamps[i])199            fast_motion_frames.append(frame_list[i])200            ids.append(i)201 202    padded_fast_motion_frames = []203    padded_fast_motion_timestamps = []204    205    if len(ids) < 5 and len(ids) > 0:206        #Padding fast_motion_frames and fast_motion_timestamps207        padded_fast_motion_frames.extend(frame_list[min(ids) - 2:min(ids)])208        padded_fast_motion_timestamps.extend(timestamps[min(ids) - 2:min(ids)])209        210        padded_fast_motion_frames.extend(fast_motion_frames)211        padded_fast_motion_timestamps.extend(fast_motion_timestamps)212        213        padded_fast_motion_frames.extend(frame_list[max(ids) + 1:max(ids) + 3])214        padded_fast_motion_timestamps.extend(timestamps[max(ids) + 1:max(ids) + 3])215        print(f"padded_fast_motion_timestamps are {padded_fast_motion_timestamps}. Length of padded_fast_motion_timestamps is {len(padded_fast_motion_frames)}")216    else:217        padded_fast_motion_frames = fast_motion_frames218        padded_fast_motion_timestamps = fast_motion_timestamps219 220    # Plot results221    plt.figure(figsize=(12, 6))222    plt.plot(adjusted_timestamps, smoothed_orb_scores, label='ORB Distance')223    plt.plot(adjusted_timestamps, smoothed_ssim_scores, label='Inverted SSIM')224    #plt.plot(adjusted_timestamps, optical_flow_scores, label='Optical Flow')225    plt.plot(adjusted_timestamps, smoothed_combined_scores, label='Combined Score')226    plt.axhline(y=motion_threshold, color='r', linestyle='--', label='Threshold')227    plt.xlabel('Frame')228    plt.ylabel('Normalized Score')229    plt.title('Motion Detection Metrics')230    plt.legend()231    plt.savefig(f"{output_dir}/motion_detection_plot_smoothened_{video_path.split('/')[-1].split('.')[0]}.png")232 233        # Plot results234    plt.figure(figsize=(12, 6))235    #plt.plot(timestamps, orb_scores, label='ORB Distance')236    plt.plot(timestamps, ssim_scores, label='Inverted SSIM')237    #plt.plot(timestamps, optical_flow_scores, label='Optical Flow')238    plt.plot(timestamps, combined_scores, label='Combined Score')239    plt.axhline(y=motion_threshold, color='r', linestyle='--', label='Threshold')240    plt.xlabel('Frame')241    plt.ylabel('Normalized Score')242    plt.title('Motion Detection Metrics')243    plt.legend()244    plt.savefig(f"{output_dir}/motion_detection_plot_raw_{video_path.split('/')[-1].split('.')[0]}.png")245 246    247    # Print results248    print(f"Max motion score is {np.max(combined_scores)} and mean motion score is {np.mean(combined_scores)} from {np.min(timestamps)} to {np.max(timestamps)}")249    print(f"Detected {len(fast_motion_timestamps)} frames when step = {step}.")250    try:251        print(f"fast motion between {np.min(fast_motion_timestamps)} and {np.max(fast_motion_timestamps)}")252    except:253        pass254 255    #for i in range(len(fast_motion_timestamps)):256    #    timestamp = fast_motion_timestamps[i]257    #    mag = fast_motion_mags[i]258    #    print(f"(Time: {timestamp:.2f}s) (Magnitude : {mag:.2f})")259    260    if len(fast_motion_timestamps) == 0:261        print("FAST MOTION NOT DETECTED!")262        return [], []263    elif len(fast_motion_timestamps) > 0.5 * len(combined_scores):264        print("More than half of the video has fast motion")265        return fast_motion_timestamps, padded_fast_motion_frames266    else:267        timestamp_clusters = find_timestamp_clusters(fast_motion_timestamps, min_time_gap = 5)268        for timestamp_cluster in timestamp_clusters:269            print(f"min time : {np.min(timestamp_cluster)} max time : {np.max(timestamp_cluster)} length : {len(timestamp_cluster)}")270        return timestamp_clusters, padded_fast_motion_frames271 272 273'''274# Open the video file275video_path = "../test_videos/"276mp4_files = [f for f in os.listdir(video_path) if f.endswith('.mp4')]277output_dir = "motion_detection_results"278os.system(f"rm -rf {output_dir}")279os.system(f"mkdir {output_dir}")280end_time = 15281start_time = 0282 283for mp4_file in mp4_files:284    print(f"\nAnalyzing video {mp4_file}")285 286    if mp4_file == "8.mp4":287        end_time = 60288        start_time = 0289    elif mp4_file == "6.mp4":290        end_time = 32291        start_time = 0292    elif mp4_file == "3.mp4":293        end_time = 6.5 #To remove last few frames that are blurry294        start_time = 0295    elif mp4_file == "2.mp4":296        end_time = 182297        start_time = 140298    else:299        end_time = 15300        start_time = 0301 302    #if mp4_file != "3.mp4" and mp4_file != "5.mp4" and mp4_file != "6.mp4":303    #    continue304 305    start = time.time()306    fast_motion_timestamps = detect_fast_motion(video_path + mp4_file, output_dir, end_time, start_time, motion_threshold = 1.5)307    end = time.time()308 309    print(f"Execution time for {mp4_file} : {end - start} seconds. Duration of the video was {end_time - start_time} seconds")310'''