基于OpenCV的视频帧十字激光线检测技术问题
问题描述
我有一支投射红色十字形状的激光笔,目标是编写Python代码,从IP Webcam推流的手机摄像头视频源中检测十字的两条线条。
单张图片检测问题
先尝试从单张图片中检测线条,但代码检测出大量多余线条,而非十字的两条主线:
# import necessary modules import numpy as np import urllib.request import cv2 as cv # read the image with open("input.jpg", "rb") as image: f = image.read() # convert to byte array bytef = bytearray(f) # convert to numpy array image = np.asarray(bytef) # Convert image to grayscale gray = cv.imdecode(image, 1) # Use canny edge detection edges = cv.Canny(gray, 50, 150, apertureSize=3) # default apertureSize: 3 # Apply HoughLinesP method to # to directly obtain line end points lines_list = [] lines = cv.HoughLinesP( edges, # Input edge image 1, # Distance resolution in pixels np.pi / 180, # Angle resolution in radians threshold=100, # Min number of votes for valid line (default: 100) minLineLength=50, # Min allowed length of line maxLineGap=10 # Max allowed gap between line for joining them (default: 10) ) if lines is not None: # Iterate over points for points in lines: # Extracted points nested in the list x1, y1, x2, y2 = points[0] # Draw the lines joing the points # On the original image cv.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2) # Maintain a simples lookup list for points lines_list.append([(x1, y1), (x2, y2)]) # display image cv.imshow("Image", image) cv.waitKey()
视频流检测问题
切换到视频源检测时,完全无法检测到任何线条,对应代码如下:
import numpy as np import cv2 as cv # replace with your own IP provided in ip webcam mobile app "IPv4_address/video" cap = cv.VideoCapture("http://192.168.1.33:8080/video") while(True): _, image = cap.read() # Resize the image image = cv.resize(image, (500, 500)) # Convert image to grayscale gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY) # Use canny edge detection edges = cv.Canny(gray, 50, 150, apertureSize=3) # Apply HoughLinesP method to # to directly obtain line end points lines_list = [] lines = cv.HoughLinesP( edges, # Input edge image 1, # Distance resolution in pixels np.pi / 180, # Angle resolution in radians threshold=10, # Min number of votes for valid line (default: 100) minLineLength=5, # Min allowed length of line maxLineGap=200 # Max allowed gap between line for joining them (default: 10) ) if lines is not None: # Iterate over points for points in lines: # Extracted points nested in the list x1, y1, x2, y2 = points[0] # Draw the lines joing the points # On the original image cv.line(image, (x1, y1), (x2, y2), (0, 255, 0), 2) # Maintain a simples lookup list for points lines_list.append([(x1, y1), (x2, y2)]) cv.imshow('Livestream', image) if cv.waitKey(1) == ord('q'): break cap.release() cv.destroyAllWindows()
解决方案
针对红色激光十字的特性,从颜色过滤入手精准提取激光区域,再结合边缘检测和直线筛选,可有效去除杂线并解决视频流检测失效问题。
核心优化方向
- 用HSV色彩空间提取红色区域,避开背景干扰
- 形态学操作去除红色区域的噪声点
- 筛选水平/垂直方向的线条(十字激光的典型角度)
- 调整直线检测参数,只保留符合激光长度特征的线条
改进后的单张图片检测代码
import numpy as np import cv2 as cv # 读取图片 image = cv.imread("input.jpg") if image is None: print("无法读取图片") exit() # 转换到HSV色彩空间,提取红色激光区域 hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV) # 红色在HSV中分为两段(色相通道为环形) lower_red1 = np.array([0, 120, 70]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([170, 120, 70]) upper_red2 = np.array([180, 255, 255]) mask1 = cv.inRange(hsv, lower_red1, upper_red1) mask2 = cv.inRange(hsv, lower_red2, upper_red2) red_mask = cv.bitwise_or(mask1, mask2) # 形态学操作去噪:闭运算补缺口,开运算除小点 kernel = np.ones((3,3), np.uint8) red_mask = cv.morphologyEx(red_mask, cv.MORPH_CLOSE, kernel) red_mask = cv.morphologyEx(red_mask, cv.MORPH_OPEN, kernel) # 提取红色区域的边缘 edges = cv.Canny(red_mask, 50, 150) # 霍夫直线检测,设置合理参数过滤短线 lines = cv.HoughLinesP( edges, rho=1, theta=np.pi/180, threshold=30, minLineLength=80, maxLineGap=20 ) # 只保留水平或垂直线条(角度误差±10度内) if lines is not None: for x1,y1,x2,y2 in lines[:,0]: angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi # 水平(0/180度)或垂直(90/270度) if (abs(angle) < 10 or abs(angle - 180) < 10) or (abs(angle - 90) < 10 or abs(angle - 270) < 10): cv.line(image, (x1,y1), (x2,y2), (0,255,0), 2) cv.imshow("十字检测结果", image) cv.waitKey(0) cv.destroyAllWindows()
改进后的视频流检测代码
import numpy as np import cv2 as cv # 替换为你的IP Webcam推流地址 cap = cv.VideoCapture("http://192.168.1.33:8080/video") if not cap.isOpened(): print("无法打开视频流") exit() while True: ret, frame = cap.read() if not ret: print("无法获取视频帧") break # 缩放帧以减少计算量 frame = cv.resize(frame, (640, 480)) # 提取红色激光区域 hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV) lower_red1 = np.array([0, 120, 70]) upper_red1 = np.array([10, 255, 255]) lower_red2 = np.array([170, 120, 70]) upper_red2 = np.array([180, 255, 255]) mask1 = cv.inRange(hsv, lower_red1, upper_red1) mask2 = cv.inRange(hsv, lower_red2, upper_red2) red_mask = cv.bitwise_or(mask1, mask2) # 形态学去噪 kernel = np.ones((3,3), np.uint8) red_mask = cv.morphologyEx(red_mask, cv.MORPH_CLOSE, kernel) red_mask = cv.morphologyEx(red_mask, cv.MORPH_OPEN, kernel) # 边缘检测 edges = cv.Canny(red_mask, 50, 150) # 霍夫直线检测 lines = cv.HoughLinesP( edges, rho=1, theta=np.pi/180, threshold=20, minLineLength=60, maxLineGap=15 ) # 筛选水平/垂直线条 if lines is not None: for x1,y1,x2,y2 in lines[:,0]: angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi if (abs(angle) < 10 or abs(angle - 180) < 10) or (abs(angle - 90) < 10 or abs(angle - 270) < 10): cv.line(frame, (x1,y1), (x2,y2), (0,255,0), 2) cv.imshow("激光十字实时检测", frame) if cv.waitKey(1) == ord('q'): break cap.release() cv.destroyAllWindows()
关键说明
- 颜色过滤:HSV空间比RGB更适合颜色提取,红色分两段是因为色相通道为0-180的环形,0和180附近均为红色区域
- 形态学操作:闭运算填补激光线条的小缺口,开运算去除背景中的红色噪点
- 角度筛选:十字激光多为水平/垂直,通过计算线条角度,过滤掉倾斜的杂线
- 参数调整:可根据实际场景调整
minLineLength、maxLineGap和阈值,适配不同距离下的激光线条长度
内容的提问来源于stack exchange,提问作者burak kirmaci
相关产品推荐
相关产品推荐

