目录
一、背景介绍
二、算法概况
三、计算距离矩阵算法
四、计算简易短路线算法
五、计算规整数据算法
六、杂七杂八的算法
七、整体算法展示
八、效果展示
九、参考资料/文献
考完教资后开始放飞自我,尽管教资成绩还没出,但是我还是不断的找代码来敲。这是我的一个项目“肢识科技”的一个分支,简述一下这个项目是通过摄像头获取人体关键点后通过对这些人体关键点对比后得出一个总评分分数,如果可以的话还可以做纠错。动态时间规整(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、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)