1+ import os
2+ import subprocess
3+ import shutil
4+
5+ # --- Configuration ---
6+ INPUT_VIDEO = "../12th.avi"
7+ OUTPUT_VIDEO = "../settings_matrix.mp4"
8+ TEMP_RAW_DIR = "matrix_temp_raw"
9+ FRAME_WIDTH = 320
10+ FRAME_HEIGHT = 320
11+
12+ # --- Parameter Space Definition ---
13+ # Define the parameter space to scan.
14+ # The script will automatically scan any TWO parameters assigned a list of values.
15+ # The other two parameters should be assigned a single scalar value.
16+
17+ # Example: Scan Brightness and Contrast
18+ BRIGHTNESS = - 0.15
19+ CONTRAST = 2.0
20+ SATURATION = [2.0 , 2.0 , 4.0 , 6.0 , 10.0 ] # Constant
21+ GAMMA = [0.6 , 0.8 , 1.0 , 1.2 , 1.4 ]
22+
23+ # Example: Scan Saturation and Gamma
24+ # BRIGHTNESS = 0.0 # Constant
25+ # CONTRAST = 1.5 # Constant
26+ # SATURATION = [0.5, 1.0, 1.5, 2.0, 2.5] # Scanned parameter (Y-axis)
27+ # GAMMA = [0.8, 0.9, 1.0, 1.1, 1.2] # Scanned parameter (X-axis)
28+
29+
30+ def extract_frames (video_path , output_dir ):
31+ """Extracts frames from the video and saves them as raw 16-bit files."""
32+ print (f"Extracting frames from { video_path } ..." )
33+ try :
34+ import cv2
35+ import numpy as np
36+ except ImportError as e :
37+ print (f"Import error: { e } . Please ensure opencv-python and numpy are installed." )
38+ return 0 , 0
39+
40+ if os .path .exists (output_dir ):
41+ shutil .rmtree (output_dir )
42+ os .makedirs (output_dir )
43+
44+ cap = cv2 .VideoCapture (video_path )
45+ if not cap .isOpened ():
46+ print (f"Error: Could not open video file { video_path } " )
47+ return 0 , 0
48+
49+ fps = cap .get (cv2 .CAP_PROP_FPS )
50+ frame_count = int (cap .get (cv2 .CAP_PROP_FRAME_COUNT ))
51+
52+ for frame_num in range (frame_count ):
53+ ret , frame = cap .read ()
54+ if not ret :
55+ break
56+ if frame .shape [2 ] == 3 :
57+ gray_frame = cv2 .cvtColor (frame , cv2 .COLOR_BGR2GRAY )
58+ else :
59+ gray_frame = frame
60+ frame_16bit = np .uint16 (gray_frame ) * 256
61+ raw_frame_path = os .path .join (output_dir , f"frame_{ frame_num :04d} .raw" )
62+ frame_16bit .tofile (raw_frame_path )
63+
64+ cap .release ()
65+ print (f"Extraction complete. { frame_count } frames extracted." )
66+ return frame_count , fps
67+
68+
69+ def create_matrix_video ():
70+ if not os .path .exists (INPUT_VIDEO ):
71+ print (f"Error: Input video not found at '{ INPUT_VIDEO } '" )
72+ return
73+
74+ # 1. Identify scanned and constant parameters
75+ all_params = {
76+ 'brightness' : BRIGHTNESS ,
77+ 'contrast' : CONTRAST ,
78+ 'saturation' : SATURATION ,
79+ 'gamma' : GAMMA
80+ }
81+ scan_params = {k : v for k , v in all_params .items () if isinstance (v , (list , tuple ))}
82+ const_params = {k : v for k , v in all_params .items () if not isinstance (v , (list , tuple ))}
83+
84+ if len (scan_params ) != 2 :
85+ print (f"Error: Exactly two parameters must be specified as lists to be scanned." )
86+ print (f"Found { len (scan_params )} scanned parameters: { list (scan_params .keys ())} " )
87+ return
88+
89+ param_y_name , param_y_levels = list (scan_params .items ())[0 ]
90+ param_x_name , param_x_levels = list (scan_params .items ())[1 ]
91+ num_y = len (param_y_levels )
92+ num_x = len (param_x_levels )
93+ total_videos = num_y * num_x
94+
95+ # 2. Prepare raw frame data
96+ num_frames , fps = extract_frames (INPUT_VIDEO , TEMP_RAW_DIR )
97+ if num_frames == 0 :
98+ return
99+
100+ # 3. Build the ffmpeg filter_complex string
101+ filter_chains = []
102+ stream_names = []
103+
104+ # Split the input stream into N copies
105+ split_filter = f"[0:v]split={ total_videos } "
106+ for i in range (total_videos ):
107+ stream_name = f"[s{ i } ]"
108+ split_filter += stream_name
109+ stream_names .append (stream_name )
110+ filter_chains .append (split_filter )
111+
112+ # Process each split stream
113+ processed_streams = []
114+ for i in range (total_videos ):
115+ y_idx = i // num_x
116+ x_idx = i % num_x
117+
118+ current_params = const_params .copy ()
119+ current_params [param_y_name ] = param_y_levels [y_idx ]
120+ current_params [param_x_name ] = param_x_levels [x_idx ]
121+
122+ in_stream = stream_names [i ]
123+ out_stream = f"[v{ i } ]"
124+ processed_streams .append (out_stream )
125+
126+ eq_filter_parts = [f"{ name } ={ value :.2f} " for name , value in current_params .items ()]
127+ eq_filter = f"eq={ ':' .join (eq_filter_parts )} "
128+
129+ y_val = param_y_levels [y_idx ]
130+ x_val = param_x_levels [x_idx ]
131+ text_filter = f"drawtext=text='{ param_y_name [0 ].upper ()} ={ y_val :.2f} { param_x_name [0 ].upper ()} ={ x_val :.2f} ':x=10:y=10:fontsize=16:fontcolor=white"
132+
133+ filter_chains .append (f"{ in_stream } { eq_filter } ,{ text_filter } { out_stream } " )
134+
135+ # Stack videos horizontally into rows
136+ row_streams = []
137+ for i in range (num_y ):
138+ row_input_streams = processed_streams [i * num_x :(i + 1 ) * num_x ]
139+ row_out_stream = f"[row{ i } ]"
140+ row_streams .append (row_out_stream )
141+ filter_chains .append (f"{ '' .join (row_input_streams )} hstack=inputs={ num_x } { row_out_stream } " )
142+
143+ # Stack rows vertically
144+ filter_chains .append (f"{ '' .join (row_streams )} vstack=inputs={ num_y } [out]" )
145+
146+ filter_complex_string = ";" .join (filter_chains )
147+
148+ # 4. Build and run the final ffmpeg command
149+ ffmpeg_command = [
150+ "ffmpeg" ,
151+ "-framerate" , str (fps ),
152+ "-f" , "image2" ,
153+ "-s" , f"{ FRAME_WIDTH } x{ FRAME_HEIGHT } " ,
154+ "-pix_fmt" , "bayer_bggr16le" ,
155+ "-i" , os .path .join (TEMP_RAW_DIR , "frame_%04d.raw" ),
156+ "-filter_complex" , filter_complex_string ,
157+ "-map" , "[out]" ,
158+ "-c:v" , "libx264" ,
159+ "-pix_fmt" , "yuv420p" ,
160+ "-crf" , "23" ,
161+ "-y" ,
162+ OUTPUT_VIDEO
163+ ]
164+
165+ print ("Running ffmpeg to create settings matrix video..." )
166+ print (f"Scanning { param_y_name } (Y-axis) and { param_x_name } (X-axis)." )
167+ print ("This may take a while..." )
168+ subprocess .run (ffmpeg_command )
169+
170+ # 5. Cleanup
171+ print ("Cleaning up temporary files..." )
172+ shutil .rmtree (TEMP_RAW_DIR )
173+
174+ print (f"Matrix video created successfully: { OUTPUT_VIDEO } " )
175+
176+
177+ if __name__ == "__main__" :
178+ create_matrix_video ()
0 commit comments