带标记的光学动捕(编码设计及匹配)

介绍

多视角下匹配不同RGB图上的同一ID,并进行三角化重建。

编码ID规则:
编码规则为 一个中心点周围的 四个角点和另外四个边中心点 分成两组各自为循环数组,若一张图片上的编码即这两组循环数组都与另一张图上的相等,则认为这两个ID是同一个ID。

如下两幅示意图:
请添加图片描述
请添加图片描述

流程

在这里插入图片描述

代码

"""
author: daoboker
date:   2022.4.28
description:    编码规则为 四个角点和另外四个边中心点 分成两组各自为循环数组,\
                若一张图片上的编码即这两组循环数组都与另一张图上的相等,则认为这两个ID是同一个ID
"""

# 色块的监测追踪以及打印中心坐标
import random
import copy
from collections import deque
import numpy as np
import cv2
from functools import reduce
import time

SUPER = 8
radius = 0

# 设定红色阈值,HSV空间
redLower1 = np.array([0, 43, 46])
redUpper1 = np.array([10, 255, 255])
redLower2 = np.array([156, 43, 46])
redUpper2 = np.array([180, 255, 255])
# 设定绿色阈值,HSV空间
greenLower = np.array([35, 43, 46])
greenUpper = np.array([77, 255, 255])
# 设定蓝色阈值,HSV空间
blueLower = np.array([100, 43, 46])
blueUpper = np.array([124, 255, 255])
# 设定紫色阈值,HSV空间
purpleLower = np.array([125, 43, 46])
purpleUpper = np.array([155, 255, 255])

mybuffer = 64  # 初始化追踪点的列表
pts = deque(maxlen=mybuffer)

image = cv2.imread('./picture/01.png', 1)
image2 = cv2.imread('./picture/04.png', 1)
# picture = cv2.resize(picture, (197, 108))
print('输入的图片大小为:', image.shape)
print('输入的图片大小为:', image2.shape)


# 判断两个循环数组是否相等
def equal(id1, id2):
    if id1 == id2:
        return True
    cnt = 0
    while (id1 != id2) and (cnt < len(id1)):
        copy = [[] for i in range(len(id1))]
        for i in range(len(id1)):
            copy[i] = id1[(cnt+i+1)%len(id1)]
        cnt += 1
        if copy == id2:
            return True
    return False


# id生成前要按顺时针或者逆时针顺序生成
def id_order(list):
    if list == [] :
        return []
    else:
        list1 = copy.deepcopy(sorted(list, key=lambda k: k[0]))[0]
        list2 = copy.deepcopy(sorted(list, key=lambda k: k[1]))[0]
        list3 = copy.deepcopy(sorted(list, key=lambda k: k[0]))[-1]
        list4 = copy.deepcopy(sorted(list, key=lambda k: k[1]))[-1]
        dd = copy.deepcopy([list1, list2, list3, list4])
        # print(tmp)
        func = lambda x, y: x if y in x else x + [y]
        quchong1 = copy.deepcopy(reduce(func, [[], ] + dd))
        print('第一部分去除重复后的为:', quchong1)
        rest = [i for i in list if i not in quchong1]
        print('减去四个角后剩余的为:', rest)
        if rest == []:
            return dd
        else:
            list5 = copy.deepcopy(sorted(rest, key=lambda k: k[0]))[0]
            list6 = copy.deepcopy(sorted(rest, key=lambda k: k[1]))[0]
            list7 = copy.deepcopy(sorted(rest, key=lambda k: k[0]))[-1]
            list8 = copy.deepcopy(sorted(rest, key=lambda k: k[1]))[-1]
            ddd = [list1, list2, list3, list4, list5, list6, list7, list8]
            print('tmp2:', ddd)
            func = lambda x, y: x if y in x else x + [y]
            rest2 = copy.deepcopy(reduce(func, [[], ] + ddd))
            print('rest2', rest2)
            last = []
            for i in range(len(rest2)):
                last.append(rest2[i][2])
            print('last', last)
            return last


