using System; using UnityEngine; using UI = UnityEngine.UI; using Klak.TestTools; using System.Linq; using System.Collections.Generic; using Zenject; using BodyPix; namespace UltraFace { public sealed class Visualizer : MonoBehaviour { #region Editable attributes [SerializeField] bool _useBodyTracking; [SerializeField] ImageSource _source = null; [SerializeField, Range(0, 1)] float _threshold = 0.5f; [SerializeField] ResourceSet _faceResources = null; [SerializeField] Shader _visualizer = null; [SerializeField] Texture2D _texture = null; [SerializeField] UI.RawImage _facePreviewUI = null; FaceDetector _faceDetector; Material _faceMaterial; ComputeBuffer _drawArgs; [Space(16)] [SerializeField] Shader _bodyShader = null; [SerializeField] RenderTexture _background = null; [SerializeField] BodyPix.ResourceSet _bodyResources; [SerializeField] UI.RawImage _bodyPreviewUI = null; [SerializeField] float _bodyThreshold; BodyDetector _bodyDetector; Material _bodyMaterial; [Inject] private DetectionSetup _detectionSetup; public int faceCounter => _faceDetector.Detections.Count(); public float minFaceHeight { get { if(_faceDetector.Detections.Count() == 0) return 0f; else return _faceDetector.Detections.Min(face => face.y2 - face.y1); } } public float maxFaceHeight { get { if(_faceDetector.Detections.Count() == 0) return 0f; else return _faceDetector.Detections.Max(face => face.y2 - face.y1); } } public Action OnDetectionStatusChanged; private List _detectedCounter; private bool _isDetected; #endregion #region MonoBehaviour implementation void Start() { _faceDetector = new FaceDetector(_faceResources); _faceMaterial = new Material(_visualizer); _drawArgs = new ComputeBuffer(4, sizeof(uint), ComputeBufferType.IndirectArguments); _drawArgs.SetData(new [] {6, 0, 0, 0}); _detectedCounter = new List(); _bodyDetector = new BodyDetector(_bodyResources, 320, 240); _bodyMaterial = new Material(_bodyShader); _bodyPreviewUI.material = _bodyMaterial; if(!_useBodyTracking) { _facePreviewUI.gameObject.SetActive(true); _bodyPreviewUI.gameObject.SetActive(false); } } public void Init() { OnDetectionStatusChanged?.Invoke(false); } void Update() { if(_useBodyTracking) { _bodyDetector.ProcessImage(_source.Texture); _bodyMaterial.SetTexture("_BgTexture", _background); _bodyMaterial.SetTexture("_CameraTexture", _source.Texture); _bodyMaterial.SetTexture("_MaskTexture", _bodyDetector.MaskTexture); _bodyMaterial.SetFloat("_Threshold", _bodyThreshold); _bodyPreviewUI.texture = _source.Texture; } _faceDetector.ProcessImage(_source.Texture, _threshold); _facePreviewUI.texture = _source.Texture; if(!_detectionSetup.isValidate) return; _detectedCounter.Add(maxFaceHeight > _detectionSetup.minFaceHeight ? true : false); if(_detectedCounter.Count == 128) { var trueCounter = _detectedCounter.Count(x => x == true); if(!_isDetected && trueCounter >= 64) { if(maxFaceHeight < _detectionSetup.minFaceHeight) return; _isDetected = true; OnDetectionStatusChanged?.Invoke(_isDetected); Debug.LogWarning("лицо определено"); } else if(_isDetected && trueCounter < 64) { _isDetected = false; OnDetectionStatusChanged?.Invoke(_isDetected); Debug.LogWarning("лицо потеряно"); } _detectedCounter = new List(); } if(Input.GetKeyDown(KeyCode.Space)) OnDetectionStatusChanged?.Invoke(!_isDetected); } void OnRenderObject() { return; _faceDetector.SetIndirectDrawCount(_drawArgs); _faceMaterial.SetFloat("_Threshold", _threshold); _faceMaterial.SetTexture("_Texture", _texture); _faceMaterial.SetBuffer("_Detections", _faceDetector.DetectionBuffer); _faceMaterial.SetPass(_texture == null ? 0 : 1); Graphics.DrawProceduralIndirectNow(MeshTopology.Triangles, _drawArgs, 0); } void OnDestroy() { _faceDetector?.Dispose(); Destroy(_faceMaterial); _drawArgs?.Dispose(); _bodyDetector?.Dispose(); _bodyDetector = null; Destroy(_bodyMaterial); _bodyMaterial = null; } #endregion } }