Module compoelem.compare.pose_line

Expand source code
from typing import Any
from compoelem.types import *
from compoelem.config import config

#optimize with KD trees or faiss: https://github.com/facebookresearch/faiss

# def euclidean_distance(a: Keypoint, b: Keypoint) -> float:
#     return 0

# def compare_global_action_line(a: GlobalActionLine], b: GlobalActionLine) -> float:
#     return 0

# def compare_global_action_lines(a: Sequence[GlobalActionLine], b: Sequence[GlobalActionLine]) -> float:
#     return 0

def compare_pose_line(a: PoseLine, b: PoseLine) -> float:
    return np.mean([a.top.distance(b.top), a.bottom.distance(b.bottom)])

def find_nearest_pose_line_distance(target: PoseLine, lines: Sequence[PoseLine]) -> Tuple[float, int]:
    # if(len(lines)):
    #     return config["compare"]["no_pose_line_penalty"]
    comparisons: Sequence[Tuple[float, int]] = []
    for idx, line in enumerate(lines):
        mean_distance = compare_pose_line(line, target)
        comparisons.append((mean_distance, idx))
    comparisons = np.array(comparisons)
    nearest_pose_line = comparisons[np.argmin(comparisons[:,0])] #type: ignore
    return nearest_pose_line

def compare_pose_lines(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> float:
    if(len(b) == 0):
        return len(a) * config["compare"]["no_pose_line_penalty"] 
    nearest_pose_line_distances: Sequence[Tuple[float, int]] = []
    for query_pose_line in a:
        nearest_pose_line_distances.append(find_nearest_pose_line_distance(query_pose_line, b))
    nearest_pose_line_distances = np.array(nearest_pose_line_distances)
    nearest_pose_line_sum = np.sum(nearest_pose_line_distances[:, 0]) #type: ignore
    penalty_pose_line_indices = [idx for idx in range(0,len(b)) if idx not in nearest_pose_line_distances[:,1]] #type: ignore
    penalty_pose_lines: Sequence[PoseLine] = np.array(b)[penalty_pose_line_indices]
    if len(penalty_pose_lines) > 0:
        penalty_sum = np.sum(np.array([find_nearest_pose_line_distance(line, a) for line in penalty_pose_lines])[:,0]) 
        return nearest_pose_line_sum + penalty_sum
    else:
        return nearest_pose_line_sum

# second approach to compare the poselines. 
# Idea is to get an approaximation of the bipartite minimum graph of pose combinations weighted by distance
# => then filter out all poses above a threshold and result will be: (pose count matched)/max(pose count query img, pose count target img)
def compare_pose_lines_2(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> Tuple[float, float, float]:
    if(len(b) == 0 or len(a) == 0):
        return (0, 0, 0) # since there are 0 poses to match
    pose_dist_tuple: Sequence[Tuple[float, int, int]] = [] # dist, query_idx, target_idx
    for query_idx, query_pose_line in enumerate(a):
        for target_idx, target_pose_line in enumerate(b):
            pose_dist_tuple.append((compare_pose_line(query_pose_line, target_pose_line), query_idx, target_idx))
    pose_dist_tuple_np = np.array(pose_dist_tuple)
    pose_dist_tuple_sorted = pose_dist_tuple_np[np.argsort(pose_dist_tuple_np[:,0], axis=0)]
    used_query_pose_idx = []
    used_target_pose_idx = []
    res = []
    for t in pose_dist_tuple_sorted:
        if t[1] not in used_query_pose_idx and t[2] not in used_target_pose_idx:
            res.append(t)
            used_query_pose_idx.append(t[1])
            used_target_pose_idx.append(t[2])
    res_np =  np.array(res)
    max_pose_count = max(len(a), len(b))
    threshold = 1/(max_pose_count * 2)+0.05
    res_filtered = res_np[res_np[:,0] < config["compare"]["filter_threshold"]]
    # res_filtered = res_np[res_np[:,0] < threshold] #TODO another idea: make threshold dynamic and depend on amount of poses in image => reason: more people in one image means that chances are high for a matching pose. To reduce chance => reduce the threshold
    if len(res_filtered) == 0:
        neg_mean_distance_hits = 0
    else:
        neg_mean_distance_hits = config["compare"]["filter_threshold"] - np.sum(res_filtered[:,0])/len(res_filtered)
        # guter match => neg md hoch (max: filter threshold wenn mean dist = 0)
        # schlechter match => neg md gering (0)
    hit_ratio = len(res_filtered) / max(len(a), len(b))
    # hit ratio: 
    # print(threshold, len(res_filtered), len(a), len(b), hit_ratio, neg_mean_distance_hits)
    return (hit_ratio * neg_mean_distance_hits), hit_ratio, neg_mean_distance_hits

# third is same as second but normalizes neg md between 0 and 1
def compare_pose_lines_3(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> Tuple[float, float, float]:
    if(len(b) == 0 or len(a) == 0):
        return (0, 0, 0) # since there are 0 poses to match
    pose_dist_tuple: Sequence[Tuple[float, int, int]] = [] # dist, query_idx, target_idx
    for query_idx, query_pose_line in enumerate(a):
        for target_idx, target_pose_line in enumerate(b):
            pose_dist_tuple.append((compare_pose_line(query_pose_line, target_pose_line), query_idx, target_idx))
    pose_dist_tuple_np = np.array(pose_dist_tuple)
    pose_dist_tuple_sorted = pose_dist_tuple_np[np.argsort(pose_dist_tuple_np[:,0], axis=0)]
    used_query_pose_idx = []
    used_target_pose_idx = []
    res = []
    for t in pose_dist_tuple_sorted:
        if t[1] not in used_query_pose_idx and t[2] not in used_target_pose_idx:
            res.append(t)
            used_query_pose_idx.append(t[1])
            used_target_pose_idx.append(t[2])
    res_np =  np.array(res)
    # max_pose_count = max(len(a), len(b))
    # threshold = 1/(max_pose_count * 2)+0.05
    res_filtered = res_np[res_np[:,0] < config["compare"]["filter_threshold"]]
    # res_filtered = res_np[res_np[:,0] < threshold] #TODO another idea: make threshold dynamic and depend on amount of poses in image => reason: more people in one image means that chances are high for a matching pose. To reduce chance => reduce the threshold
    if len(res_filtered) == 0:
        neg_mean_distance_hits = 0
    else:
        neg_mean_distance_hits = (config["compare"]["filter_threshold"] - np.sum(res_filtered[:,0])/len(res_filtered)) / config["compare"]["filter_threshold"]
        # guter match => neg md hoch (max = 1)
        # schlechter match => neg md gering (min = 0)
    hit_ratio = len(res_filtered) / max(len(a), len(b))
    # guter match => neg hr hoch (max = 1)
    # schlechter match => neg hr gering (min = 0)
    # guter match => neg cr hoch (max = 1)
    # schlechter match => neg cr gering (min = 0)
    return (hit_ratio * neg_mean_distance_hits), hit_ratio, neg_mean_distance_hits

# if we normalize by action center we are getting more than one normalization results because we can have multiple action centers
# because we only want one similarity score for each pair of pictures we then filter for the lowest similarity between all combination of normalizations
def filter_pose_line_ga_result(ga_res: Sequence[Tuple[float, float, float, Any]]) -> Tuple[float, float, float, Any]:
    # combined_ratio, hit_ratio, neg_mean_distance_hits, target_data
    np_ga_res = np.array(ga_res)
    ga_res_filtered_by_hit_ratio = np_ga_res[np_ga_res[:,1] == max(np.array(ga_res)[:,1])] #first filter elem with max hit ratio
    ga_res_filtered_by_mean_dist = ga_res_filtered_by_hit_ratio[ga_res_filtered_by_hit_ratio[:,2] == max(np.array(ga_res_filtered_by_hit_ratio)[:,2])] #then filter elem with max neq md
    return ga_res_filtered_by_mean_dist[0]

Functions

def compare_pose_line(a: PoseLine, b: PoseLine) ‑> float
Expand source code
def compare_pose_line(a: PoseLine, b: PoseLine) -> float:
    return np.mean([a.top.distance(b.top), a.bottom.distance(b.bottom)])
def compare_pose_lines(a: Sequence[PoseLine], b: Sequence[PoseLine]) ‑> float
Expand source code
def compare_pose_lines(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> float:
    if(len(b) == 0):
        return len(a) * config["compare"]["no_pose_line_penalty"] 
    nearest_pose_line_distances: Sequence[Tuple[float, int]] = []
    for query_pose_line in a:
        nearest_pose_line_distances.append(find_nearest_pose_line_distance(query_pose_line, b))
    nearest_pose_line_distances = np.array(nearest_pose_line_distances)
    nearest_pose_line_sum = np.sum(nearest_pose_line_distances[:, 0]) #type: ignore
    penalty_pose_line_indices = [idx for idx in range(0,len(b)) if idx not in nearest_pose_line_distances[:,1]] #type: ignore
    penalty_pose_lines: Sequence[PoseLine] = np.array(b)[penalty_pose_line_indices]
    if len(penalty_pose_lines) > 0:
        penalty_sum = np.sum(np.array([find_nearest_pose_line_distance(line, a) for line in penalty_pose_lines])[:,0]) 
        return nearest_pose_line_sum + penalty_sum
    else:
        return nearest_pose_line_sum
def compare_pose_lines_2(a: Sequence[PoseLine], b: Sequence[PoseLine]) ‑> Tuple[float, float, float]
Expand source code
def compare_pose_lines_2(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> Tuple[float, float, float]:
    if(len(b) == 0 or len(a) == 0):
        return (0, 0, 0) # since there are 0 poses to match
    pose_dist_tuple: Sequence[Tuple[float, int, int]] = [] # dist, query_idx, target_idx
    for query_idx, query_pose_line in enumerate(a):
        for target_idx, target_pose_line in enumerate(b):
            pose_dist_tuple.append((compare_pose_line(query_pose_line, target_pose_line), query_idx, target_idx))
    pose_dist_tuple_np = np.array(pose_dist_tuple)
    pose_dist_tuple_sorted = pose_dist_tuple_np[np.argsort(pose_dist_tuple_np[:,0], axis=0)]
    used_query_pose_idx = []
    used_target_pose_idx = []
    res = []
    for t in pose_dist_tuple_sorted:
        if t[1] not in used_query_pose_idx and t[2] not in used_target_pose_idx:
            res.append(t)
            used_query_pose_idx.append(t[1])
            used_target_pose_idx.append(t[2])
    res_np =  np.array(res)
    max_pose_count = max(len(a), len(b))
    threshold = 1/(max_pose_count * 2)+0.05
    res_filtered = res_np[res_np[:,0] < config["compare"]["filter_threshold"]]
    # res_filtered = res_np[res_np[:,0] < threshold] #TODO another idea: make threshold dynamic and depend on amount of poses in image => reason: more people in one image means that chances are high for a matching pose. To reduce chance => reduce the threshold
    if len(res_filtered) == 0:
        neg_mean_distance_hits = 0
    else:
        neg_mean_distance_hits = config["compare"]["filter_threshold"] - np.sum(res_filtered[:,0])/len(res_filtered)
        # guter match => neg md hoch (max: filter threshold wenn mean dist = 0)
        # schlechter match => neg md gering (0)
    hit_ratio = len(res_filtered) / max(len(a), len(b))
    # hit ratio: 
    # print(threshold, len(res_filtered), len(a), len(b), hit_ratio, neg_mean_distance_hits)
    return (hit_ratio * neg_mean_distance_hits), hit_ratio, neg_mean_distance_hits
def compare_pose_lines_3(a: Sequence[PoseLine], b: Sequence[PoseLine]) ‑> Tuple[float, float, float]
Expand source code
def compare_pose_lines_3(a: Sequence[PoseLine], b: Sequence[PoseLine]) -> Tuple[float, float, float]:
    if(len(b) == 0 or len(a) == 0):
        return (0, 0, 0) # since there are 0 poses to match
    pose_dist_tuple: Sequence[Tuple[float, int, int]] = [] # dist, query_idx, target_idx
    for query_idx, query_pose_line in enumerate(a):
        for target_idx, target_pose_line in enumerate(b):
            pose_dist_tuple.append((compare_pose_line(query_pose_line, target_pose_line), query_idx, target_idx))
    pose_dist_tuple_np = np.array(pose_dist_tuple)
    pose_dist_tuple_sorted = pose_dist_tuple_np[np.argsort(pose_dist_tuple_np[:,0], axis=0)]
    used_query_pose_idx = []
    used_target_pose_idx = []
    res = []
    for t in pose_dist_tuple_sorted:
        if t[1] not in used_query_pose_idx and t[2] not in used_target_pose_idx:
            res.append(t)
            used_query_pose_idx.append(t[1])
            used_target_pose_idx.append(t[2])
    res_np =  np.array(res)
    # max_pose_count = max(len(a), len(b))
    # threshold = 1/(max_pose_count * 2)+0.05
    res_filtered = res_np[res_np[:,0] < config["compare"]["filter_threshold"]]
    # res_filtered = res_np[res_np[:,0] < threshold] #TODO another idea: make threshold dynamic and depend on amount of poses in image => reason: more people in one image means that chances are high for a matching pose. To reduce chance => reduce the threshold
    if len(res_filtered) == 0:
        neg_mean_distance_hits = 0
    else:
        neg_mean_distance_hits = (config["compare"]["filter_threshold"] - np.sum(res_filtered[:,0])/len(res_filtered)) / config["compare"]["filter_threshold"]
        # guter match => neg md hoch (max = 1)
        # schlechter match => neg md gering (min = 0)
    hit_ratio = len(res_filtered) / max(len(a), len(b))
    # guter match => neg hr hoch (max = 1)
    # schlechter match => neg hr gering (min = 0)
    # guter match => neg cr hoch (max = 1)
    # schlechter match => neg cr gering (min = 0)
    return (hit_ratio * neg_mean_distance_hits), hit_ratio, neg_mean_distance_hits
def filter_pose_line_ga_result(ga_res: Sequence[Tuple[float, float, float, Any]]) ‑> Tuple[float, float, float, Any]
Expand source code
def filter_pose_line_ga_result(ga_res: Sequence[Tuple[float, float, float, Any]]) -> Tuple[float, float, float, Any]:
    # combined_ratio, hit_ratio, neg_mean_distance_hits, target_data
    np_ga_res = np.array(ga_res)
    ga_res_filtered_by_hit_ratio = np_ga_res[np_ga_res[:,1] == max(np.array(ga_res)[:,1])] #first filter elem with max hit ratio
    ga_res_filtered_by_mean_dist = ga_res_filtered_by_hit_ratio[ga_res_filtered_by_hit_ratio[:,2] == max(np.array(ga_res_filtered_by_hit_ratio)[:,2])] #then filter elem with max neq md
    return ga_res_filtered_by_mean_dist[0]
def find_nearest_pose_line_distance(target: PoseLine, lines: Sequence[PoseLine]) ‑> Tuple[float, int]
Expand source code
def find_nearest_pose_line_distance(target: PoseLine, lines: Sequence[PoseLine]) -> Tuple[float, int]:
    # if(len(lines)):
    #     return config["compare"]["no_pose_line_penalty"]
    comparisons: Sequence[Tuple[float, int]] = []
    for idx, line in enumerate(lines):
        mean_distance = compare_pose_line(line, target)
        comparisons.append((mean_distance, idx))
    comparisons = np.array(comparisons)
    nearest_pose_line = comparisons[np.argmin(comparisons[:,0])] #type: ignore
    return nearest_pose_line