Kinect 脸部追踪 转UGUI坐标 不同分辨率适配思路

插眼:2020/11/14 16:08 更新: 毛边处理 , 追踪物体使用插值运动代码

代码在后面

2020/11/14 13:07 插眼 :毛边处理 , 追踪物体使用插值运动需要处理

效果预览: 

1080*673 显示

1920*1080 显示:

磨刀不误砍材工

1. 新建一个物体,挂载FaceUGUIPos脚本,会自动帮你添加KinectManager和FacetrackingManager脚本

2.Camera 设置为透视图

3. 场景预览图,我的分辨率是1080*1920

Gif图:

4.需要注意的是 我是以左上为(0,0)坐标计算的,所以你的锚点也应该是这样的

5.

6.FaceUGUIPos脚本设置

插眼:2020/11/14 16:08

更新: 毛边处理 , 追踪物体使用插值运动代码

using OpenCVForUnity.CoreModule;
using OpenCVForUnity.ImgprocModule;
using OpenCVForUnity.UnityUtils;
using UnityEngine;
using UnityEngine.UI;
namespace QFramework.Example
{
    [RequireComponent(typeof(KinectManager))]
    [RequireComponent(typeof(FacetrackingManager))]
    public class FaceUGUIPos : MonoBehaviour
    {
        public Vector2 _vNewPos;

        public RawImage UserTexRawImage;  // 用户图显示
        public Vector2 m_vUserTexRawImageSize = new Vector2(1080f, 673f); // 用户图显示大小
        public RawImage m_FaceTrackRaw; // 覆盖脸部显示的图 
        public Vector2 m_vFaceTrackRawSize = new Vector2(100f, 100f); // 覆盖脸部显示的图 大小
        public Vector2 m_vFaceRectDepthMinMax = new Vector2(170f, 1700f);
        public float m_fSpeed = 20f; // 移动速度 防止抖动太厉害

        private RectTransform m_FaceTrackRawRect;
        private KinectManager _manager; // KinectManager 
        private FacetrackingManager _facetrackingManager;
        // Start is called before the first frame update
        private void Start()
        {
            _manager = KinectManager.Instance;
            _facetrackingManager = FacetrackingManager.Instance;

            // 以下参数需要在 面板上设置 重要的事说三遍
            // 以下参数需要在 面板上设置 重要的事说三遍
            // 以下参数需要在 面板上设置 重要的事说三遍
            //_manager.computeUserMap = KinectManager.UserMapType.CutOutTexture;
            //_manager.computeColorMap = true;
            //_manager.maxTrackedUsers = 1;
            //_facetrackingManager.foregroundCamera = Camera.main;
            是否显示 脸部 方框
            //_facetrackingManager.displayFaceRect = true;
            //_facetrackingManager.faceTrackingTolerance = 0.1f;

            _vNewPos = new Vector2();
            m_FaceTrackRawRect = m_FaceTrackRaw.GetComponent<RectTransform>();
            UserTexRawImage.GetComponent<RectTransform>().sizeDelta = m_vUserTexRawImageSize;
            m_FaceTrackRaw.GetComponent<RectTransform>().sizeDelta = m_vFaceTrackRawSize;

            InitGetBodyColorMat();


        }


        // Update is called once per frame
        private void Update()
        {
           

            if (_manager && _manager.IsInitialized() && _manager.IsUserDetected())
            {

                //如果只需要扣人的身体原图 请 return,不要运行GetComicMat(),否则算法会延迟;
                UserTexRawImage.texture = GetBodyColorMat();
                // 未经过Opencv处理过的抠图
                //UserTexRawImage.texture = _manager.GetUsersLblTex();

                GetFacePos();

            }
        }

