【Python】曲线简化算法实现

Overview

曲线简化算法通常应用于运动捕捉数据的关键帧提取,在此基础上还演化出了更多的算法
本文对基本的曲线简化算法进行了代码实现,以关键帧个数或线性重建误差作为迭代终止条件
其中,计算点i到直线n1-n2的距离公式如下[1]:
点到直线距离
更多算法及分析可参考[2]

Code

注释中提供一些简单的说明以便于理解

#!/usr/bin/env python
#-*- coding: utf-8 -*-

#######################
# Info : Curve Simplify
# Version 1.0
# Author : Alex Pan
# Date : 2017-07-11
#######################

import numpy as np
from termcolor import colored
import ipdb

## Data Type
uintType = np.uint8
floatType = np.float32

##-----------------------------------------------------------------------------------
## Get Distance Between point and [line_start-line_end] Line
def getPoint2LineDistance(point, line_start, line_end):
    # Exception
    if not isinstance(point, np.ndarray) or not isinstance(line_start, np.ndarray) or not isinstance(line_end, np.ndarray):
        raise TypeError('All points MUST be numpy.ndarray!')
    elif point.ndim != 1 or point.shape != line_start.shape or point.shape != line_end.shape:
        raise ValueError('points dimensions error or NOT matched!')
    elif (line_start == line_end).all():
        raise Exception('line_start is the SAME as line_end!')

    return np.sqrt(np.sum(np.square(point - line_start)) - np.square(np.sum((line_end - line_start) * (point - line_start))) / np.sum(np.square(line_end - line_start), dtype = floatType))

##-----------------------------------------------------------------------------------
## Constrcuct np.linspace Array between raw_array[index_start] and raw_array[index_end]
def getLinspaceArray(raw_array, index_start, index_end):
    # Exception
    if not isinstance(raw_array, np.ndarray):
        raise TypeError('raw_array MUST be numpy.ndarray!')
    elif index_start < 0 or index_end > raw_array.shape[0] or index_start > index_end:
        raise ValueError('index_start or index_end INVALID!')

    # Reconstruct Array by np.linspace Based on keyIndexes
    linspaceArray = np.linspace(raw_array[index_start][0], raw_array[index_end][0], num = index_end - index_start + 1, endpoint = True, dtype = floatType)
    for i in xrange(1, raw_array.shape[1]):
        linspaceArray = np.row_stack((linspaceArray, np.linspace(raw_array[index_start][i], raw_array[index_end][i], num = index_end - index_start + 1, endpoint = True, dtype = floatType)))

    return np.transpose(linspaceArray)

##-----------------------------------------------------------------------------------
## Compute Error Between 2 Arrays
def computeReconstructError(array_A, array_B):
    # Exception
    if not isinstance(array_A, np.ndarray) or not isinstance(array_B, np.ndarray):
        raise TypeError('array_A and array_B MUST be numpy.ndarray!')
    elif array_A.shape != array_B.shape:
        raise ValueError('array_A and array_B dimensions NOT matched!')

    # Vector
    if array_A.ndim == array_B.ndim == 1:
        return np.sqrt(np.sum(np.square(array_A - array_B)))

    # Array
    error_array = array_A - array_B
    error_list = [np.sqrt(np.sum(np.square(error))) for error in error_array]

    return float(sum(error_list)) / len(error_list)

##-----------------------------------------------------------------------------------
## Function of Curve Simplify Algorithm
def curveSimplify(poses_array, max_key = 10, error_threshold = 0.05):
    # Exception
    if not isinstance(poses_array, np.ndarray):
        raise TypeError('poses_array MUST be numpy.ndarray!')

    # Initialize
    N_poses, M_poses = poses_array.shape
    keyIndexes = [0, N_poses - 1]
    reconstructArray = getLinspaceArray(raw_array = poses_array, index_start = keyIndexes[0], index_end = keyIndexes[-1])

    # Divide
    flagContinue = True
    while flagContinue:
        keyIndexes.sort()
        keyDeltas = [(keyIndexes[i], keyIndexes[i + 1]) for i in xrange(len(keyIndexes) - 1)]
        for keyStart, keyEnd in keyDeltas:
            distanceList = [getPoint2LineDistance(point = poses_array[i], line_start = poses_array[keyStart], line_end = poses_array[keyEnd]) for i in xrange(keyStart + 1, keyEnd)]
            keyNew = keyStart + distanceList.index(max(distanceList)) + 1
            keyIndexes.append(keyNew)

            # Reconstruct [keyStart-keyNew] & [keyNew-keyEnd]
            reconstructArray[keyStart : keyNew + 1] = getLinspaceArray(raw_array = poses_array, index_start = keyStart, index_end = keyNew)
            reconstructArray[keyNew : keyEnd + 1] = getLinspaceArray(raw_array = poses_array, index_start = keyNew, index_end = keyEnd)
            reconstructError = computeReconstructError(poses_array, reconstructArray)

            # Print Screen
            print colored('keyNum:', 'magenta'), len(keyIndexes)
            print 'recError:', colored(str(reconstructError), 'white')

            # ipdb.set_trace()

            # End Condition: KeyNum or ReconstructError
            if len(keyIndexes) == max_key or reconstructError < error_threshold:
                flagContinue = False
                break

    keyIndexes.sort()
    return keyIndexes, reconstructError

Reference

[1] 杨涛,肖俊,吴飞,庄越挺. 基于分层曲线简化的运动捕获数据关键帧提取
[2] 杨涛. 人体运动捕获数据关键帧提取算法研究

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值