using System.Collections; using System.Collections.Generic; using UnityEngine; using Unity.Barracuda; public class BarracudaRunner : MonoBehaviour { public NNModel neuralNetworkModel; public WorkerFactory.Type workerType = WorkerFactory.Type.Auto; public float waitTime = 10; public bool verbose = true; /// /// Number of joint points. /// private const int jointNum = 24; private int jointNumSquared { get { return jointNum * 2; } } private int jointNumCube { get { return jointNum * 3; } } /// /// Input image size. /// public int inputImageSize; private float inputImageHalfSize { get { return (float)inputImageSize / 2f; } } private float imageScale { get { return inputImageSize / (float)heatMapCol; } } /// /// Column number of heatmap. /// public int heatMapCol; private int heatMapColSquared { get { return heatMapCol * heatMapCol; } } private int heatMapColCube { get { return heatMapCol * heatMapCol * heatMapCol; } } /// /// Kalman filter parameter Q. /// public float kalmanParamQ; /// /// Kalman filter parameter R. /// public float kalmanParamR; public bool useLowPassFilter = true; public float lowPassParam = 0.1f; public Texture2D baseTexture; public VideoCapture videoCapture; public Avatar avatar; private float[] heatMap3D; private float[] offset3D; private int heatMapColxJointNum { get { return heatMapCol * jointNum; } } private int cubeOffsetLinear { get { return heatMapCol * jointNumCube; } } private int cubeOffsetSquared { get { return heatMapColSquared * jointNumCube; } } private const string inputName1 = "input.1"; private const string inputName2 = "input.4"; private const string inputName3 = "input.7"; private Tensor input = new Tensor(); private Dictionary inputs = new Dictionary() { { inputName1, null }, { inputName2, null }, { inputName3, null }, }; private Tensor[] outputs = new Tensor[4]; private Model model; private IWorker worker; private bool loaded = false; private void Start() { heatMap3D = new float[jointNum * heatMapColCube]; offset3D = new float[jointNum * heatMapColCube * 3]; // Disabel sleep. Screen.sleepTimeout = SleepTimeout.NeverSleep; // Initialize the model. model = ModelLoader.Load(neuralNetworkModel, verbose); worker = WorkerFactory.CreateWorker(workerType, model, verbose); StartCoroutine(Load()); } private void Update() { if( loaded ) { UpdateAvatar(); } } private IEnumerator Load() { inputs[inputName1] = new Tensor(baseTexture); inputs[inputName2] = new Tensor(baseTexture); inputs[inputName3] = new Tensor(baseTexture); // Create input and execute model. yield return worker.StartManualSchedule(inputs); // Get outputs. for( int i = 2; i < model.outputs.Count; i++ ) { outputs[i] = worker.PeekOutput(model.outputs[i]); } // Get data from outputs. offset3D = outputs[2].data.Download(outputs[2].shape); heatMap3D = outputs[3].data.Download(outputs[3].shape); // Release outputs. for( int i = 2; i < outputs.Length; i++ ) { outputs[i].Dispose(); } PredictPose(); yield return new WaitForSeconds(waitTime); videoCapture.Initialize(inputImageSize, inputImageSize); loaded = true; } private void UpdateAvatar() { input = new Tensor(videoCapture.renderTexture); if( inputs[inputName1] == null ) { inputs[inputName1] = input; inputs[inputName2] = new Tensor(videoCapture.renderTexture); inputs[inputName3] = new Tensor(videoCapture.renderTexture); } else { inputs[inputName3].Dispose(); inputs[inputName2].Dispose(); inputs[inputName1].Dispose(); inputs[inputName3] = input; inputs[inputName2] = input; inputs[inputName1] = input; } StartCoroutine(ExecuteModel()); } private IEnumerator ExecuteModel() { // Create input and execute model. yield return worker.StartManualSchedule(inputs); // Get outputs. for( int i = 2; i < model.outputs.Count; i++ ) { outputs[i] = worker.PeekOutput(model.outputs[i]); } // Get data from outputs. offset3D = outputs[2].ToReadOnlyArray(); heatMap3D = outputs[3].ToReadOnlyArray(); // Release outputs. for ( int i = 2; i < outputs.Length; i++ ) { outputs[i].Dispose(); } PredictPose(); } private void PredictPose() { for( int j = 0; j < jointNum; j++ ) { int maxXIndex = 0; int maxYIndex = 0; int maxZIndex = 0; avatar.jointPoints[j].score3D = 0.0f; int jj = j * heatMapCol; for( int z = 0; z < heatMapCol; z++ ) { int zz = jj + z; for( int y = 0; y < heatMapCol; y++ ) { int yy = y * heatMapColSquared * jointNum + zz; for( int x = 0; x < heatMapCol; x++ ) { float v = heatMap3D[yy + x * heatMapColxJointNum]; if( v > avatar.jointPoints[j].score3D ) { avatar.jointPoints[j].score3D = v; maxXIndex = x; maxYIndex = y; maxZIndex = z; } } } } avatar.jointPoints[j].now3D.x = (offset3D[maxYIndex * cubeOffsetSquared + maxXIndex * cubeOffsetLinear + j * heatMapCol + maxZIndex] + 0.5f + (float)maxXIndex) * imageScale - inputImageHalfSize; avatar.jointPoints[j].now3D.y = inputImageHalfSize - (offset3D[maxYIndex * cubeOffsetSquared + maxXIndex * cubeOffsetLinear + (j + jointNum) * heatMapCol + maxZIndex] + 0.5f + (float)maxYIndex) * imageScale; avatar.jointPoints[j].now3D.z = (offset3D[maxYIndex * cubeOffsetSquared + maxXIndex * cubeOffsetLinear + (j + jointNumSquared) * heatMapCol + maxZIndex] + 0.5f + (float)(maxZIndex - 14)) * imageScale; } // Calculate hip location. Vector3 lc = (avatar.jointPoints[PositionIndex.RightThighBend.Int()].now3D + avatar.jointPoints[PositionIndex.LeftThighBend.Int()].now3D) / 2f; avatar.jointPoints[PositionIndex.Hip.Int()].now3D = (avatar.jointPoints[PositionIndex.AbdomenUpper.Int()].now3D + lc) / 2f; // Calculate neck location. avatar.jointPoints[PositionIndex.Neck.Int()].now3D = (avatar.jointPoints[PositionIndex.RightShoulderBend.Int()].now3D + avatar.jointPoints[PositionIndex.LeftShoulderBend.Int()].now3D) / 2f; // Calculate head location. Vector3 cEar = (avatar.jointPoints[PositionIndex.RightEar.Int()].now3D + avatar.jointPoints[PositionIndex.LeftEar.Int()].now3D) / 2f; Vector3 hv = cEar - avatar.jointPoints[PositionIndex.Neck.Int()].now3D; Vector3 nhv = Vector3.Normalize(hv); Vector3 nv = avatar.jointPoints[PositionIndex.Nose.Int()].now3D - avatar.jointPoints[PositionIndex.Neck.Int()].now3D; avatar.jointPoints[PositionIndex.Head.Int()].now3D = avatar.jointPoints[PositionIndex.Neck.Int()].now3D + nhv * Vector3.Dot(nhv, nv); // Calculate spine location. avatar.jointPoints[PositionIndex.Spine.Int()].now3D = avatar.jointPoints[PositionIndex.AbdomenUpper.Int()].now3D; // Kalman filter. foreach( JointPoint jp in avatar.jointPoints ) { KalmanFilter(jp); } // Low pass filter. if( useLowPassFilter ) { foreach( JointPoint jp in avatar.jointPoints ) { jp.prevPos3D[0] = jp.pos3D; for( int i = 1; i < jp.prevPos3D.Length; i++ ) { jp.prevPos3D[i] = jp.prevPos3D[i] * lowPassParam + jp.prevPos3D[i - 1] * (1f - lowPassParam); } jp.pos3D = jp.prevPos3D[jp.prevPos3D.Length - 1]; } } } private void KalmanFilter( JointPoint measurement ) { MeasurementUpdate(measurement); measurement.pos3D.x = measurement.x.x + (measurement.now3D.x - measurement.x.x) * measurement.k.x; measurement.pos3D.y = measurement.x.y + (measurement.now3D.y - measurement.x.y) * measurement.k.y; measurement.pos3D.z = measurement.x.z + (measurement.now3D.z - measurement.x.z) * measurement.k.z; measurement.x = measurement.pos3D; } private void MeasurementUpdate( JointPoint measurement ) { measurement.k.x = (measurement.p.x + kalmanParamQ) / (measurement.p.x + kalmanParamQ + kalmanParamR); measurement.k.y = (measurement.p.y + kalmanParamQ) / (measurement.p.y + kalmanParamQ + kalmanParamR); measurement.k.z = (measurement.p.z + kalmanParamQ) / (measurement.p.z + kalmanParamQ + kalmanParamR); measurement.p.x = kalmanParamR * (measurement.p.x + kalmanParamQ) / (kalmanParamR + measurement.p.x + kalmanParamQ); measurement.p.y = kalmanParamR * (measurement.p.y + kalmanParamQ) / (kalmanParamR + measurement.p.y + kalmanParamQ); measurement.p.z = kalmanParamR * (measurement.p.z + kalmanParamQ) / (kalmanParamR + measurement.p.z + kalmanParamQ); } private void OnDestroy() { worker?.Dispose(); // Assuming model with multiple inputs that were passed as a Dictionary foreach (var key in inputs.Keys) { inputs[key].Dispose(); } inputs.Clear(); } }