        /// <summary>
        /// Face坐标转换
        /// </summary>
        private void GetFacePos()
        {
            long item = _manager.GetPrimaryUserID();
            // 追踪当前用户骨骼
            if (_facetrackingManager.IsTrackingFace(item))
            {
                UnityEngine.Rect rect = _facetrackingManager.GetFaceColorRect(item);
                Debug.Log(rect.x + " " + rect.y);

                 求出脸部中心坐标
                //_vNewPos.x = rect.x + rect.width / 2;
                //_vNewPos.y = rect.y + rect.height / 2;

                // map映射 将 Kinect深度摄像头坐标和 彩色摄像头坐标映射
                // 200f,1700f 要实际看你的rect.x到屏幕左右边缘的位置,1920是深度摄像头的分辨率.x
                _vNewPos.x = ValueMap(rect.x, m_vFaceRectDepthMinMax.x, m_vFaceRectDepthMinMax.y, 0f, 1920f);
                _vNewPos.y = rect.y;
                Debug.Log(" -----------" + _vNewPos);

                // kinect 彩色分辨率是1920*1080的, 1080 * 673 是你的显示的区域大小,自己更改
                _vNewPos.x = (m_vUserTexRawImageSize.x * _vNewPos.x / 1920) + m_vFaceTrackRawSize.x / 2;
                _vNewPos.y = -(m_vUserTexRawImageSize.y * rect.y / 1080) - m_vFaceTrackRawSize.y / 2;
                //m_FaceTrackRawRect.anchoredPosition = _vNewPos;
                m_FaceTrackRawRect.anchoredPosition = Vector2.Lerp(m_FaceTrackRawRect.anchoredPosition,
                            _vNewPos, m_fSpeed * Time.deltaTime);

                // 需要追踪多物体的时候使用的示例
                //if (GameObject.Find(item.ToString()) != null)
                //{
                //    Transform tran = GameObject.Find(item.ToString()).transform;
                //    tran.localPosition = Vector3.Lerp(tran.localPosition,
                //        new Vector3(rect.x - 1020, 480 - rect.y, -1200f), speed * Time.deltaTime);
                //}
            }
        }



        private int kinectDepthWidth = 512;
        private int kinectDepthHeight = 424;
        private Mat rgbaMat;
        private Mat lineMat2;
        private Mat bodyColorMat;
        public Texture2D BodyColorTex;

        /// <summary>
        /// 初始化OpenCV数据
        /// </summary>
        private void InitGetBodyColorMat()
        {
            rgbaMat = new Mat(kinectDepthHeight, kinectDepthWidth, CvType.CV_8UC4);
            lineMat2 = new Mat(kinectDepthHeight, kinectDepthWidth, CvType.CV_8UC1);
            bodyColorMat = new Mat(kinectDepthHeight, kinectDepthWidth, CvType.CV_8UC4);
            BodyColorTex = new Texture2D(kinectDepthWidth, kinectDepthHeight, TextureFormat.RGBA32, false);
        }

        /// <summary>
        /// 只显示人的颜色图
        /// </summary>
        /// <returns></returns>
        private Texture2D GetBodyColorMat()
        {

            // bodyTexture  转 mat GetUsersClrTex(); GetUsersLblTex();
            Utils.texture2DToMat(_manager.GetUsersLblTex(), rgbaMat);

            // ----------------对深度图降噪-------------
            Imgproc.medianBlur(rgbaMat, lineMat2, 13); // 中值过滤
            Imgproc.Canny(lineMat2, lineMat2, 20, 120); // 线平滑
            Core.bitwise_not(lineMat2, lineMat2); // 反转
            for (int i = 0; i < lineMat2.cols(); i++)
            {
                for (int j = 0; j < lineMat2.rows(); j++)
                {
                    //这个150是阈值,你可以自己定义来试试效果
                    if (lineMat2.get(j, i)[0] > 250)
                    {
                        // 根据 mask 图 对原图相应位置的点设置
                        rgbaMat.put(j, i, 255, 255, 255, 0);
                    }
                }
            }
            rgbaMat.copyTo(bodyColorMat);
            Utils.matToTexture2D(bodyColorMat, BodyColorTex);
            return BodyColorTex;
        }

        /// <summary>
        /// 在给定数据类型的限制内,将一个值从一个任意范围映射到另一个范围
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private float ValueMap(float input, float inputMin, float inputMax, float outputMin, float outputMax)
        {
            //通常,您可以使用以下公式将值input从一个范围[inputMin, inputMax]转换为另一个范围[outputMin, outputMax]:

            float output;
            // 因为 C# 的特性,所有要使用 Float数值 不然会给你整除掉
            //output = (value - 200f) * ((1920f - 0f) / (1700f - 200f)) + 0f;
            output = (input - inputMin) * ((outputMax - outputMin) / (inputMax - inputMin)) + outputMin;
            return output;
        }
    }
}

2020/11/14 15:07 代码:

using UnityEngine;
using UnityEngine.UI;
namespace QFramework.Example
{
    [RequireComponent(typeof(KinectManager))]
    [RequireComponent(typeof(FacetrackingManager))]
    public class FaceUGUIPos : MonoBehaviour
    {
        public Vector2 _vNewPos;

