多边形热区目标点位置检测

本文介绍了通过yolov5和deepsort模型进行视频帧中的人物识别,然后利用多边形热区判断人物是否位于特定区域内。核心算法是检查目标中心点是否在多边形内,通过‘穿针引线’的方法确定点是否在不规则多边形内。代码实现了从视频帧中获取检测框,计算每个热区内的人员数量。
摘要由CSDN通过智能技术生成

本文的背景在于通过yolov5模型和deepsort模型,对监控中抽取视频帧,从而得出人物识别预测模型,再通过配置文件将待检测的多边形热区传入后,检测各热区中的人数。

*而本文将讲述如何判断目标中心点是否在任意多边形区域内。* 

from AIDetector_pytorch import Detector
import imutils
import numpy as np
import cv2
import configparser
def onSegment(p: tuple, q: tuple, r: tuple) -> bool:
    '''
    检查待判定点是否落在线段上
    :param p:'tuple':线段起始点
    :param q:'tuple':待判定点
    :param r:'tuple':线段终止点
    :return:'bool':若待判定点落在线段上返回True,否则返回False
    '''
    if ((q[0] <= max(p[0], r[0])) &
            (q[0] >= min(p[0], r[0])) &
            (q[1] <= max(p[1], r[1])) &
            (q[1] >= min(p[1], r[1]))):
        return True

    return False
def orientation(p: tuple, q: tuple, r: tuple) -> int:
    '''
    对输入点坐标进行方向判定
    :params:'tuple':三个点坐标p,q,r
    :return:'int':
        返回值为0:p、q、r三点共线
        返回值为1:顺时针方向排列
        返回值为2:逆时针方向排列
    '''
    val = (((q[1] - p[1]) *
            (r[0] - q[0])) -
           ((q[0] - p[0]) *
            (r[1] - q[1])))

    if val == 0:
        return 0
    if val > 0:
        return 1  # Collinear
    else:
        return 2  # Clock or counterclock
def doIntersect(p1, q1, p2, q2):
    '''
    检测p1为起始点q1为终止点组成的线段与p2为起始点q2为终止点组成的线段是否相交
    :param p1:'tuple':第一个多边形端点
    :param q1:'tuple':第二个多边形端点
    :param p2:'tuple':目标检测对象中心点
    :param q2:'tuple':与p2在同一条水平线上,往右侧延申的无穷点(横坐标设置为10000)
    :return:'bool':若两线段相交返回True,否则返回False
    '''
    # Find the four orientations needed for
    # general and special cases
    o1 = orientation(p1, q1, p2)
    o2 = orientation(p1, q1, q2)
    o3 = orientation(p2, q2, p1)
    o4 = orientation(p2, q2, q1)

    # General case
    if (o1 != o2) and (o3 != o4):
        return True

    # Special Cases
    # p1, q1 and p2 are colinear and
    # p2 lies on segment p1q1
    if (o1 == 0) and (onSegment(p1, p2, q1)):
        return True

    # p1, q1 and p2 are colinear and
    # q2 lies on segment p1q1
    if (o2 == 0) and (onSegment(p1, q2, q1)):
        return True

    # p2, q2 and p1 are colinear and
    # p1 lies on segment p2q2
    if (o3 == 0) and (onSegment(p2, p1, q2)):
        return True

    # p2, q2 and q1 are colinear and
    # q1 lies on segment p2q2
    if (o4 == 0) and (onSegment(p2, q1, q2)):
        return True

    return False
def is_inside_polygon(points, p: tuple) -> bool:
    '''
    在给定多边形热区内,判断中心点是否在不规则多边形区域内部。若输入点数小于3,
    则返回False。从目标中心点开始,往右侧画一条水平线,并将其延长到无穷大。计
    算该线与多边形边缘相交的交点的个数。若交点的个数为奇数或者点位于多边形的边
    上,则点在多边形内。如果没有一个条件为真,那么点在多边形外。
    :param points:'list':多边形热区各个坐标点(按连接顺序依次输入的坐标)
    :param p:'tuple':目标检测对象中心点坐标
    :return:'bool':若p点落在多边形热区内则返回True
    '''
    INT_MAX = 10000
    points = np.ravel(points)
    points = [(points[i - 1], points[i]) for i in range(len(points)) if i % 2 != 0]

    n = len(points)

    # There must be at least 3 vertices
    # in polygon
    if n < 3:
        return False
    # Create a point for line segment
    # from p to infinite
    extreme = (INT_MAX, p[1])
    count = i = 0
    while True:
        next = (i + 1) % n

        # Check if the line segment from 'p' to
        # 'extreme' intersects with the line
        # segment from 'polygon[i]' to 'polygon[next]'
        if (doIntersect(points[i],
                        points[next],
                        p, extreme)):

            # If the point 'p' is colinear with line
            # segment 'i-next', then check if it lies
            # on segment. If it lies, return true, otherwise false
            if orientation(points[i], p,
                           points[next]) == 0:
                return onSegment(points[i], p,
                                 points[next])

            count += 1

        i = next

        if (i == 0):
            break

    # Return true if count is odd, false otherwise
    return (count % 2 == 1)