# 获取每张图片的各个id,[[x,y,label...],....]
def get_id(image):
    global radius
    # 构建掩膜,求出轮廓
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)  # 转到HSV空间
    red_mask1 = cv2.inRange(hsv, redLower1, redUpper1)  # 根据阈值构建掩膜
    red_mask2 = cv2.inRange(hsv, redLower2, redUpper2)
    red_mask = red_mask1 + red_mask2
    red_mask = cv2.erode(red_mask, None, iterations=1)  # 腐蚀操作,去除白噪点
    red_mask = cv2.dilate(red_mask, None, iterations=2)  # 膨胀操作,增大边缘体积。其实先腐蚀再膨胀的效果是开运算,去除噪点
    red_contours, red_hierarchy = cv2.findContours(red_mask.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)  # 轮廓检测,https://blog.csdn.net/Easen_Yu/article/details/89365497

    green_mask = cv2.inRange(hsv, greenLower, greenUpper)
    green_mask = cv2.erode(green_mask, None, iterations=1)
    green_mask = cv2.dilate(green_mask, None, iterations=2)
    green_contours, green_hierarchy = cv2.findContours(green_mask.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    blue_mask = cv2.inRange(hsv, blueLower, blueUpper)
    blue_mask = cv2.erode(blue_mask, None, iterations=1)
    blue_mask = cv2.dilate(blue_mask, None, iterations=2)
    blue_contours, blue_hierarchy = cv2.findContours(blue_mask.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)

    purple_mask = cv2.inRange(hsv, purpleLower, purpleUpper)
    purple_mask = cv2.erode(purple_mask, None, iterations=1)
    purple_mask = cv2.dilate(purple_mask, None, iterations=2)
    purple_contours, purple_hierarchy = cv2.findContours(purple_mask.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    # center = None  # 初始化圆形轮廓质心
    # print(len(redContours))

    # 创建颜色信息字典,以0123来代表四种颜色,BGR格式
    color = {'0': (0, 0, 255), '1': (0, 255, 0), '2': (255, 0, 0), '3': (128, 0, 128)}
    # 构造标签列表
    label_list = []
    # 当为不同颜色时,即label为0123时,构造列表[[x,y,label],...]
    for i in range(len(red_contours)):
        ((center_x, center_y), radius) = cv2.minEnclosingCircle(red_contours[i])
        # 计算轮廓的矩
        M = cv2.moments(red_contours[i])
        if radius < 8:
            # # 计算质心
            # cv2.circle(image, (int(center_x), int(center_y)), int(radius), (255, 255, 255), 2)
            label_list.append([int(center_x), int(center_y), 0])
            # cv2.circle(image, center, 5, (0, 0, 255), -1)
            # print('红色色块的中心坐标', (int(center_x), int(center_y)))
    for i in range(len(green_contours)):
        ((center_x, center_y), radius) = cv2.minEnclosingCircle(green_contours[i])
        # 计算轮廓的矩
        M = cv2.moments(green_contours[i])
        if radius < 8:
            label_list.append([int(center_x), int(center_y), 1])
    for i in range(len(blue_contours)):
        ((center_x, center_y), radius) = cv2.minEnclosingCircle(blue_contours[i])
        # 计算轮廓的矩
        M = cv2.moments(blue_contours[i])
        if radius < 8:
            label_list.append([int(center_x), int(center_y), 2])
    for i in range(len(purple_contours)):
        ((center_x, center_y), radius) = cv2.minEnclosingCircle(purple_contours[i])
        # 计算轮廓的矩
        M = cv2.moments(purple_contours[i])
        if radius < 8:
            label_list.append([int(center_x), int(center_y), 3])

    '''
    暴力遍历匹配算法
    label_list --> [[x,y,1个label],...]
    id --> [[x,y,9个label],...]
    '''
    id = copy.deepcopy(label_list)
    for i in range(len(label_list)):
        tmp = []
        for j in range(len(label_list)):
            if ((label_list[i][0] - label_list[j][0])**2) + ((label_list[i][1] - label_list[j][1])**2) < (27)**2 and (i != j):
                tmp.append(label_list[j])
                # print('label_list[j]', label_list[j])
        print('原始数据:', tmp)
        id[i].extend(id_order(tmp))
        print('id[{}]'.format(i), id[i])
        # print('label_list[i]', label_list[i])
        print('--------------------------')
    return id


if __name__ == '__main__':
    start_time = time.time()
    id1 = copy.deepcopy(get_id(image))
    id2 = copy.deepcopy(get_id(image2))
    print(len(id1))

    # Create a new output image that concatenates the two images together
    # (a.k.a) a montage
    rows1 = image.shape[0]
    cols1 = image.shape[1]
    rows2 = image2.shape[0]
    cols2 = image2.shape[1]
    out = np.zeros((max([rows1, rows2]), cols1 + cols2, 3), dtype='uint8')
    # Place the first image to the left
    out[:rows1, :cols1] = image
    # Place the next image to the right of it
    out[:rows2, cols1:] = image2

    # 2-判断id列表里相同的两个id,比较的是列表的第三个数开始到最后的数即为颜色标签
    for i in range(len(id1)):
        for j in range(len(id2)):
            if equal(id1[i][3:7], id2[j][3:7]) and equal(id1[i][7:], id2[j][7:]) and (id1[i][2] == id2[j][2]):   # 从第二位label即周围一圈的label开始比较
                cv2.circle(image, (id1[i][0], id1[i][1]), int(radius), (255, 0, 255), 2)
                cv2.circle(image2, (id2[j][0], id2[j][1]), int(radius), (255, 0, 255), 2)
                # if random.randint(1, 8) == 3:
                #     cv2.line(out, (id1[i][0], id1[i][1]), (id2[j][0]+cols1, id2[j][1]), (0,255,255), 1)
                print([id1[i], id2[j]])
    end_time = time.time()
    print('程序所花的时间为:', end_time - start_time)
    cv2.namedWindow('image1', 0)
    cv2.resizeWindow('image1', 259, 561)  # 自己设定窗口图片的大小
    cv2.namedWindow('image2', 0)
    cv2.resizeWindow('image2', 259, 561)  # 自己设定窗口图片的大小
    cv2.imshow('image1', image)
    cv2.imshow('image2', image2)

    cv2.namedWindow('out', 0)
    cv2.resizeWindow('out', 259, 561)  # 自己设定窗口图片的大小
    cv2.imshow('out', out)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

其他编码规则

四个格子+三种颜色

Trackable Surfaces
在这里插入图片描述
在这里插入图片描述
此种编码方式的ID数为18,
计算过程为。由于此四个格子组成的标记需要带方向性,所以需要减去自身中心对称的情况。
此种编码方式
①标记本身形状较大的情况下容易被检测;
②较少的格子和颜色,可考虑加颜色数,例如加一种颜色ID数变为57种;
③各标记之间步长为两个田字格中心之间的距离,不够紧凑;
在这里插入图片描述

圆点排列+四种颜色

Garment-based motion capture (GaMoCap): high-density capture of human shape in motion
在这里插入图片描述
在这里插入图片描述

此种编码方式的ID数为32640,
计算过程为。由于此9个圆组成的标记也需要带方向性,所以减去自身旋转对称的情况。
此种编码方式:
①因为格子和颜色相较于第一种都有增加,所以ID数有了相当大的增加;
②各标记之间步长就是圆点圆心之间的距离,比较紧凑;

总结:

  1. 第一种编码方式可能更加倾向于角点的检测,若是采用滤出颜色得到颜色中心坐标的方法,也很难得出一种标记ID规则和ID坐标的确定方法。
  2. 而第二种编码方式可以根据滤出颜色的坐标进而得到周围8个圆的颜色信息,从而得到整个标记的ID信息,并且可以以中心圆的圆心作为标记坐标。

后续工作

需要解决自遮挡产生折叠编码,互遮挡看不到编码的问题,还有速度上的问题;
想着可以利用已有的模型去拟合被遮挡未能重建的部分;

带标记的手部光学动捕的遮挡问题

检测中存在两类遮挡(1)待检测的目标之间相互遮挡;(2)待检测的目标被干扰物体遮挡。
解决方法:

  1. 增加相机数量,用已有模型去拟合
    Capturing Detailed Deformations of Moving Human Bodies
    关于遮挡,由于点的3D信息是通过三角化实现的,因此一个标记必须被多个相机直接看到。在实践中对于非常复杂的运动,需要增加了要部署的相机数量以增加看到标记的机会。附加运动学模型可以减少这种问题的影响,因为被遮挡的标记位置可以通过其他标记的位置来预测。
    在这里插入图片描述在这里插入图片描述
    即使在大的遮挡情况下,只要整个两个字母的代码可见,作者的方法仍然可以获得正确标记的角落。并且正确地拒绝了腹部区域的皱纹四边形,因为读取代码将是困难的或者不可能的。
    由于相机数量有限,3D重建的(三角)点将不可避免地错过观测。作者首先拟合一个已有的模型,使用这个精致的身体模型来插入静止姿势中缺失的角,从而得到没有任何孔洞的最终网格。

在这里插入图片描述

  1. 通过其他帧的无遮挡部分来插补空洞
    Capturing and Animating Occluded Cloth
    在获取过程中,遮挡不可避免地会在重建的网格中产生孔洞。作者用数据驱动的方法来填充孔洞:即使用来自其他帧的无遮挡网格来自动插补空洞。
    例如在下图情况中,左腿的大腿内侧区域很难观察到,因为右腿遮挡了它的视线。之后必须填充包含错误视图、单视角或双视角中所看到的区域。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值