        public RawImage UserTexRawImage;  // 用户图显示
        public Vector2 m_vUserTexRawImageSize = new Vector2(1080f, 673f); // 用户图显示大小
        public RawImage m_FaceTrackRaw; // 覆盖脸部显示的图 
        public Vector2 m_vFaceTrackRawSize = new Vector2(100f, 100f); // 覆盖脸部显示的图 大小

        public Vector2 m_vFaceRectDepthMinMax = new Vector2(170f, 1700f);

        private KinectManager _manager; // KinectManager 
        private FacetrackingManager _facetrackingManager;
        // Start is called before the first frame update
        private void Start()
        {
            _manager = KinectManager.Instance;
            _facetrackingManager = FacetrackingManager.Instance;

            // 以下参数需要在 面板上设置 重要的事说三遍
            // 以下参数需要在 面板上设置 重要的事说三遍
            // 以下参数需要在 面板上设置 重要的事说三遍
            //_manager.computeUserMap = KinectManager.UserMapType.CutOutTexture;
            //_manager.computeColorMap = true;
            //_manager.maxTrackedUsers = 1;
            //_facetrackingManager.foregroundCamera = Camera.main;
            是否显示 脸部 方框
            //_facetrackingManager.displayFaceRect = true;
            //_facetrackingManager.faceTrackingTolerance = 0.1f;

            _vNewPos = new Vector2();

            UserTexRawImage.GetComponent<RectTransform>().sizeDelta = m_vUserTexRawImageSize;
            m_FaceTrackRaw.GetComponent<RectTransform>().sizeDelta = m_vFaceTrackRawSize;

        }

        // Update is called once per frame
        private void Update()
        {
            UserTexRawImage.texture = _manager.GetUsersLblTex();

            if (_manager && _manager.IsInitialized() && _manager.IsUserDetected())
            {
                long item = _manager.GetPrimaryUserID();
                // 追踪当前用户骨骼
                if (_facetrackingManager.IsTrackingFace(item))
                {
                    Rect rect = _facetrackingManager.GetFaceColorRect(item);
                    Debug.Log(rect.x + " " + rect.y);

                     求出脸部中心坐标
                    //_vNewPos.x = rect.x + rect.width / 2;
                    //_vNewPos.y = rect.y + rect.height / 2;

                    // map映射 将 Kinect深度摄像头坐标和 彩色摄像头坐标映射
                    // 200f,1700f 要实际看你的rect.x到屏幕左右边缘的位置,1920是深度摄像头的分辨率.x
                    _vNewPos.x = ValueMap(rect.x, m_vFaceRectDepthMinMax.x, m_vFaceRectDepthMinMax.y, 0f, 1920f);
                    _vNewPos.y = rect.y;
                    Debug.Log(" -----------" + _vNewPos);

                    // kinect 彩色分辨率是1920*1080的, 1080 * 673 是你的显示的区域大小,自己更改
                    _vNewPos.x = (m_vUserTexRawImageSize.x * _vNewPos.x / 1920) + m_vFaceTrackRawSize.x / 2;
                    _vNewPos.y = -(m_vUserTexRawImageSize.y * rect.y / 1080) - m_vFaceTrackRawSize.y / 2;
                    m_FaceTrackRaw.GetComponent<RectTransform>().anchoredPosition = _vNewPos;

                    // 需要追踪多物体的时候使用的示例
                    //if (GameObject.Find(item.ToString()) != null)
                    //{
                    //    Transform tran = GameObject.Find(item.ToString()).transform;
                    //    tran.localPosition = Vector3.Lerp(tran.localPosition,
                    //        new Vector3(rect.x - 1020, 480 - rect.y, -1200f), speed * Time.deltaTime);
                    //}
                }

            }
        }


        /// <summary>
        /// 在给定数据类型的限制内,将一个值从一个任意范围映射到另一个范围
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private float ValueMap(float input, float inputMin, float inputMax, float outputMin, float outputMax)
        {
            //通常,您可以使用以下公式将值input从一个范围[inputMin, inputMax]转换为另一个范围[outputMin, outputMax]:

            float output;
            // 因为 C# 的特性,所有要使用 Float数值 不然会给你整除掉
            //output = (value - 200f) * ((1920f - 0f) / (1700f - 200f)) + 0f;
            output = (input - inputMin) * ((outputMax - outputMin) / (inputMax - inputMin)) + outputMin;
            return output;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_39097425/article/details/109690784