这段代码是关于多边形判定,其核心思想在于==“穿针引线”==,从目标点往右侧引一条无限长的线,若右侧有多边形,无关多边形凹凸性,线总是会有进入-出去的流程,即若与多边形相交次数为奇数,则该点在多边形内。若为偶数,则不在多边形内。同时,还要考虑共线等情况。详细描述请看代码接口。

def plot_bboxes(image, bboxes, line_thickness=None):
    # Plots one bounding box on image img
    tl = line_thickness or round(
        0.002 * (image.shape[0] + image.shape[1]) / 2) + 1  # line/font thickness
    for (x1, y1, x2, y2, cls_id, pos_id) in bboxes:
        if cls_id in ['person']:
            color = (0, 0, 255)
        else:
            color = (0, 255, 0)
        c1, c2 = (x1, y1), (x2, y2)
        cv2.rectangle(image, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)
        tf = max(tl - 1, 1)  # font thickness
        t_size = cv2.getTextSize(cls_id, 0, fontScale=tl / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3
        cv2.rectangle(image, c1, c2, color, -1, cv2.LINE_AA)  # filled
        cv2.putText(image, '{} ID-{}'.format(cls_id, pos_id), (c1[0], c1[1] - 2), 0, tl / 3,
                    [225, 255, 255], thickness=tf, lineType=cv2.LINE_AA)

    return image
def get_num_single(area,result):
    '''
    计算检测对象中心点,判断目标对象中心点是否在热区内,并对预测结果标签进行判断,
    若在热区内且标签为'person',则num加一。
    :param area:'numpy.ndarray':单个热区坐标信息
    :param result:'dict': 预测结果坐标
    :return:'int':检测人数num
    '''
    num=0
    #result = det.feedCap(frame)
    for i in result['bboxes']:
        box = i[:4]#读取前四位坐标信息
        center = (
                int((box[0] + box[2]) / 2), int((box[1] + box[3]) / 2),
        )
        #cv2.circle(frame, center, point_size=1, point_color=(0, 0, 255), thickness=4)
        if i[4] == 'person':
            if is_inside_polygon(area, center):
                num = num+1
    return num
def get_eachnum(frame,config):
    '''
    检测各个热区内人数
    :param frame:'numpy.ndarray': 视频帧
    :param config:'list': 热区集合
    :return:list: 热区编号以及各热区检测人数
    '''
    eachnum=[]
    result = det.feedCap(frame)
    for area in config:
        eachnum.append(area[0])
        pts = np.array(eval(area[1])).reshape((-1, 1, 2))
        eachnum.append(get_num_single(pts,result))
    return eachnum
if __name__ == '__main__':
    det = Detector()
    video_capture = cv2.VideoCapture(0)
    config = configparser.ConfigParser()
    config.read('config.config')
    area = config.items('area')
    while video_capture.isOpened():
        ret, frame = video_capture.read()
        if not ret:
            break
        flag = cv2.waitKey(1)
        print(get_eachnum(frame, area))
#     结果呈现
# #     result 从feedcap中获取
# #     frame = plot_bboxes(frame, result['bboxes'])
# #     绘制未填充的多边形
# #     for area in area:
# #         pts = np.array(eval(area[1])).reshape((-1, 1, 2))
# #         cv2.polylines(frame, [pts], isClosed=True, color=(255, 255, 0), thickness=1)
# #     cv2.imshow('demo', frame)
# #     if flag == 27:
# #         break
# # video_capture.release()
# # cv2.destroyAllWindows()

这段是实现过程,当然,大家是跑不了的哈,因为你们没有yolov5和deepsort嘿嘿嘿

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值