using UnityEngine; using System; using System.Collections; using System.Collections.Generic; using System.IO; using com.rfilkov.components; namespace com.rfilkov.kinect { /// /// Background removal manager is the component that filters and renders user body silhouettes. /// public class BackgroundRemovalManager : MonoBehaviour { [Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] public int sensorIndex = 0; [Tooltip("Index of the player, tracked by this component. -1 means all players, 0 - the 1st player only, 1 - the 2nd player only, etc.")] public int playerIndex = -1; [Tooltip("RawImage used for displaying the foreground image.")] public UnityEngine.UI.RawImage foregroundImage; [Tooltip("Camera used for alignment of bodies to color camera image.")] public Camera foregroundCamera; [Tooltip("Resolution of the generated foreground textures.")] private DepthSensorBase.PointCloudResolution foregroundImageResolution = DepthSensorBase.PointCloudResolution.ColorCameraResolution; [Tooltip("Whether only the alpha texture is needed.")] public bool computeAlphaMaskOnly = false; [Tooltip("Whether the alpha texture will be inverted or not..")] public bool invertAlphaMask = false; [Tooltip("(Advanced) Whether to apply the median filter before the other filters.")] public bool applyMedianFilter = false; [Tooltip("(Advanced) Number of iterations used by the alpha texture's erode filter 0.")] [Range(0, 9)] public int erodeIterations0 = 0; // 1 [Tooltip("(Advanced) Number of iterations used by the alpha texture's dilate filter 1.")] [Range(0, 9)] public int dilateIterations = 0; // 3; [Tooltip("(Advanced) Whether to apply the gradient filter.")] private bool applyGradientFilter = true; [Tooltip("(Advanced) Number of iterations used by the alpha texture's erode filter 2.")] [Range(0, 9)] public int erodeIterations = 0; // 4; [Tooltip("(Advanced) Whether to apply the blur filter after at the end.")] public bool applyBlurFilter = true; [Tooltip("(Advanced) Color applied to the body contour after the filters.")] public Color bodyContourColor = Color.black; [Tooltip("UI-Text to display the BR-Manager debug messages.")] public UnityEngine.UI.Text debugText; // max number of bodies to track private const int MAX_BODY_COUNT = 10; // primary sensor data structure private KinectInterop.SensorData sensorData = null; private KinectManager kinectManager = null; // sensor interface private DepthSensorBase sensorInt = null; // render texture resolution private Vector2Int textureRes; // Bool to keep track whether Kinect and BR library have been initialized private bool bBackgroundRemovalInited = false; private int lastColorW = 0, lastColorH = 0; // The single instance of BackgroundRemovalManager //private static BackgroundRemovalManager instance; // last point cloud frame time private ulong lastDepth2SpaceFrameTime = 0; private ulong lastColorBodyIndexBufferTime = 0; // render textures used by the shaders private RenderTexture colorTexture = null; private RenderTexture vertexTexture = null; private RenderTexture alphaTexture = null; private RenderTexture foregroundTexture = null; // Materials used to apply the shaders private Material medianFilterMat = null; private Material erodeFilterMat = null; private Material dilateFilterMat = null; private Material gradientFilterMat = null; private Material blurFilterMat = null; private Material invertAlphaMat = null; private Material foregroundMat = null; // reference to filter-by-distance component private BackgroundRemovalByBodyBounds filterByBody = null; private BackgroundRemovalByDist filterByDist = null; private BackgroundRemovalByBodyIndex filterByBI = null; private BackgroundRemovalByGreenScreen filterByGS = null; // whether the textures are created or not private bool bColorTextureCreated = false; private bool bVertexTextureCreated = false; ///// ///// Gets the single BackgroundRemovalManager instance. ///// ///// The BackgroundRemovalManager instance. //public static BackgroundRemovalManager Instance //{ // get // { // return instance; // } //} /// /// Determines whether the BackgroundRemovalManager was successfully initialized. /// /// true if the BackgroundRemovalManager was successfully initialized; otherwise, false. public bool IsBackgroundRemovalInited() { return bBackgroundRemovalInited; } /// /// Gets the foreground image texture. /// /// The foreground image texture. public Texture GetForegroundTex() { return foregroundTexture; } /// /// Gets the alpha texture. /// /// The alpha texture. public Texture GetAlphaTex() { return alphaTexture; } /// /// Gets the color texture. /// /// The color texture. public Texture GetColorTex() { return colorTexture; } /// /// Gets the last background removal frame time. /// /// The last background removal time. public ulong GetLastBackgroundRemovalTime() { return lastDepth2SpaceFrameTime; } //----------------------------------- end of public functions --------------------------------------// //void Awake() //{ // instance = this; //} public void Start() { try { // get sensor data kinectManager = KinectManager.Instance; if (kinectManager && kinectManager.IsInitialized()) { sensorData = kinectManager.GetSensorData(sensorIndex); } if (sensorData == null || sensorData.sensorInterface == null) { throw new Exception("Background removal cannot be started, because KinectManager is missing or not initialized."); } if(foregroundImage == null) { // look for a foreground image foregroundImage = GetComponent(); } if (!foregroundCamera) { // by default - the main camera foregroundCamera = Camera.main; } // try to get reference to other filter components filterByBody = GetComponent(); if(filterByBody == null) filterByDist = GetComponent(); if (filterByBody == null && filterByDist == null) filterByBI = GetComponent(); if (filterByBody == null && filterByDist == null && filterByBI == null) filterByGS = GetComponent(); if (filterByBody == null && filterByDist == null && filterByBI == null && filterByGS == null) filterByBI = gameObject.AddComponent(); // fallback //// Initialize the background removal //bBackgroundRemovalInited = InitBackgroundRemoval(sensorData); //if (bBackgroundRemovalInited) //{ // if (debugText != null) // debugText.text = string.Empty; //} //else //{ // throw new Exception("Background removal could not be initialized."); //} //bBackgroundRemovalInited = bSuccess; } catch (DllNotFoundException ex) { Debug.LogError(ex.ToString()); if (debugText != null) debugText.text = "Please check the SDK installations."; } catch (Exception ex) { Debug.LogException(ex); if (debugText != null) debugText.text = ex.Message; } } public void OnDestroy() { if (bBackgroundRemovalInited) { // finish background removal FinishBackgroundRemoval(sensorData); } bBackgroundRemovalInited = false; //instance = null; } void Update() { if (sensorData == null) return; if(!bBackgroundRemovalInited || lastColorW != sensorData.colorImageWidth || lastColorH != sensorData.colorImageHeight) { lastColorW = sensorData.colorImageWidth; lastColorH = sensorData.colorImageHeight; if (bBackgroundRemovalInited) { FinishBackgroundRemoval(sensorData); if(foregroundImage != null) foregroundImage.texture = null; //bBackgroundRemovalInited = false; // dispose the used shaders & buffers, as well if(sensorInt != null) sensorInt.UpdateTransformedFrameTextures(sensorData, kinectManager); } bBackgroundRemovalInited = InitBackgroundRemoval(sensorData); if (bBackgroundRemovalInited) { if (debugText != null) debugText.text = string.Empty; } } if (bBackgroundRemovalInited) { // update the background removal UpdateBackgroundRemoval(sensorData); // check for valid foreground image texture if(foregroundImage != null && foregroundImage.texture == null) { foregroundImage.texture = foregroundTexture; foregroundImage.rectTransform.localScale = kinectManager.GetColorImageScale(sensorIndex); foregroundImage.color = Color.white; } } } // initializes background removal with shaders private bool InitBackgroundRemoval(KinectInterop.SensorData sensorData) { if (sensorData != null && sensorData.sensorInterface != null && KinectInterop.IsDirectX11Available()) { if(filterByBody != null) { if (!filterByBody.InitBackgroundRemoval(sensorData, MAX_BODY_COUNT)) { Debug.LogError("Could not init the background removal by body bounds!"); return false; } } else if(filterByBI != null) { if(!filterByBI.InitBackgroundRemoval(sensorData)) { Debug.LogError("Could not init the background removal by body index!"); return false; } } else if (filterByGS != null) { if (!filterByGS.InitBackgroundRemoval(sensorData)) { Debug.LogError("Could not init the background removal by green screen!"); return false; } } sensorInt = (DepthSensorBase)sensorData.sensorInterface; // set the texture resolution if (sensorInt.pointCloudColorTexture == null && sensorInt.pointCloudVertexTexture == null) { sensorInt.pointCloudResolution = foregroundImageResolution; } textureRes = sensorInt.GetPointCloudTexResolution(sensorData); if(sensorInt.pointCloudColorTexture == null) { colorTexture = KinectInterop.CreateRenderTexture(colorTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); sensorInt.pointCloudColorTexture = colorTexture; bColorTextureCreated = true; } else { colorTexture = sensorInt.pointCloudColorTexture; bColorTextureCreated = false; } if (filterByBody != null || filterByDist != null) { if(sensorInt.pointCloudVertexTexture == null) { vertexTexture = KinectInterop.CreateRenderTexture(vertexTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGBHalf); sensorInt.pointCloudVertexTexture = vertexTexture; bVertexTextureCreated = true; } else { vertexTexture = sensorInt.pointCloudVertexTexture; bVertexTextureCreated = false; } } alphaTexture = KinectInterop.CreateRenderTexture(alphaTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); foregroundTexture = KinectInterop.CreateRenderTexture(foregroundTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); Shader erodeShader = Shader.Find("Kinect/ErodeShader"); erodeFilterMat = new Material(erodeShader); erodeFilterMat.SetFloat("_TexResX", (float)textureRes.x); erodeFilterMat.SetFloat("_TexResY", (float)textureRes.y); //sensorData.erodeBodyMaterial.SetTexture("_MainTex", sensorData.bodyIndexTexture); Shader dilateShader = Shader.Find("Kinect/DilateShader"); dilateFilterMat = new Material(dilateShader); dilateFilterMat.SetFloat("_TexResX", (float)textureRes.x); dilateFilterMat.SetFloat("_TexResY", (float)textureRes.y); //sensorData.dilateBodyMaterial.SetTexture("_MainTex", sensorData.bodyIndexTexture); Shader gradientShader = Shader.Find("Kinect/GradientShader"); gradientFilterMat = new Material(gradientShader); Shader medianShader = Shader.Find("Kinect/MedianShader"); medianFilterMat = new Material(medianShader); //sensorData.medianBodyMaterial.SetFloat("_Amount", 1.0f); Shader blurShader = Shader.Find("Kinect/BlurShader"); blurFilterMat = new Material(blurShader); Shader invertShader = Shader.Find("Kinect/InvertShader"); invertAlphaMat = new Material(invertShader); Shader foregroundShader = Shader.Find("Kinect/ForegroundShader"); foregroundMat = new Material(foregroundShader); return true; } return false; } // releases background removal shader resources private void FinishBackgroundRemoval(KinectInterop.SensorData sensorData) { if(filterByBody != null) { filterByBody.FinishBackgroundRemoval(sensorData); } else if(filterByBI != null) { filterByBI.FinishBackgroundRemoval(sensorData); } if (sensorInt) { sensorInt.pointCloudColorTexture = null; sensorInt.pointCloudVertexTexture = null; } if (bColorTextureCreated && colorTexture) { colorTexture.Release(); colorTexture = null; } if (bVertexTextureCreated && vertexTexture) { vertexTexture.Release(); vertexTexture = null; } if (alphaTexture) { alphaTexture.Release(); alphaTexture = null; } if(foregroundTexture) { foregroundTexture.Release(); foregroundTexture = null; } erodeFilterMat = null; dilateFilterMat = null; medianFilterMat = null; blurFilterMat = null; invertAlphaMat = null; foregroundMat = null; } // computes current background removal texture private bool UpdateBackgroundRemoval(KinectInterop.SensorData sensorData) { if (bBackgroundRemovalInited && (lastDepth2SpaceFrameTime != sensorData.lastDepth2SpaceFrameTime || lastColorBodyIndexBufferTime != sensorData.lastColorBodyIndexBufferTime || filterByGS != null)) { lastDepth2SpaceFrameTime = sensorData.lastDepth2SpaceFrameTime; lastColorBodyIndexBufferTime = sensorData.lastColorBodyIndexBufferTime; //Debug.Log("BR Depth2SpaceFrameTime: " + lastDepth2SpaceFrameTime + " ColorBodyIndexBufferTime: " + lastColorBodyIndexBufferTime); RenderTexture[] tempTextures = new RenderTexture[2]; tempTextures[0] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); tempTextures[1] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); RenderTexture[] tempGradTextures = null; if (applyGradientFilter) { tempGradTextures = new RenderTexture[2]; tempGradTextures[0] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); tempGradTextures[1] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); } // filter if(filterByBody != null && sensorInt != null) { filterByBody.ApplyForegroundFilterByBody(vertexTexture, alphaTexture, playerIndex, sensorIndex, MAX_BODY_COUNT, sensorInt.GetSensorToWorldMatrix(), kinectManager, foregroundCamera); } else if(filterByDist != null && sensorInt != null) { // filter by distance filterByDist.ApplyVertexFilter(vertexTexture, alphaTexture, sensorInt.GetSensorToWorldMatrix()); } else if(filterByBI != null) { // filter by body index filterByBI.ApplyForegroundFilterByBodyIndex(alphaTexture, sensorData, kinectManager, playerIndex, MAX_BODY_COUNT); } else if (filterByGS != null) { // filter by green screen filterByGS.ApplyForegroundFilterByGreenScreen(alphaTexture, sensorData, kinectManager, colorTexture); } //if(filterByBI == null) //{ // Graphics.Blit(vertexTexture, alphaTexture); //} // median if (applyMedianFilter) { ApplySimpleFilter(alphaTexture, alphaTexture, medianFilterMat, tempTextures); } //else //{ // Graphics.Blit(vertexTexture, alphaTexture); //} // erode0 ApplyIterableFilter(alphaTexture, alphaTexture, erodeFilterMat, erodeIterations0, tempTextures); if(applyGradientFilter) { Graphics.CopyTexture(alphaTexture, tempGradTextures[0]); } // dilate ApplyIterableFilter(alphaTexture, alphaTexture, dilateFilterMat, dilateIterations, tempTextures); if (applyGradientFilter) { //Graphics.Blit(alphaTexture, tempGradTextures[1]); gradientFilterMat.SetTexture("_ErodeTex", tempGradTextures[0]); ApplySimpleFilter(alphaTexture, tempGradTextures[1], gradientFilterMat, tempTextures); } // erode ApplyIterableFilter(alphaTexture, alphaTexture, erodeFilterMat, erodeIterations, tempTextures); if (tempGradTextures != null) { Graphics.Blit(alphaTexture, tempGradTextures[0]); } // blur if(applyBlurFilter) { ApplySimpleFilter(alphaTexture, alphaTexture, blurFilterMat, tempTextures); } if(invertAlphaMask) { ApplySimpleFilter(alphaTexture, alphaTexture, invertAlphaMat, tempTextures); } if(!computeAlphaMaskOnly) { foregroundMat.SetTexture("_ColorTex", colorTexture); foregroundMat.SetTexture("_GradientTex", tempGradTextures[1]); Color gradientColor = (erodeIterations0 != 0 || dilateIterations != 0 || erodeIterations != 0) ? bodyContourColor : Color.clear; foregroundMat.SetColor("_GradientColor", gradientColor); ApplySimpleFilter(alphaTexture, foregroundTexture, foregroundMat, tempTextures); } else { Graphics.CopyTexture(alphaTexture, foregroundTexture); } // cleanup if (tempGradTextures != null) { RenderTexture.ReleaseTemporary(tempGradTextures[0]); RenderTexture.ReleaseTemporary(tempGradTextures[1]); } RenderTexture.ReleaseTemporary(tempTextures[0]); RenderTexture.ReleaseTemporary(tempTextures[1]); //sensorData.usedColorBodyIndexBufferTime = sensorData.lastColorBodyIndexBufferTime; } return true; } // applies iterable filter to the source texture private void ApplyIterableFilter(RenderTexture source, RenderTexture destination, Material filterMat, int numIterations, RenderTexture[] tempTextures) { if (!source || !destination || !filterMat || numIterations == 0) return; Graphics.Blit(source, tempTextures[0]); for (int i = 0; i < numIterations; i++) { Graphics.Blit(tempTextures[i % 2], tempTextures[(i + 1) % 2], filterMat); } if ((numIterations % 2) == 0) { Graphics.Blit(tempTextures[0], destination); } else { Graphics.Blit(tempTextures[1], destination); } } // applies simple filter to the source texture private void ApplySimpleFilter(RenderTexture source, RenderTexture destination, Material filterMat, RenderTexture[] tempTextures) { if (!source || !destination || !filterMat) return; Graphics.Blit(source, tempTextures[0], filterMat); Graphics.Blit(tempTextures[0], destination); } } }