diff --git a/calibration_script.py b/calibration_script.py new file mode 100644 index 0000000..f8d85ac --- /dev/null +++ b/calibration_script.py @@ -0,0 +1,192 @@ +import os +os.environ["OPENCV_VIDEOIO_MSMF_ENABLE_HW_TRANSFORMS"] = "0" +import cv2 +import orisys +import numpy as np +import time + +import pyqtgraph as pg + +def create_plot(): + p1 = win.addPlot(title="法相力") + p1.setLabel('bottom', '时间', units='s') + win.nextRow() + p2 = win.addPlot(title="切向力 X") + p2.setLabel('bottom', '时间', units='s') + win.nextRow() + p3 = win.addPlot(title="切向力 Y") + p3.setLabel('bottom', '时间', units='s') + plot_af = p1.plot(pen='k') + plot_tfx = p2.plot(pen='k') + plot_tfy = p3.plot(pen='k') + return p1, p2, p3, plot_af, plot_tfx, plot_tfy + +def update(plot_af, plot_tfx, plot_tfy, data, p1, p2, p3): + # global plot_af, plot_tfx, plot_tfy, data, p1, p2, p3 + plot_af.setData(data[:,:,0]) + p1.setYRange(min(data[:,1,0]),max(data[:,1,0])) + plot_tfx.setData(data[:,:,1]) + p2.setYRange(min(data[:,1,1]),max(data[:,1,1])) + plot_tfy.setData(data[:,:,2]) + p3.setYRange(min(data[:,1,2]),max(data[:,1,2])) + +# 深度图 +def draw_magnitude_map(vfield, threshold=3.0, colormap=cv2.COLORMAP_JET) -> np.ndarray: + """ + Draw color map of pointwise magnitude of velocity field. + + Args: + vfield: Velocity field to visualize (2D vector field) + threshold: Minimum magnitude to display (below threshold shows as zero/blue) + colormap: OpenCV colormap to use (default: COLORMAP_JET) + + Returns: + np.ndarray: 400x400 BGR image with colored magnitude map + """ + flow_field = vfield + + # Compute magnitude at each point + magnitude = np.sqrt(flow_field[:, :, 0]**2 + flow_field[:, :, 1]**2) + + # Smooth by downsampling and interpolating back + downsample_factor = 10 + h, w = magnitude.shape + magnitude_downsampled = magnitude[::downsample_factor, ::downsample_factor] + magnitude_smoothed = cv2.resize(magnitude_downsampled, (w, h), interpolation=cv2.INTER_CUBIC) + + # Apply threshold - set below-threshold values to zero + magnitude_thresholded = magnitude_smoothed.copy() + magnitude_thresholded[magnitude_smoothed < threshold] = 0 + + # Normalize to 0-255 range + mag_min, mag_max = magnitude_thresholded.min(), magnitude_thresholded.max() + if mag_max > mag_min: + magnitude_norm = ((magnitude_thresholded - mag_min) / (mag_max - mag_min) * 255).astype(np.uint8) + else: + magnitude_norm = np.zeros_like(magnitude_thresholded, dtype=np.uint8) + + # Apply colormap + magnitude_colored = cv2.applyColorMap(magnitude_norm, colormap) + + # Resize to output size + out_img = cv2.resize(magnitude_colored, (400, 400), interpolation=cv2.INTER_CUBIC) + + return out_img + +# 实时图表 +npoints = 100 # 图表数据长度 +pg.setConfigOption('background', 'w') +pg.setConfigOption('foreground', 'k') +app = pg.mkQApp("Plotting Example") +win = pg.GraphicsLayoutWidget(show=True, title="Basic plotting examples") +win.setWindowTitle('pyqtgraph example: Plotting') +p1, p2, p3, plot_af, plot_tfx, plot_tfy = create_plot() +data = np.zeros([npoints,2,3]) +start_time = None # Will be set when sensor starts +for i in range(3): + data[:,0,i] = np.linspace(-npoints,0,npoints) + + + +def main(): + global start_time + + # 打开视频 + # sensor1 = Sensor("./s1.mp4",isstitch=True,cuda = True,verbose = True) + # 方式1: 从视频文件读取 + sensor1 = orisys.Sensor(8, config_name="./config/ddjx01.json", cal_path= "./config/ddjx01.npy", cuda=False, verbose=True) + + start_time = time.time() + is_save = False + frame_count = 0 + writer = None + while True: + sensor1.get_img() # 提取并拼接复眼图像 + + if is_save and writer is not None and sensor1.frame is not None: + frame_count += 1 + save_img = sensor1.frame + + # Convert grayscale (1 channel) to BGR (3 channels) if needed + if len(save_img.shape) == 2 or (len(save_img.shape) == 3 and save_img.shape[2] == 1): + if len(save_img.shape) == 3: + save_img = save_img.squeeze(2) + save_img = cv2.cvtColor(save_img, cv2.COLOR_GRAY2BGR) + + writer.write(save_img) + + sensor1.compute_deformation(check_motion=False) # 计算形变和分解 + # 提取信息: + # 向量场: VRAW, VNORMAL, VSHEAR: 原始,法向,切向 + # 帧率,时间戳: FPS, TIMESTAMP + # 变形量总和: FNORMAL , FSHEARX, FSHEARY : 法向,切向(X),切向(Y) + fps, fn, fx, fy, flow = sensor1.read_info(sensor1.info.FPS, sensor1.info.FNORMAL, sensor1.info.FSHEARX, sensor1.info.FSHEARY, sensor1.info.VRAW) + # print(fps,fn,fx,fy) + # 显示向量场 + arrows = orisys.util.draw_arrows(sensor1.img, flow, threshold=5, grid_spacing=10, arrow_scale=1.0) + cv2.imshow("Deformation", arrows) + # 显示深度图 + mag = draw_magnitude_map(flow) + cv2.imshow("Magnitude", mag) + + # 显示图表 - 更新时间轴 + current_time = time.time() - start_time + for i in range(3): + data[:-1, 0, i] = data[1:, 0, i] # shift time data left + data[:-1, 1, i] = data[1:, 1, i] # shift force data left + data[-1, 0, :] = current_time # set current time for all plots + data[-1, 1, 0] = fn + data[-1, 1, 1] = fx + data[-1, 1, 2] = fy + fn_mean = np.mean(data[:,1,0]) + fx_mean = np.mean(data[:,1,1]) + fy_mean = np.mean(data[:,1,2]) + print(f"fps: {fps}, fn_mean: {fn_mean:.2f}, fx_mean: {fx_mean:.2f}, fy_mean: {fy_mean:.2f}") + update(plot_af, plot_tfx, plot_tfy, data, p1, p2, p3) # Update plot + + key = cv2.waitKey(1) + if key & 0xFF == ord("q"): + print("\n 停止程序") + break + if key == ord("s"): + save_path = f"./data/pulse_test.mp4" + # Ensure directory exists + os.makedirs(os.path.dirname(save_path), exist_ok=True) + # Get frame dimensions from the actual frame + save_img = sensor1.frame + + if save_img is not None: + # Convert grayscale (1 channel) to BGR (3 channels) if needed for dimension check + temp_img = save_img.copy() + if len(temp_img.shape) == 2 or (len(temp_img.shape) == 3 and temp_img.shape[2] == 1): + if len(temp_img.shape) == 3: + temp_img = temp_img.squeeze(2) + temp_img = cv2.cvtColor(temp_img, cv2.COLOR_GRAY2BGR) + + frame_height, frame_width = temp_img.shape[:2] + writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), 30, (frame_width, frame_height)) + if writer.isOpened(): + is_save = True + print(f"Start writing to {save_path}") + else: + print(f"Failed to open video writer for {save_path}") + writer = None + else: + print("No frame available to determine video dimensions") + if key == ord("e"): + is_save = False + if writer is not None: + writer.release() + writer = None + if save_path is not None: + print(f"Stop writing to {save_path}, total frames: {frame_count}") + else: + print(f"Stop writing, total frames: {frame_count}") + frame_count = 0 + + sensor1.disconnect() # 断开传感器连接 + + + +if __name__ == '__main__': + main() diff --git a/config/ddjx01.json b/config/ddjx01.json index 6c3ddb0..55118af 100644 --- a/config/ddjx01.json +++ b/config/ddjx01.json @@ -12,7 +12,7 @@ "stitcher": { "min_area_frac": 0.01, "max_area_frac": 0.04, - "max_aspect_ratio": 100, + "max_aspect_ratio": 50, "index": [1,2,3,6,7,8,11,12,13], "margin": 10, "nominal_ov_pos": 100, diff --git a/example2_two_sensor_pipeline.py b/example2_two_sensor_pipeline.py index 3e52d53..3c89040 100644 --- a/example2_two_sensor_pipeline.py +++ b/example2_two_sensor_pipeline.py @@ -124,21 +124,21 @@ def main(): # 1. 配置多个传感器 # ========================================================================= # 目标帧率:设为正数时,两路传感器会按此帧率限速,使 FPS 接近一致;设为 None 则不限速 - target_fps = 15 + target_fps = 30 num_sensors = 2 sensor_configs = [ { 'sensor_id': 0, - 'vid_src': './s1_720p.mp4', - 'config_name': './config/prototype_of3_720p.json', - 'cal_path': './config/dd01a_0_720p.npy' - }, + 'vid_src': 4, + 'config_name': './config/prototype_of3_480p.json', + 'cal_path': './config/dd01a_0_480p.npy' + }, { 'sensor_id': 1, - 'vid_src': './s1_720p.mp4', # 可以改为不同的视频源或摄像头索引 - 'config_name': './config/prototype_of3_720p.json', - 'cal_path': './config/dd01a_0_720p.npy' + 'vid_src': 6, # 可以改为不同的视频源或摄像头索引 + 'config_name': './config/prototype_of3_480p.json', + 'cal_path': './config/dd01a_0_480p.npy' }, ] diff --git a/example3_plot_data.py b/example3_plot_data.py index e2ef63f..ecadbf4 100644 --- a/example3_plot_data.py +++ b/example3_plot_data.py @@ -94,7 +94,7 @@ def main(): # 打开视频 # sensor1 = Sensor("./s1.mp4",isstitch=True,cuda = True,verbose = True) # 方式1: 从视频文件读取 - sensor1 = orisys.Sensor(0, config_name="./config/prototype_of3.json", cal_path= "./config/dd01a_0.npy", isstitch=True, cuda=False, verbose=True) + sensor1 = orisys.Sensor(8, config_name="./config/ddjx01.json", cal_path= "./config/ddjx01.npy", cuda=False, verbose=True) start_time = time.time() is_save = False @@ -115,7 +115,7 @@ def main(): writer.write(save_img) - sensor1.compute_deformation() # 计算形变和分解 + sensor1.compute_deformation(check_motion=False) # 计算形变和分解 # 提取信息: # 向量场: VRAW, VNORMAL, VSHEAR: 原始,法向,切向 # 帧率,时间戳: FPS, TIMESTAMP diff --git a/patch_generation/patch_generation_3_org.py b/patch_generation/patch_generation_3_org.py index a435dce..9b869ec 100755 --- a/patch_generation/patch_generation_3_org.py +++ b/patch_generation/patch_generation_3_org.py @@ -115,8 +115,10 @@ def generate_color_pattern(h, w, d, r): def main(): # Parameters for the pattern. # For example, using 100x100 pixels image with patches of 3x3 pixels. - height = 2750 - width = 1950 + # height = 2750 + # width = 1950 + height = 1200 + width = 1000 patch_size = 12 # Set r to a value that gives sufficient difference. # Note: r is the threshold for squared difference per channel, and the condition is diff --git a/src/orisys/Stitcher.py b/src/orisys/Stitcher.py index 6498204..8b7230a 100644 --- a/src/orisys/Stitcher.py +++ b/src/orisys/Stitcher.py @@ -449,7 +449,7 @@ class Stitcher: n_boxes = 0 bk_thresh = 0 # initial black threshold bk_thresh_step = 5 - bk_thresh_max = 100 + bk_thresh_max = 150 # Also include a low global V threshold to capture colored but dark tiles hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) @@ -458,7 +458,12 @@ class Stitcher: while n_boxes != n_boxes_target and bk_thresh <= bk_thresh_max: _, v_mask = cv2.threshold(v, bk_thresh, 255, cv2.THRESH_BINARY) # tune 50 if needed - mask = v_mask + mask = v_mask.copy() + # Exclude left and right boundary (20 pixels each) from detection + edge = 20 + if w > edge * 2: + mask[:, :edge] = 0 + mask[:, -edge:] = 0 # Morphology: remove small specks, fill small holes k = max(1, morph_k) diff --git a/test_opencl.py b/test_opencl.py new file mode 100644 index 0000000..7a0e9a6 --- /dev/null +++ b/test_opencl.py @@ -0,0 +1,33 @@ +import pyopencl as cl + +# Get all platforms +platforms = cl.get_platforms() +if not platforms: + raise RuntimeError("No OpenCL platforms found") + +# Use the first platform (rusticl in your case) +platform = platforms[0] +print("Using platform:", platform.name) + +# Get devices for that platform +devices = platform.get_devices() +if not devices: + raise RuntimeError("No devices found on platform") + +for platform in cl.get_platforms(): + for device in platform.get_devices(): + print("Device:", device.name) + # if device.type & cl.device_type.GPU == 0: + # print("Skipping non-GPU device:", device.name) + +# Use the first device (your Adreno GPU) +device = devices[0] +print("Using device:", device.name) + +# Create a context with that device +ctx = cl.Context([device]) + +# Create a command queue +queue = cl.CommandQueue(ctx) + +print("Context and command queue created successfully!") \ No newline at end of file