前不久在无人机检测跟踪的项目中用到了多目标跟踪算法(该项目后续会发贴介绍),其中需要涉及多个目标在两帧之间的匹配问题,最初使用的是最简单的距离最小化原则进行帧间多目标的匹配。后来通过实习和查阅论文等渠道了解到了多目标跟踪领域经典的Sort和DeepSort算法,其中都使用到了匈牙利算法解决匹配问题,因此开此贴记录一下算法的学习过程。
指派问题概述
首先,对匈牙利算法解决的问题进行概述:实际中,会遇到这样的问题,有n项不同的任务,需要n个人分别完成其中的1项,每个人完成任务的时间不一样。于是就有一个问题,如何分配任务使得花费时间最少。
通俗来讲,就是n*n矩阵中,选取n个元素,每行每列各有1个元素,使得和最小。
如下表所示
上表可以抽象成一个矩阵,如果是如上表所示的求和最小问题,那么这个矩阵就叫做花费矩阵(Cost Matrix);如果要求的问题是使之和最大化,那么这个矩阵就叫做利益矩阵(Profit Matrix)。
匈牙利算法流程
算法流程
匈牙利算法有多种实现方式,比如基于图论的方式等,本文主要使用矩阵变换来实现,这种方式你甚至可以在纸上写写画画,而且理解起来比较简单。
本文算法流程如上图所示,首先进行列规约,即每行减去此行最小元素,每一列减去该列最小元素,规约后每行每列中必有0元素出现。接下来进行试指派,也就是划最少的线覆盖矩阵中全部的0元素,如果试指派的独立0元素数等于方阵维度则算法结束,如果不等于则需要对矩阵进行调整,重复试指派和调整步骤直到满足算法结束条件。
以上是我简要描述的算法流程,值得一提的是,用矩阵变换求解的匈牙利算法也有多种实现,主要不同就在于试指派和调整矩阵这块,但万变不离其宗都是为了用最少的线覆盖矩阵中全部的零元素。咱们废话少说,来看一个例子。
程序实现
完整代码(带测试用例)
'''
@Date: 2020/2/23
@Author:ZhuJunHui
@Brief: Hungarian Algorithm using Python and NumPy
'''
import numpy as np
import collections
import time
class Hungarian():
"""
"""
def __init__(self, input_matrix=None, is_profit_matrix=False):
"""
输入为一个二维嵌套列表
is_profit_matrix=False代表输入是消费矩阵(需要使消费最小化),反之则为利益矩阵(需要使利益最大化)
"""
if input_matrix is not None:
# 保存输入
my_matrix = np.array(input_matrix)
self._input_matrix = np.array(input_matrix)
self._maxColumn = my_matrix.shape[1]
self._maxRow = my_matrix.shape[0]
# 本算法必须作用于方阵,如果不为方阵则填充0变为方阵
matrix_size = max(self._maxColumn, self._maxRow)
pad_columns = matrix_size - self._maxRow
pad_rows = matrix_size - self._maxColumn
my_matrix = np.pad(my_matrix, ((0,pad_columns),(0,pad_rows)), 'constant', constant_values=(0))
# 如果需要,则转化为消费矩阵
if is_profit_matrix:
my_matrix = self.make_cost_matrix(my_matrix)
self._cost_matrix = my_matrix
self._size = len(my_matrix)
self._shape = my_matrix.shape
# 存放算法结果
self._results = []
self._totalPotential = 0
else:
self._cost_matrix = None
def make_cost_matrix(self,profit_matrix):
'''利益矩阵转化为消费矩阵,输出为numpy矩阵'''
# 消费矩阵 = 利益矩阵最大值组成的矩阵 - 利益矩阵
matrix_shape = profit_matrix.shape
offset_matrix = np.ones(matrix_shape, dtype=int) * profit_matrix.max()
cost_matrix = offset_matrix - profit_matrix
return cost_matrix
def get_results(self):
"""获取算法结果"""
return self._results
def calculate(self):
"""
实施匈牙利算法的函数
"""
result_matrix = self._cost_matrix.copy()
# 步骤 1: 矩阵每一行减去本行的最小值
for index, row in enumerate(result_matrix):
result_matrix[index] -= row.