1692E. Binary Deque

原题链接

E. Binary Deque

time limit per test

2 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

Slavic has an array of length nn consisting only of zeroes and ones. In one operation, he removes either the first or the last element of the array.

What is the minimum number of operations Slavic has to perform such that the total sum of the array is equal to ss after performing all the operations? In case the sum ss can't be obtained after any amount of operations, you should output -1.

Input

The first line contains a single integer tt (1≤t≤1041≤t≤104) — the number of test cases.

The first line of each test case contains two integers nn and ss (1≤n,s≤2⋅1051≤n,s≤2⋅105

import cv2 import numpy as np from collections import deque # 基础配置参数(关键修改:固定px_per_cm,动态计算透视目标尺寸) known_length_cm = 29.7 # A4纸长度(cm) known_width_cm = 21.0 # A4纸宽度(cm) FIXED_PX_PER_CM = 100 # 固定像素/厘米比例(核心!不受距离影响) threshold_value = 75 # 二值化阈值 max_threshold = 255 # 最大阈值 # 存储检测数据的双端队列(保留最近5次结果用于滤波) detection_data = { "axis_distance": deque(maxlen=5), "vertical_distance": deque(maxlen=5), "shapes": deque(maxlen=5) } def order_points(pts): """标准四边形顶点排序(左上→右上→右下→左下),避免透视变换扭曲""" rect = np.zeros((4, 2), dtype="float32") s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] # 左上(x+y最小) rect[2] = pts[np.argmax(s)] # 右下(x+y最大) diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] # 右上(x-y最小) rect[3] = pts[np.argmax(diff)] # 左下(x-y最大) return rect def detect_shapes_in_warped(warped, px_per_cm, display_frame, threshold_value): """ 非YOLO模式核心形状检测函数(无修改,仅依赖稳定的px_per_cm) :param warped: 透视变换后的矫正图像 :param px_per_cm: 固定像素/厘米比例(恒为FIXED_PX_PER_CM) :param display_frame: 主显示帧(用于叠加文字说明) :param threshold_value: 二值化阈值 :return: warped_display(标注后矫正图)、shapes_info(形状信息列表)、binary_warped(二值化图像) """ # 1. 图像预处理:灰度转换 + 二值化 gray_warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY) _, binary_warped = cv2.threshold(gray_warped, threshold_value, max_threshold, cv2.THRESH_BINARY) # 2. 形态学闭运算:填充孔洞(5x5矩形结构元素) kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) binary_warped = cv2.morphologyEx(binary_warped, cv2.MORPH_CLOSE, kernel) # 3. 轮廓提取 + 小噪点过滤(面积<500像素) contours, _ = cv2.findContours(binary_warped, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE) warped_display = warped.copy() shapes_info = [] # 4. 形状识别与分类 for i, cnt in enumerate(contours): area = cv2.contourArea(cnt) if area < 500: # 过滤小噪点 continue perimeter = cv2.arcLength(cnt, True) if perimeter == 0: # 避免除零错误 continue approx = cv2.approxPolyDP(cnt, 0.04 * perimeter, True) # 多边形逼近 approx_len = len(approx) x, y, w, h = cv2.boundingRect(cnt) # 外接矩形 # 三角形(3条边) if approx_len == 3: side_lengths = [np.linalg.norm(approx[j][0] - approx[(j + 1) % 3][0]) for j in range(3)] max_side_cm = max(side_lengths) / px_per_cm # 可视化标注 cv2.drawContours(warped_display, [approx], 0, (0, 255, 0), 2) cv2.putText(warped_display, f"Triangle: {max_side_cm:.2f}cm", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) cv2.putText(display_frame, f"Triangle {i + 1}: {max_side_cm:.2f}cm", (10, 50 + i * 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) # 存储形状信息 shapes_info.append({ "type": "triangle", "max_side_cm": max_side_cm, "contour_index": i }) # 正方形(4条边 + 宽高比0.85~1.15) elif approx_len == 4 and 0.85 <= float(w) / h <= 1.15: side_cm = w / px_per_cm # 可视化标注 cv2.rectangle(warped_display, (x, y), (x + w, y + h), (255, 0, 0), 2) cv2.putText(warped_display, f"Square: {side_cm:.2f}cm", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2) cv2.putText(display_frame, f"Square {i + 1}: {side_cm:.2f}cm", (10, 90 + i * 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2) # 存储形状信息 shapes_info.append({ "type": "square", "side_cm": side_cm, "contour_index": i }) # 圆形(圆形度0.85~1.15) else: circularity = (4 * np.pi * area) / (perimeter ** 2) if 0.85 <= circularity <= 1.15: diameter_cm = max(w, h) / px_per_cm center = (x + w // 2, y + h // 2) # 可视化标注 cv2.circle(warped_display, center, w // 2, (0, 255, 255), 2) cv2.putText(warped_display, f"Circle: {diameter_cm:.2f}cm", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2) cv2.putText(display_frame, f"Circle {i + 1}: {diameter_cm:.2f}cm", (10, 130 + i * 30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2) # 存储形状信息 shapes_info.append({ "type": "circle", "diameter_cm": diameter_cm, "contour_index": i }) # 新增:返回二值化图像,用于单独显示 return warped_display, shapes_info, binary_warped # 辅助函数:移动平均滤波(用于距离和形状尺寸平滑) def moving_average_filter(data, window_size=5): if len(data) < window_size: return sum(data) / len(data) if data else 0 return sum(list(data)[-window_size:]) / window_size # 辅助函数:形状数据滤波(基于历史5次结果) def filter_shape_data(shapes_history, shape_type): if not shapes_history: return None type_history = [s for entry in shapes_history for s in entry if s["type"] == shape_type] if not type_history: return None filtered = type_history[-1].copy() if shape_type == "triangle": filtered["max_side_cm"] = sum(s["max_side_cm"] for s in type_history) / len(type_history) elif shape_type == "square": filtered["side_cm"] = sum(s["side_cm"] for s in type_history) / len(type_history) elif shape_type == "circle": filtered["diameter_cm"] = sum(s["diameter_cm"] for s in type_history) / len(type_history) return filtered # 主流程(核心修改:适配电脑摄像头,移除V4L2后端) def main_basic_detection(): # 关键修改:电脑摄像头用默认后端(移除cv2.CAP_V4L2),0表示默认摄像头,1表示外接摄像头 cap = cv2.VideoCapture(0) if not cap.isOpened(): print("无法打开摄像头!请检查:") print("1. 摄像头是否被其他程序占用(如Zoom、微信视频)") print("2. 电脑是否授予摄像头访问权限(设置→隐私→摄像头)") print("3. 若有外接摄像头,将cap = cv2.VideoCapture(0) 改为 cap = cv2.VideoCapture(1)") return # 可选优化:设置摄像头分辨率(电脑摄像头推荐1280x720,平衡清晰度和性能) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) # 相机标定参数(用于畸变校正,可选但建议保留) camera_matrix = np.array([ [1.22664341e+04, 0.00000000e+00, 1.23363768e+03], [0.00000000e+00, 1.22682592e+04, 3.25759070e+02], [0.00000000e+00, 0.00000000e+00, 1.00000000e+00] ], dtype=np.float32) dist_coeffs = np.array([[0, 0, 0, 0, 0]], dtype=np.float32) # 动态计算透视变换目标尺寸(固定px_per_cm,不受距离影响) dst_width = int(known_width_cm * FIXED_PX_PER_CM) # 21cm × 100px/cm = 2100px dst_height = int(known_length_cm * FIXED_PX_PER_CM) # 29.7cm × 100px/cm = 2970px dst_pts = np.array([ [0, 0], [dst_width - 1, 0], [dst_width - 1, dst_height - 1], [0, dst_height - 1] ], dtype=np.float32) # 目标四边形顶点(固定尺寸) while True: ret, frame = cap.read() if not ret: print("获取帧失败!请检查摄像头连接") break # 1. 畸变校正(可选,提升精度) h, w = frame.shape[:2] new_camera_matrix, roi = cv2.getOptimalNewCameraMatrix(camera_matrix, dist_coeffs, (w, h), 1, (w, h)) mapx, mapy = cv2.initUndistortRectifyMap(camera_matrix, dist_coeffs, None, new_camera_matrix, (w, h), 5) frame = cv2.remap(frame, mapx, mapy, cv2.INTER_LINEAR) x, y, crop_w, crop_h = roi frame = frame[y:y + crop_h, x:x + crop_w] # 2. 检测A4纸轮廓(用于透视变换) gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) _, binary = cv2.threshold(gray, threshold_value, max_threshold, cv2.THRESH_BINARY) inv_binary = cv2.bitwise_not(binary) # 反相:A4纸为白色,背景为黑色 # 形态学开运算:先腐蚀再膨胀,去除小噪点 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) inv_binary = cv2.morphologyEx(inv_binary, cv2.MORPH_OPEN, kernel) # 提取轮廓 contours, _ = cv2.findContours(inv_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 3. 透视变换(核心修改:目标尺寸固定,px_per_cm恒为100) warped = frame.copy() if contours: # 选择最大轮廓(A4纸) max_contour = max(contours, key=cv2.contourArea) perimeter = cv2.arcLength(max_contour, True) approx = cv2.approxPolyDP(max_contour, 0.03 * perimeter, True) # 多边形逼近 if len(approx) == 4: # 确认是四边形(A4纸) src_pts = order_points(approx.reshape(4, 2).astype(np.float32)) # 排序顶点 M = cv2.getPerspectiveTransform(src_pts, dst_pts) # 计算透视矩阵 warped = cv2.warpPerspective(frame, M, (dst_width, dst_height)) # 透视变换 # 4. 固定px_per_cm,不受距离影响(核心!) px_per_cm = FIXED_PX_PER_CM # 5. 核心:形状检测与尺寸测量(新增接收二值化图像) display_frame = frame.copy() cv2.putText(display_frame, "Shape Detection Results (PC Camera)", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 0, 0), 2) # 新增:接收binary_warped(透视后的二值化图像) warped_display, shapes_info, binary_warped = detect_shapes_in_warped(warped, px_per_cm, display_frame, threshold_value) # 6. 存储形状数据用于滤波 if shapes_info: detection_data["shapes"].append(shapes_info) # 7. 显示结果(调整窗口大小适配电脑屏幕) cv2.imshow("1. Detection Results", cv2.resize(display_frame, (800, 600))) # 检测结果窗口 cv2.imshow("2. Warped & Annotated", cv2.resize(warped_display, (600, 800))) # 矫正+标注窗口 # 新增:二值化图像窗口(将单通道灰度图转为三通道BGR,便于统一显示) binary_warped_bgr = cv2.cvtColor(binary_warped, cv2.COLOR_GRAY2BGR) # 在二值化图像上添加标题,方便识别 cv2.putText(binary_warped_bgr, "Binary Image (Warped)", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) cv2.imshow("3. Binary Image", cv2.resize(binary_warped_bgr, (600, 800))) # 二值化窗口 # 按键退出(按'q'退出,按's'保存当前所有结果) key = cv2.waitKey(1) & 0xFF if key == ord('q'): break elif key == ord('s'): cv2.imwrite("1_detection_result.jpg", display_frame) cv2.imwrite("2_warped_annotated.jpg", warped_display) cv2.imwrite("3_binary_image.jpg", binary_warped) print("所有结果已保存到当前目录!") # 释放资源(关闭所有窗口) cap.release() cv2.destroyAllWindows() if __name__ == "__main__": main_basic_detection()针对这个代码的基础上给我添加pnn算法,实现无论距离远近,测出来物体的实际尺寸不随距离变化产生太大误差【相机内参矩阵】(camera_matrix): [[422.17826004 0. 222.64141001] [ 0. 437.04479208 307.11403884] [ 0. 0. 1. ]] 【畸变系数】(dist_coeffs): [[-0.00606872 0.05575608 0.00526463 0.00236328 -0.14050405]]这是摄像机的参数
最新发布
12-03
要在包含OpenCV形状检测的Python代码中添加PNN(概率神经网络)算法以实现物体实际尺寸测量不受距离影响,以下是一个详细的实现步骤和代码示例。 首先,PNN是一种基于贝叶斯决策规则和Parzen窗的概率神经网络,常用于分类和回归任务。在物体尺寸测量中,可以使用PNN来根据像素尺寸和距离信息预测物体的实际尺寸。 以下是实现该功能的Python代码: ```python import cv2 import numpy as np from neupy import algorithms # 相机内参矩阵和畸变系数 camera_matrix = np.array([[422.17826004, 0., 222.64141001], [0., 437.04479208, 307.11403884], [0., 0., 1.]]) dist_coeffs = np.array([[-0.00606872, 0.05575608, 0.00526463, 0.00236328, -0.14050405]]) # 假设这里有一些训练数据,格式为 (像素尺寸, 距离, 实际尺寸) train_data = np.array([ [100, 1000, 10], [120, 1200, 10], [80, 800, 10], [150, 1500, 15], [180, 1800, 15] ]) # 分离特征和标签 X_train = train_data[:, :2] y_train = train_data[:, 2] # 创建PNN模型 pnn = algorithms.PNN(std=10) pnn.train(X_train, y_train) def measure_object_size(image): # 图像去畸变 undistorted_image = cv2.undistort(image, camera_matrix, dist_coeffs) # 进行形状检测,这里简单以检测轮廓为例 gray = cv2.cvtColor(undistorted_image, cv2.COLOR_BGR2GRAY) _, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for contour in contours: # 计算轮廓的边界框 x, y, w, h = cv2.boundingRect(contour) # 假设这里可以通过某种方法获取物体的距离,这里简单假设为1000mm distance = 1000 # 计算像素尺寸 pixel_size = max(w, h) # 使用PNN模型预测实际尺寸 input_data = np.array([[pixel_size, distance]]) predicted_size = pnn.predict(input_data) # 在图像上绘制边界框和预测的实际尺寸 cv2.rectangle(undistorted_image, (x, y), (x + w, y + h), (0, 255, 0), 2) cv2.putText(undistorted_image, f"Size: {predicted_size[0]:.2f}mm", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2) return undistorted_image # 读取图像 image = cv2.imread('your_image.jpg') result = measure_object_size(image) # 显示结果 cv2.imshow('Object Size Measurement', result) cv2.waitKey(0) cv2.destroyAllWindows() ``` ### 代码说明: 1. **相机内参和畸变系数**:定义了相机的内参矩阵和畸变系数,用于图像去畸变处理。 2. **训练数据**:假设了一些训练数据,包含像素尺寸、距离和实际尺寸。 3. **PNN模型**:使用`neupy`库创建了一个PNN模型,并使用训练数据进行训练。 4. **形状检测**:使用OpenCV进行形状检测,计算物体的像素尺寸。 5. **尺寸预测**:使用PNN模型根据像素尺寸和距离预测物体的实际尺寸。 6. **结果显示**:在图像上绘制边界框和预测的实际尺寸,并显示结果。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值