opencv入侵检测算法笔记
import cv2
import numpy as np
# 定义多个感兴趣区域(ROI)的边界
rois = [
#三个大触点
(610, 650, 590, 630), # ROI 2: (top, bottom, left, right)
(610, 650, 665, 715), # ROI 3: (top, bottom, left, right)
(610, 650, 750, 790), # ROI 4: (top, bottom, left, right)
#四个小触点
(524, 550, 620, 641),
(524, 550, 660, 678),
(524, 550, 693, 711),
(524, 550, 729, 749)
]
# 读取视频文件
video_capture = cv2.VideoCapture("test_video.mp4")
# 获取视频的帧率、宽度和高度
fps = int(video_capture.get(cv2.CAP_PROP_FPS))
width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
# 创建视频写入对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('out.mp4', fourcc, fps, (width, height))
# 创建文本文件来存储识别结果
output_file = open("intrusion_results.txt", "w")
# 定义矩形框绘制时的颜色
normal_color = (0, 255, 0) # 正常状态下的颜色为绿色
intrusion_color = (0, 0, 255) # 入侵状态下的颜色为红色
# 定义每个ROI的背景和初始帧中的物体轮廓
roi_bgs = []
initial_contours = [[] for _ in rois]
# 定义最小运动物体的面积阈值
threshold_area = 5
# 读取第一帧,并提取每个ROI的背景
ret, frame = video_capture.read()
for idx, (top, bottom, left, right) in enumerate(rois):
roi = frame[top:bottom, left:right]
roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
roi_bg = cv2.GaussianBlur(roi_gray, (15, 15), 0)
roi_bgs.append(roi_bg)
frame_count = 0
# 主循环,处理视频帧
while True:
# 读取一帧视频
ret, frame = video_capture.read()
# 检查是否成功读取帧
if not ret:
break
frame_count += 1
# 处理每一个ROI
for roi_idx, (top, bottom, left, right) in enumerate(rois):
# 提取当前ROI的区域
roi_frame = frame[top:bottom, left:right]
# 将ROI区域转换为灰度图像
gray = cv2.cvtColor(roi_frame, cv2.COLOR_BGR2GRAY)
#高斯模糊
gray = cv2.GaussianBlur(gray, (7, 7), 0)
# 计算当前帧与ROI背景之间的差异
frame_delta = cv2.absdiff(roi_bgs[roi_idx], gray)
# 手动设置固定阈值进行二值化
_, thresh = cv2.threshold(frame_delta, 50, 255, cv2.THRESH_BINARY)
# 对运动区域进行形态学操作
kernel = np.ones((5, 5), np.uint8)
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# 寻找轮廓并筛选出有效运动对象
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 检测是否有入侵发生
intrusion_detected = False
for contour in contours:
if cv2.contourArea(contour) < threshold_area:
continue
# 在原始帧上绘制矩形框
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (left+x, top+y), (left+x+w, top+y+h), intrusion_color, 2)
# 将入侵对象的位置信息保存到文本文件中
output_file.write(f"Intrusion detected at frame: {frame_count}, ROI: {roi_idx+1}, Position: ({left+x}, {top+y})\n")
intrusion_detected = True
# 记录初始帧中的物体轮廓
if frame_count == 1:
initial_contours[roi_idx] = contours
# 绘制检测范围的矩形框
cv2.rectangle(frame, (left, top), (right, bottom), normal_color, 2)
# 如果没有入侵发生,则将检测范围的颜色设置为绿色
if not intrusion_detected:
cv2.rectangle(frame, (left, top), (right, bottom), normal_color, 2)
# 将带有检测结果的帧写入新视频文件
out.write(frame)
# 关闭视频写入对象
out.release()
# 关闭输出文件
output_file.close()
# 释放视频捕获对象
video_capture.release()
高斯模糊和开闭操作有何不同
高斯模糊 | 开闭操作 | |
---|---|---|
目的 | 主要用于平滑图像,减少噪声。 | 主要用于去除噪声和填补空洞,保持图像中物体的形状。 |
应用场景 | 适用于需要去除随机噪声的场景,如预处理边缘检测。 | 适用于去除特定形状噪声或填补小孔的场景,如二值图像处理和形态分析。 |
处理方式 | 使用高斯核对图像进行卷积,是一种线性平滑技术。 | 使用结构元素进行腐蚀和膨胀,是非线性的形态学处理技术。 |
为了对图像进行处理,消除物体对反光的影响,可以进行高斯模糊和开关运算操作
高斯模糊(Gaussian Blur),也叫高斯平滑。通常用它来减少图像噪声以及降低细节层次。 | gray = cv2.GaussianBlur(gray, (21, 21), 0) |
闭运算(MORPH_CLOSE):闭运算是先膨胀后腐蚀的过程。其作用是消除小的黑色点(噪声),填补小的白色区域(目标内部的空洞),从而使得目标区域更加完整和连贯。 | # 对运动区域进行形态学操作 kernel = np.ones((5, 5), np.uint8) thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) |
sift识别算法
import cv2
import numpy as np
# 加载图像
image1 = cv2.imread('tanzhen09.jpg')
image2 = cv2.imread('sa.jpg')
# # 定义感兴趣区域(ROI)
# x, y, w, h = (500, 400, 550, 750)
# x, y, w, h = (759, 444, 170, 120)
x, y, w, h = (780, 440, 100, 90)
roi = image2[y:y+h, x:x+w]
cv2.imwrite('roi_check.jpg', roi)
# 转换为灰度图像
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray2 = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
# 高斯模糊
gray1_blurred = cv2.GaussianBlur(gray1, (11, 11), 0)
gray2_blurred = cv2.GaussianBlur(gray2, (11, 11), 0)
# 二值化图像
_, gray1_binary = cv2.threshold(gray1_blurred, 100, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
_, gray2_binary = cv2.threshold(gray2_blurred, 100, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 保存结果
out_path1 = 'bingray_check01.jpg'
cv2.imwrite(out_path1, gray1_binary)
out_path2 = 'bingray_check02.jpg'
cv2.imwrite(out_path2, gray2_binary)
# 初始化 SIFT 检测器
sift = cv2.SIFT_create()
# 检测 SIFT 关键点和计算描述符
keypoints1, descriptors1 = sift.detectAndCompute(gray1_binary, None)
keypoints2, descriptors2 = sift.detectAndCompute(gray2_binary, None)
# 使用 FLANN 匹配器进行描述符匹配
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 使用 knnMatch() 找到每个描述符的两个最佳匹配
matches = flann.knnMatch(descriptors1, descriptors2, k=2)
# 使用比率测试找到好的匹配点
good_matches = []
for m, n in matches:
if m.distance < 0.9 * n.distance:
good_matches.append(m)
# 输出匹配结果
print(f"好的匹配数量:{len(good_matches)}")
# 设置匹配阈值
threshold = 10 # 根据具体情况调整阈值
if len(good_matches) > threshold:
print("图像匹配")
else:
print("图像不匹配")
# 如果匹配成功,找到目标位置并绘制白色框
if len(good_matches) > threshold:
# 获取关键点的坐标
src_pts = np.float32([keypoints1[m.queryIdx].pt for m in good_matches]).reshape(-1, 2)
dst_pts = np.float32([keypoints2[m.trainIdx].pt for m in good_matches]).reshape(-1, 2)
# 计算单应性矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# 获取模板的轮廓
h1, w1 = gray1.shape
pts = np.float32([[0, 0], [0, h1 - 1], [w1 - 1, h1 - 1], [w1 - 1, 0]]).reshape(-1, 1, 2)
# 使用单应性矩阵将轮廓映射到目标图像上
dst = cv2.perspectiveTransform(pts, M)
# 绘制白色框
roi_with_box = cv2.polylines(roi, [np.int32(dst)], True, (255, 255, 255), 3, cv2.LINE_AA)
cv2.imwrite('roi_with_box.jpg', roi_with_box)
# 在原始图像中标记框的位置信息
image2_with_box = image2.copy()
box_top_left = (x + int(dst[0][0][0]), y + int(dst[0][0][1]))
box_bottom_right = (x + int(dst[2][0][0]), y + int(dst[2][0][1]))
cv2.rectangle(image2_with_box, box_top_left, box_bottom_right, (255, 255, 255), 3)
# 保存结果图像
cv2.imwrite('matched_image_with_box.jpg', image2_with_box)
print("finish")
sift与入侵检测相结合
import cv2
import numpy as np
# 定义多个感兴趣区域(ROI)的边界(左上右下)
rois = {
"roiA": {"coords": (600, 650, 670, 700), "flag": False, "count": 0},
"roiB": {"coords": (760, 650, 830, 700), "flag": False, "count": 0},
"roiC": {"coords": (920, 650, 990, 700), "flag": False, "count": 0},
"roi0": {"coords": (860, 490, 910, 530), "flag": False, "count": 0},
"roia": {"coords": (800, 490, 840, 530), "flag": False, "count": 0},
"roib": {"coords": (750, 490, 790, 530), "flag": False, "count": 0},
"roic": {"coords": (690, 490, 730, 530), "flag": False, "count": 0},
"rois1":{"coords": (540, 430, 590, 480), "flag": False, "count": 0},
"rois2":{"coords": (550, 560, 600, 610), "flag": False, "count": 0},
# 可以继续添加更多的ROI区域
}
# 设置匹配阈值
threshold = 10
# 加载参考图像
image1 = cv2.imread('tanzhen09.jpg')
gray1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
gray1_blurred = cv2.GaussianBlur(gray1, (15, 15), 0)
_, gray1_binary = cv2.threshold(gray1_blurred, 100, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 初始化SIFT检测器
sift = cv2.SIFT_create()
keypoints1, descriptors1 = sift.detectAndCompute(gray1_binary, None)
# 使用FLANN匹配器
FLANN_INDEX_KDTREE = 1
index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
search_params = dict(checks=50)
flann = cv2.FlannBasedMatcher(index_params, search_params)
# 读取视频文件
video_capture = cv2.VideoCapture("test_video.mp4")
# 获取视频的帧率
fps = int(video_capture.get(cv2.CAP_PROP_FPS))
time_per_frame = 1 / fps
# 创建视频写入对象
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
width = int(video_capture.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video_capture.get(cv2.CAP_PROP_FRAME_HEIGHT))
out = cv2.VideoWriter('output_video.mp4', fourcc, fps, (width, height))
# 创建文本文件来存储识别结果
output_file = open("intrusion_results.txt", "w")
# 定义矩形框绘制时的颜色
normal_color = (255, 255, 255) # 正常状态下的颜色为黄色(BGR格式)
stay_color = (0, 255, 255) # 停留超过颜色为绿色(BGR格式)
intrusion_color = (0, 0, 255) # 入侵状态下的颜色为红色(BGR格式)
in_sift_color = (0, 255, 0) # 进入SIFT检测的颜色为绿色(BGR格式)
# 定义最小运动物体的面积阈值
threshold_area = 5
# 存储检测到的触点停留时间
detection_timers = [0 for _ in rois]
stay_threshold = 0.3 # 停留超过0.2秒认为入侵
# 读取第一帧,并提取每个ROI的背景
ret, frame = video_capture.read()
if ret:
for roi_name, roi_info in rois.items():
left, top, right, bottom = roi_info["coords"]
roi = frame[top:bottom, left:right] # 提取当前ROI区域的图像
roi_gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY)
roi_bg = cv2.GaussianBlur(roi_gray, (3, 3), 0) # 对ROI区域进行高斯模糊
roi_info["bg"] = roi_gray # 将处理后的ROI背景保存
frame_count = 0
while True:
# 读取一帧视频
ret, frame = video_capture.read()
# 检查是否成功读取帧
if not ret:
break
frame_count += 1
# 遍历每个ROI区域进行处理
for roi_name, roi_info in rois.items():
left, top, right, bottom = roi_info["coords"]
roi_frame = frame[top:bottom, left:right] # 提取当前ROI区域的图像
gray = cv2.cvtColor(roi_frame, cv2.COLOR_BGR2GRAY)
#高斯模糊
gray = cv2.GaussianBlur(gray, (3, 3), 0)
frame_delta = cv2.absdiff(roi_info["bg"], gray) # 计算当前帧与背景之间的差异
# 手动设置固定阈值进行二值化
_, thresh = cv2.threshold(frame_delta, 60, 255, cv2.THRESH_BINARY)
# 形态学操作,消除光照影响
kernel = np.ones((5, 5), np.uint8)
thresh = cv2.erode(thresh, kernel, iterations=2)
thresh = cv2.dilate(thresh, kernel, iterations=2)
# 寻找轮廓
contours, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_L1)
intrusion_detected = False
# 遍历每个轮廓
for contour in contours:
if cv2.contourArea(contour) < threshold_area:
continue
(x, y, w, h) = cv2.boundingRect(contour)
cv2.rectangle(frame, (left + x, top + y), (left + x + w, top + y + h), intrusion_color, 2)
# 记录入侵事件到输出文件
output_file.write(
f"Intrusion detected at frame: {frame_count}, ROI: {roi_name}, Position: ({left + x}, {top + y})\n")
intrusion_detected = True
# 更新停留时间计时器
if intrusion_detected:
detection_timers[list(rois.keys()).index(roi_name)] += time_per_frame
else:
detection_timers[list(rois.keys()).index(roi_name)] = 0
# 如果停留时间超过阈值,记录停留事件到输出文件并标记为停留状态
if detection_timers[list(rois.keys()).index(roi_name)] >= stay_threshold:
output_file.write(
f"Stay detected at frame: {frame_count}, ROI: {roi_name}, Duration: {detection_timers[list(rois.keys()).index(roi_name)]:.2f} seconds\n")
cv2.rectangle(frame, (left, top), (right, bottom), stay_color, 2)
else:
cv2.rectangle(frame, (left, top), (right, bottom), normal_color, 2)
if roi_info.get("flag", False): # 检查ROI是否有标志为True
cv2.rectangle(frame, (left, top), (right, bottom), in_sift_color, 2)
# 如果检测到入侵,扩展ROI区域并进行SIFT特征匹配
if intrusion_detected:
# 扩展ROI区域的边界
new_top = max(bottom - 90, 0)
new_left = max(right - 110, 0)
new_bottom = min(bottom , height)
new_right = min(right , width)
#cv2.rectangle(frame, (new_left, new_top), (new_right, new_bottom), in_sift_color, 2)
# 提取扩展后的ROI区域
extended_roi = frame[new_top:new_bottom, new_left:new_right]
# 高斯模糊
extended_roi_gray = cv2.cvtColor(extended_roi, cv2.COLOR_BGR2GRAY)
extended_roi_blurred = cv2.GaussianBlur(extended_roi_gray, (15, 15), 0)
# 二值化
_, extended_roi_binary = cv2.threshold(extended_roi_blurred, 100, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# 检测并计算扩展后ROI区域的SIFT特征点和描述符
keypoints2, descriptors2 = sift.detectAndCompute(extended_roi_binary, None)
# 如果检测到足够的特征点对
if descriptors2 is not None and len(descriptors2) >= 2:
# 使用FLANN匹配器进行特征点匹配
matches = flann.knnMatch(descriptors1, descriptors2, k=2)
good_matches = [m for m, n in matches if m.distance < 0.9 * n.distance]
#print(len(good_matches))
# 如果匹配到的良好特征点对数目超过阈值
if len(good_matches) > threshold:
# 增加ROI区域的计数器
roi_info["count"] += 1
# 如果连续检测到足够帧数的目标
if roi_info["count"] >= int(fps * 0.15):
# 设置ROI区域标志为检测到目标
roi_info["flag"] = True
else:
# 如果未匹配到足够的良好特征点对,重置ROI区域计数器
roi_info["count"] = 0
# 将处理后的帧写入输出视频
out.write(frame)
# 释放资源
out.release()
output_file.close()
video_capture.release()
print("视频处理完成。")
# 输出每个区域的flag值
for roi_name, roi_info in rois.items():
print(f"区域 {roi_name} 的检测结果: {'检测到目标' if roi_info['flag'] else '未检测到目标'}")
# 输出完成信息
print("视频处理完成。")
注意python循环里的进退格,写错一个位置就可能导致循环次数发生巨大改变。