; your code goes here
# This script adds a trailing light effect ( "đèn vèo đít" ) to a tracked object in a video using OpenCV and MoviePy.
from moviepy. editor import VideoFileClip, CompositeVideoClip
import cv2
import numpy as np
from moviepy. video. VideoClip import VideoClip
# Load video
video_path = "/mnt/data/snaptik.vn_b2961.mp4"
clip = VideoFileClip( video_path)
# Use a simple tracker based on background subtraction ( for demonstration)
cap = cv2. VideoCapture( video_path)
fps = cap. get ( cv2. CAP_PROP_FPS)
width = int( cap. get ( cv2. CAP_PROP_FRAME_WIDTH) )
height = int( cap. get ( cv2. CAP_PROP_FRAME_HEIGHT) )
# Background subtractor
fgbg = cv2. createBackgroundSubtractorMOG2( )
positions = [ ]
# Track object positions
while True:
ret , frame = cap. read( )
if not ret:
break
fgmask = fgbg. apply ( frame)
contours, _ = cv2. findContours( fgmask, cv2. RETR_EXTERNAL, cv2. CHAIN_APPROX_SIMPLE)
if contours:
largest = max ( contours, key= cv2. contourArea)
M = cv2. moments( largest)
if M[ "m00" ] != 0 :
cx = int( M[ "m10" ] / M[ "m00" ] )
cy = int( M[ "m01" ] / M[ "m00" ] )
positions. append ( ( cx, cy) )
else:
positions . append ( ( 0 , 0 ) )
else:
positions . append ( ( 0 , 0 ) )
cap. release( )
# Generate effect per frame
def make_effect_frame( t) :
frame = np. zeros( ( height, width, 4 ) , dtype= np. uint8)
index = int( t * fps)
tail_length = 10
for i in range( tail_length) :
idx = index - i
if 0 <= idx < len( positions) :
x , y = positions[ idx]
alpha = int( 200 * ( 1 - i / tail_length) )
cv2. circle( frame, ( x, y) , 20 , ( 255 , 255 , 0 , alpha) , -1 )
return frame
# Create and overlay effect clip
effect = VideoClip( make_effect_frame, duration= clip. duration) . set_duration( clip. duration)
final = CompositeVideoClip( [ clip, effect] )
# Output video
final. write_videofile( "/mnt/data/output_with_tail_light.mp4" , codec= "libx264" , audio_codec= "aac" )
( exit)
; empty line at the end
OyB5b3VyIGNvZGUgZ29lcyBoZXJlCgojIFRoaXMgc2NyaXB0IGFkZHMgYSB0cmFpbGluZyBsaWdodCBlZmZlY3QgKCLEkcOobiB2w6hvIMSRw610IikgdG8gYSB0cmFja2VkIG9iamVjdCBpbiBhIHZpZGVvIHVzaW5nIE9wZW5DViBhbmQgTW92aWVQeS4KCmZyb20gbW92aWVweS5lZGl0b3IgaW1wb3J0IFZpZGVvRmlsZUNsaXAsIENvbXBvc2l0ZVZpZGVvQ2xpcAppbXBvcnQgY3YyCmltcG9ydCBudW1weSBhcyBucApmcm9tIG1vdmllcHkudmlkZW8uVmlkZW9DbGlwIGltcG9ydCBWaWRlb0NsaXAKCiMgTG9hZCB2aWRlbwp2aWRlb19wYXRoID0gIi9tbnQvZGF0YS9zbmFwdGlrLnZuX2IyOTYxLm1wNCIKY2xpcCA9IFZpZGVvRmlsZUNsaXAodmlkZW9fcGF0aCkKCiMgVXNlIGEgc2ltcGxlIHRyYWNrZXIgYmFzZWQgb24gYmFja2dyb3VuZCBzdWJ0cmFjdGlvbiAoZm9yIGRlbW9uc3RyYXRpb24pCmNhcCA9IGN2Mi5WaWRlb0NhcHR1cmUodmlkZW9fcGF0aCkKZnBzID0gY2FwLmdldChjdjIuQ0FQX1BST1BfRlBTKQp3aWR0aCA9IGludChjYXAuZ2V0KGN2Mi5DQVBfUFJPUF9GUkFNRV9XSURUSCkpCmhlaWdodCA9IGludChjYXAuZ2V0KGN2Mi5DQVBfUFJPUF9GUkFNRV9IRUlHSFQpKQoKIyBCYWNrZ3JvdW5kIHN1YnRyYWN0b3IKZmdiZyA9IGN2Mi5jcmVhdGVCYWNrZ3JvdW5kU3VidHJhY3Rvck1PRzIoKQpwb3NpdGlvbnMgPSBbXQoKIyBUcmFjayBvYmplY3QgcG9zaXRpb25zCndoaWxlIFRydWU6CiAgICByZXQsIGZyYW1lID0gY2FwLnJlYWQoKQogICAgaWYgbm90IHJldDoKICAgICAgICBicmVhawogICAgZmdtYXNrID0gZmdiZy5hcHBseShmcmFtZSkKICAgIGNvbnRvdXJzLCBfID0gY3YyLmZpbmRDb250b3VycyhmZ21hc2ssIGN2Mi5SRVRSX0VYVEVSTkFMLCBjdjIuQ0hBSU5fQVBQUk9YX1NJTVBMRSkKICAgIGlmIGNvbnRvdXJzOgogICAgICAgIGxhcmdlc3QgPSBtYXgoY29udG91cnMsIGtleT1jdjIuY29udG91ckFyZWEpCiAgICAgICAgTSA9IGN2Mi5tb21lbnRzKGxhcmdlc3QpCiAgICAgICAgaWYgTVsibTAwIl0gIT0gMDoKICAgICAgICAgICAgY3ggPSBpbnQoTVsibTEwIl0gLyBNWyJtMDAiXSkKICAgICAgICAgICAgY3kgPSBpbnQoTVsibTAxIl0gLyBNWyJtMDAiXSkKICAgICAgICAgICAgcG9zaXRpb25zLmFwcGVuZCgoY3gsIGN5KSkKICAgICAgICBlbHNlOgogICAgICAgICAgICBwb3NpdGlvbnMuYXBwZW5kKCgwLCAwKSkKICAgIGVsc2U6CiAgICAgICAgcG9zaXRpb25zLmFwcGVuZCgoMCwgMCkpCgpjYXAucmVsZWFzZSgpCgojIEdlbmVyYXRlIGVmZmVjdCBwZXIgZnJhbWUKZGVmIG1ha2VfZWZmZWN0X2ZyYW1lKHQpOgogICAgZnJhbWUgPSBucC56ZXJvcygoaGVpZ2h0LCB3aWR0aCwgNCksIGR0eXBlPW5wLnVpbnQ4KQogICAgaW5kZXggPSBpbnQodCAqIGZwcykKICAgIHRhaWxfbGVuZ3RoID0gMTAKICAgIGZvciBpIGluIHJhbmdlKHRhaWxfbGVuZ3RoKToKICAgICAgICBpZHggPSBpbmRleCAtIGkKICAgICAgICBpZiAwIDw9IGlkeCA8IGxlbihwb3NpdGlvbnMpOgogICAgICAgICAgICB4LCB5ID0gcG9zaXRpb25zW2lkeF0KICAgICAgICAgICAgYWxwaGEgPSBpbnQoMjAwICogKDEgLSBpIC8gdGFpbF9sZW5ndGgpKQogICAgICAgICAgICBjdjIuY2lyY2xlKGZyYW1lLCAoeCwgeSksIDIwLCAoMjU1LCAyNTUsIDAsIGFscGhhKSwgLTEpCiAgICByZXR1cm4gZnJhbWUKCiMgQ3JlYXRlIGFuZCBvdmVybGF5IGVmZmVjdCBjbGlwCmVmZmVjdCA9IFZpZGVvQ2xpcChtYWtlX2VmZmVjdF9mcmFtZSwgZHVyYXRpb249Y2xpcC5kdXJhdGlvbikuc2V0X2R1cmF0aW9uKGNsaXAuZHVyYXRpb24pCmZpbmFsID0gQ29tcG9zaXRlVmlkZW9DbGlwKFtjbGlwLCBlZmZlY3RdKQoKIyBPdXRwdXQgdmlkZW8KZmluYWwud3JpdGVfdmlkZW9maWxlKCIvbW50L2RhdGEvb3V0cHV0X3dpdGhfdGFpbF9saWdodC5tcDQiLCBjb2RlYz0ibGlieDI2NCIsIGF1ZGlvX2NvZGVjPSJhYWMiKQoKKGV4aXQpCjsgZW1wdHkgbGluZSBhdCB0aGUgZW5k