动态时间规整 | 初入人工智能

目录

一、背景介绍

二、算法概况

三、计算距离矩阵算法

四、计算简易短路线算法

五、计算规整数据算法

六、杂七杂八的算法

七、整体算法展示

八、效果展示

九、参考资料/文献


一、背景介绍

考完教资后开始放飞自我,尽管教资成绩还没出,但是我还是不断的找代码来敲。这是我的一个项目“肢识科技”的一个分支,简述一下这个项目是通过摄像头获取人体关键点后通过对这些人体关键点对比后得出一个总评分分数,如果可以的话还可以做纠错。动态时间规整(DTW)可以说是这个项目必不可少的一个部分了。


二、算法概况

算法流程:通过djl技术来识别出人体关键点,再用这些人体关键点数据先计算出每帧占用的面积(因为动态时间规整的参数是两个时间序列,所以我把面积当做时间序列进行数据分析),再通过距离矩阵算法算出距离矩阵,接着计算简易短路线,最后计算规整数据。


三、计算距离矩阵算法

/**
 * 计算距离矩阵
 */
public boolean calcDistanceMatrix(){
    if (data == null || comparison == null || data.length <= 0
            || comparison.length <= 0)
        return false;
    distanceMatrix = new double[data.length][comparison.length];
    //距离矩阵
    for(int i=0;i 
  

解释一下参数:data是源数据,comparison是对比数据 , distanceMatrix是计算后的距离矩阵


四、计算简易短路线算法

/**
 * 计算简易路线
 */
public boolean calcSimpleRoute(){
    if (distanceMatrix == null || distanceMatrix.length <= 0) return false;
    int height = distanceMatrix.length;
    int width = distanceMatrix[0].length;
    simpleRoute = new boolean[width + height - 2];
    int index = 0;
    int i = 0 , j = 0;
    iFor: for (; i < height; i ++){
        for (; j < width ; j++){
            //判断
            if (i + 1 >= height && j + 1 >= width) {
                break iFor;//已经到右上角了
            }
            if (i + 1 >= height) {//已经在最上面那一层了,只能往右走 -
                simpleRoute[index ++] = true;
                continue;//继续
            }
            if(j + 1 >= width){//已经在最右一层,只能往上走
                simpleRoute[index ++] = false;
                continue iFor;//继续
            }
            //不在最右和最上,在中间
            double up = Math.abs(distanceMatrix[i + 1][j]);
            double right = Math.abs(distanceMatrix[i][j + 1]);
            if (up > right){//往右走
                simpleRoute[index ++] = true;
                continue;//继续
            }
            if (up < right){//往上走
                simpleRoute[index ++] = false;
                continue iFor;
            }
            double heightPercent = (double)i / height;//高百分
            double widthPercent = (double)j / width;//宽百分
            if (heightPercent > widthPercent){//剩余的宽多一点,往右走
                simpleRoute[index ++] = true;
                continue;//继续
            }else if (heightPercent < widthPercent){//剩余的高多一点,往上走
                simpleRoute[index ++] = false;
                continue iFor;
            }
            //随机走
            simpleRoute[index ++] = new Random().nextBoolean();
        }
    }
    return true;
}

这个算法很简单,算法的核心思想:只能往上和往右走,对比上和右的数值,哪个小走那边,如果一样大,就看高百分和宽百分,如果这都一样那就随机。

参数说明:simpleRoute是存储路线的参数,true为往右走,false为往上走

举个栗子:

假设有个矩阵在这里

0 0 0 0 1 0 0 0
1 1 1 1 0 1 1 1
0 0 0 0 1 0 0 0

那么他的路线就应该是这个样子

那么simpleRoute应该是这个样子 [true, true, true, false , true , true, false , true, true]


五、计算规整数据算法

/**
 * 计算规整数据
 */
public boolean calcRegularData(){
    if (data == null || simpleRoute == null ||
            data.length <= 0 || simpleRoute.length <= 0 ||
            comparison == null || comparison.length <= 0) return false;
    regularData = new double[comparison.length];
    //对比上次
    if (comparison.length >= data.length){
        int readIndex = 0;
        int index = 0;
        for (boolean temp : simpleRoute){
            if (index >= comparison.length) break;//超出范围
            regularData[index ++] = data[readIndex];
            if (!temp) {//往上走
                if (++readIndex >= data.length)
                    break;//超出范围
            }
        }
    }else {//数据长度更大
        double add = 0;
        int addLength = 0;
        int readIndex = 0;
        int index = 0;
        for (boolean temp : simpleRoute){
            if (index >= comparison.length) break;//超出范围
            if (temp) {
                if (addLength <= 0) addLength = 1;
                regularData[index++] = add / addLength;
                add = 0;
                addLength = 0;
            }
            if (readIndex >= data.length - 1)
                continue;
            addLength += 1;
            add += data[readIndex++];
        }
    }
    return true;
}

参数说明:regularData是规整后的数据

算法思想:遍历simpleRoute路线,如果data数据比comparison长,false时就计算平均,true就跳到上一行计算

如果data数据比comparison短,false就跳上一行计算,true就继续写上一次数据

备注:不过这一部分的算法个人感觉不是很优秀,我暂时也找不到且想不到更好的算了,参考一下得了


六、杂七杂八的算法

/**
 * 计算三角形面积
 */
private double calcTriangle(Point2D a , Point2D b , Point2D c){
    double aL = Math.sqrt(
            Math.pow((b.getX() - c.getX()),2.0) +
                    Math.pow(b.getY() - c.getY() , 2.0)
            );
    double bL = Math.sqrt(
            Math.pow((a.getX() - c.getX()),2.0) +
                    Math.pow(a.getY() - c.getY() , 2.0)
    );
    double cL = Math.sqrt(
            Math.pow((b.getX() - a.getX()),2.0) +
                    Math.pow(b.getY() - a.getY() , 2.0)
    );//计算三边长度
    double p = (aL + bL+ cL) / 2;
    return Math.sqrt(
        p * (p - aL) * (p - bL) * (p - cL)
    );
}

计算三角形面积公式(海伦公式):S=√p (p-a) (p-b) (p-c)  ; p = ( a + b + c ) / 2


七、整体算法展示

package com.github.lamzier.pojo;

import lombok.Data;
import java.util.Arrays;
import java.util.Random;

@Data
public class DynamicTimeWarping {

    private double[] data;//原始数据
    private double[] regularData;//规整数据
    private double[] comparison;//比较数据
    private double[][] distanceMatrix;//距离矩阵
    private boolean[] simpleRoute;//简易路线 右为true , 上为false

    public static void main(String[] args) {
        double[] data = new double[]{2530.8200341722320000,1175.1343583194505000,689.7846926562484000,2016.9687109943486000};
        double[] comparison = new double[]{267.5232442792040500, 404.4394840854965000,
                387.2320867294816000, 373.1241750613792000, 170.1118517874741400,
                378.7395880554623000, 2191.4237059663506000, 1110.0221268646424000,
                1462.5484181366292000, 1548.6010698865970000, 553.4801441919813000,
                623.2573379254926000, 947.4400868001018000, 815.4844438126282000,
                324.6664125088604000, 651.9186905016200000, 694.7342681812484000,
                1406.6804337062597000, 1981.0037076749613000, 995.5633866923604000,
                1213.6373597908764000, 176.9171199772737300, 349.4162006747608000,
                313.6463205201098400, 285.6810977822403000, 295.6756575306924300,
                307.5464914066156400, 1144.0725711368210000, 531.5807669975796000,
                259.7218683105894000, 1605.8979189210738000, 316.4024024821072000,
                546.3945422993970000, 1760.8236006748307000, 1022.2751806429127000,
                403.8890802486801000, 990.6273172440245000, 2823.8350956012860000,
                1056.4574079873503000, 104.8944459796324200, 288.3940849550800000};
        DynamicTimeWarping dynamicTimeWarping = new DynamicTimeWarping(data,comparison);
        dynamicTimeWarping.calcDistanceMatrix();
        dynamicTimeWarping.calcSimpleRoute();
//        System.err.println(Arrays.toString(dynamicTimeWarping.getSimpleRoute()));
        dynamicTimeWarping.calcRegularData();//计算规整数据

        System.err.println("口口口口源数据:" + Arrays.toString(data));
        System.err.println("口口口对比数据:" + Arrays.toString(comparison));
        double[] regularData = dynamicTimeWarping.getRegularData();
        System.err.println("规整后的源数据:" + Arrays.toString(regularData));




    }

    /**
     * 初始化
     * @param data 数据
     * @param comparison 比对数据
     */
    public DynamicTimeWarping(double[] data , double[] comparison){
        this.data = data;
        this.comparison = comparison;
    }

    /**
     * 计算规整数据
     */
    public boolean calcRegularData(){
        if (data == null || simpleRoute == null ||
                data.length <= 0 || simpleRoute.length <= 0 ||
                comparison == null || comparison.length <= 0) return false;
        regularData = new double[comparison.length];
        //对比上次
        if (comparison.length >= data.length){
            int readIndex = 0;
            int index = 0;
            for (boolean temp : simpleRoute){
                if (index >= comparison.length) break;//超出范围
                regularData[index ++] = data[readIndex];
                if (!temp) {//往上走
                    if (++readIndex >= data.length)
                        break;//超出范围
                }
            }
        }else {//数据长度更大
            double add = 0;
            int addLength = 0;
            int readIndex = 0;
            int index = 0;
            for (boolean temp : simpleRoute){
                if (index >= comparison.length) break;//超出范围
                if (temp) {
                    if (addLength <= 0) addLength = 1;
                    regularData[index++] = add / addLength;
                    add = 0;
                    addLength = 0;
                }
                if (readIndex >= data.length - 1)
                    continue;
                addLength += 1;
                add += data[readIndex++];
            }
        }
        return true;
    }

    /**
     * 计算距离矩阵
     */
    public boolean calcDistanceMatrix(){
        if (data == null || comparison == null || data.length <= 0
                || comparison.length <= 0)
            return false;
        distanceMatrix = new double[data.length][comparison.length];
        //距离矩阵
        for(int i=0;i= height && j + 1 >= width) {
                    break iFor;//已经到右上角了
                }
                if (i + 1 >= height) {//已经在最上面那一层了,只能往右走 -
                    simpleRoute[index ++] = true;
                    continue;//继续
                }
                if(j + 1 >= width){//已经在最右一层,只能往上走
                    simpleRoute[index ++] = false;
                    continue iFor;//继续
                }
                //不在最右和最上,在中间
                double up = Math.abs(distanceMatrix[i + 1][j]);
                double right = Math.abs(distanceMatrix[i][j + 1]);
                if (up > right){//往右走
                    simpleRoute[index ++] = true;
                    continue;//继续
                }
                if (up < right){//往上走
                    simpleRoute[index ++] = false;
                    continue iFor;
                }
                double heightPercent = (double)i / height;//高百分
                double widthPercent = (double)j / width;//宽百分
                if (heightPercent > widthPercent){//剩余的宽多一点,往右走
                    simpleRoute[index ++] = true;
                    continue;//继续
                }else if (heightPercent < widthPercent){//剩余的高多一点,往上走
                    simpleRoute[index ++] = false;
                    continue iFor;
                }
                //随机走
                simpleRoute[index ++] = new Random().nextBoolean();
            }
        }
        return true;
    }





}

整体算法


八、效果展示

数据展示采用的是这个学期刚学的SPSS

动态时间规整 | 初入人工智能_第1张图片

源数据

动态时间规整 | 初入人工智能_第2张图片

对比数据

动态时间规整 | 初入人工智能_第3张图片

规整后数据

怎么说,效果还行,可能是曲线特征不明显哈哈


九、参考资料/文献

1、DTW(Dynamic Time Warping)动态时间规整 - 知乎 (zhihu.com)

2、动态时间规整—DTW算法_赵至柔的博客-CSDN博客_dtw算法

3、Dynamic Time Warping 动态时间规整算法 - 阿凡卢 - 博客园 (cnblogs.com)

4、蔡兴泉,霍宇晴,李发建,孙海燕.面向太极拳学习的人体姿态估计及相似度计算[J].图学学报,2022,43(04):695-706.

面向太极拳学习的人体姿态估计及相似度计算 - 中国知网 (cnki.net)

原博客地址:单身博客 | LamBlog (lamzy.top)

你可能感兴趣的:(人工智能,算法,动态规划,java)