包含有
Imgcodecs.imread-------------------------------获取外部图片【不可以有中文路径】
Utils.texture2DToMat----------------------------T2d转为Mat
Utils.matToTexture2D---------------------------Mat转为T2d
Imgproc.resize------------------------------------重定义尺寸
Imgproc.cvtColor --------------------------------转化色彩空间为灰度图
Imgproc.threshold--------------------------------把图片二值化
Imgproc.findContours---------------------------查找所有轮廓
Imgproc.contourArea----------------------------获取到轮廓的大小
Imgproc.drawContours--------------------------绘制出轮廓
Imgproc.rectangle--------------------------------画出轮廓大矩形
Imgproc.circle -------------------------------------画一个圈
Imgproc.minAreaRect----------------------------获取轮廓旋转矩形
Imgproc.line-----------------------------------------画线
backgroundSubstractorMOG2.apply---------MOG2
Utils.getFilePath-----------------------------------查询StreamingAssets目录下对应文件
Imgproc.getPerspectiveTransform------------由四对点计算透射变换
Imgproc.warpPerspective -----------------------对图像进行透视变换
ConvertScreenPointToTexturePoint ----------把鼠标坐标转换为Mat坐标
```csharp
//*********************❤*********************
//
// 文件名(File Name): 总管理.cs
//
// 作者(Author): LoveNeon
//
// 创建时间(CreateTime):
//
// 说明(Description): 借鉴链接
// https://www.jianshu.com/p/ef47d03711c7 图像中的轮廓识别
//
//*********************❤*********************
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using OpenCVForUnity;
using OpenCVForUnity.ImgcodecsModule;
using OpenCVForUnity.ImgprocModule;
using OpenCVForUnity.CoreModule;
using OpenCVForUnity.UnityUtils;
using LoveNeon;
using OpenCVForUnity.VideoioModule;
using OpenCVForUnity.BgsegmModule;
using OpenCVForUnity.VideoModule;
using OpenCVForUnity.UnityUtils.Helper;
using OpenCVForUnityExample;
public class 总管理 : MonoBehaviour {
//抠图
public Texture2D m_t2d;
public RawImage m_img;
public int alpha = 150;
//轮廓识别
public Texture2D m_t2d2;
public RawImage m_img2;
public RawImage m_img3;
//运动追踪
int width = 768;
int height = 576;
Mat rgbMat;
Mat fgmaskMat;
BackgroundSubtractorMOG2 backgroundSubstractorMOG2;
public Texture2D m_t2d3;
public RawImage m_img4;
//差异点
public WebCamTextureToMatHelper webCamTextureToMatHelper;
Texture2D texture;
FpsMonitor fpsMonitor;
Vector2 m_beginPos;
Vector2 m_endPos;
Vector2 m_nowPos;
public static 总管理 my;
// Use this for initialization
void Awake()
{
if (my == null)
{
my = this;
}
}
void Start () {
//m_img.texture = LoveNeon_ImageManage.GetTexture2DByPath(Application.streamingAssetsPath + "/test.png");
//rgbMat = new Mat(width, height, CvType.CV_8UC3);
//fgmaskMat = new Mat(width, height, CvType.CV_8UC1);
//m_t2d3 = new Texture2D(width, height, TextureFormat.RGBA32, false);
//
//backgroundSubstractorMOG2 = Video.createBackgroundSubtractorMOG2();
//capture = new VideoCapture(Application.streamingAssetsPath + "/768x576_mjpeg.mjpeg");
//
//
//if (!capture.isOpened())
//{
// Debug.Log("没打开 退出");
//}
//else
//{
// capture.read(rgbMat);
// Utils.fastMatToTexture2D(rgbMat, m_t2d3);
// m_img4.texture = m_t2d3;
//}
//差异点 初始化
fpsMonitor = GetComponent<FpsMonitor>();
webCamTextureToMatHelper = gameObject.GetComponent<WebCamTextureToMatHelper>();
#if UNITY_ANDROID && !UNITY_EDITOR
// Avoids the front camera low light issue that occurs in only some Android devices (e.g. Google Pixel, Pixel2).
webCamTextureToMatHelper.avoidAndroidFrontCameraLowLightIssue = true;
#endif
webCamTextureToMatHelper.Initialize();
backgroundSubstractorMOG2 = Video.createBackgroundSubtractorMOG2();
}
// Update is called once per frame
void Update () {
if (Input.GetKeyDown(KeyCode.A))
{
抠图();
}
if (Input.GetKeyDown(KeyCode.B))
{
轮廓识别();
}
if (Input.GetKeyDown(KeyCode.C))
{
仿射变换();
}
if (Input.GetMouseButtonDown(0))
{
Point storedTouchPoint = ConvertScreenPointToTexturePoint(Input.mousePosition, m_img.GetComponent<RectTransform>(), 476, 462);
Mat src = new Mat(new Size(m_t2d.width, m_t2d.height), CvType.CV_8UC3);
Utils.texture2DToMat(m_t2d, src);
Debug.Log(storedTouchPoint);
Imgproc.circle(src, storedTouchPoint, 5, new Scalar(0, 255, 0), -1);
// 定义一个 Texture2D 对象
Texture2D tex = new Texture2D(src.cols(), src.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(src, tex);
// 将处理后的图片现实在 RawImage 组件上
m_img.texture = tex;
}
//运动追踪();
差异点获取_MOG2();
if (m_nowPos == Vector2.zero)
{
if (m_beginPos.x < m_endPos.x - 1)
{
Debug.Log("向左");
Debug.Log(m_beginPos);
Debug.Log(m_endPos);
}
else if (m_beginPos.x > m_endPos.x + 1)
{
Debug.Log("向右");
Debug.Log(m_beginPos);
Debug.Log(m_endPos);
}
m_beginPos = Vector2.zero;
m_endPos = Vector2.zero;
}
else
{
if (m_beginPos == Vector2.zero)
{
m_beginPos = m_nowPos;
}
m_endPos = m_nowPos;
}
}
public void 抠图()
{
#region 外部读取图片
//把图片读进三通道的容器 而flags则影响着你要读图像的通道数: 不支持中文路径
//当flags等于 - 1的时候会从透明通道开始读,则通道数为4。 png
//当flags等于0时候会只读灰色通道,就是灰度图,通道数为1。 灰度图
//当flags大于等于1时,通道数为3,则是没有透明通道的图片了。 jpg
//Mat src = Imgcodecs.imread(Application.streamingAssetsPath + "/test.png", 1);
#endregion
#region 查询StreamingAssets目录下对应文件 如果有就返回文件的路径
//这里是OpenCVForUnity提供的一个功能类,
//他去查询StreamingAssets目录下对应文件
//如果有就返回文件的路径
//var str = Utils.getFilePath("test.png");
//Debug.Log(str);
#endregion
#region texture2DToMat
Mat src = new Mat(new Size(m_t2d.width, m_t2d.height), CvType.CV_8UC3);
Utils.texture2DToMat(m_t2d, src);
#endregion
//把尺寸设置为大小
Imgproc.resize(src, src, new Size(500, 500));
//建立一个四通道的容器
Mat dst = new Mat(src.cols(), src.rows(), CvType.CV_8UC4);
//转换色彩空间
Imgproc.cvtColor(src, dst, Imgproc.COLOR_BGR2RGBA);
// 遍历一下这个 dst 容器, 里面是处理图像的逻辑 会输出一个处理过的 dst 返回出来
for (int i = 0; i < dst.cols(); i++)
{
for (int j = 0; j < dst.rows(); j++)
{
//这个150是阈值,你可以自己定义来试试效果
if (dst.get(j, i)[0] > alpha)
{
dst.put(j, i, 255, 255, 255, 0);
}
}
}
// 定义一个 Texture2D 对象
Texture2D tex = new Texture2D(dst.cols(), dst.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(dst, tex);
// 将处理后的图片现实在 RawImage 组件上
m_img.texture = tex;
//m_img.SetNativeSize();
}
public void 轮廓识别()
{
//先转成mat
Mat src = new Mat(new Size(m_t2d2.width, m_t2d2.height), CvType.CV_8UC3);
Utils.texture2DToMat(m_t2d2, src);
//---------------------------------------------------------------------------
#region 把Mat转为灰度的图像,并二值化
//创立一个Gray的Mat容器
Mat gray = new Mat();
//转化色彩空间为灰度图
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
//创了一个Bw的Mat容器
Mat bw = new Mat();
//把图片二值化后装入BW容器
Imgproc.threshold(gray, bw, 50, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
#endregion
//---------------------------------------------------------------------------
#region 二值化的后的图像,这样我们就能进行找轮廓的操作了
//这是返回值,他会返回轮廓的各种信息在这个Mat容器里
Mat hierarchy = new Mat();
//这个也是一个返回值,他包含了找到的所有轮廓的点的信息。
List<MatOfPoint> contours = new List<MatOfPoint>();
//findContours 查找所有轮廓
Imgproc.findContours(bw, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
//返回的所有轮廓的点信息都在contours这个列表,
//如果不出意外的话,他返回值应该是7
Debug.Log(contours.Count);
#endregion
//---------------------------------------------------------------------------
#region 下面我们让他把轮廓在图像上表现出来,我们需要用到drawContours这个函数
//因为上面说contours这个列表里包含每个轮廓的信息
//所以我们要遍历这个列表 最后把每个轮廓放入src
for (int i = 0; i < contours.Count; i++)
{
//contourArea获取到轮廓的大小
var area = Imgproc.contourArea(contours[i]);
Debug.Log(area);
//这里的值你可以自己写,根据你自己的图像需求。
if (area > 20000)
{
//如果大于20000就说明过大,不绘制。
continue;
}
绘制描边轮廓
第一个参数是彩色的原图,第二个是列表,
第三个参数是对应的IDX,指向列表里的第几个轮廓
第四个参数是色彩new scalar(r,g,b) -0 0 255就是蓝色
第五个参数是线的粗细thick
//Imgproc.drawContours(src, contours, i, new Scalar(0, 0, 255), 10);
//传入每个轮廓得到每个轮廓的矩形范围
//var _rect = Imgproc.boundingRect(contours[i]);//boundingRect //画出包含轮廓的矩形范围
//把轮廓绘制到原始的彩色图像上
//Imgproc.rectangle(src, _rect, new Scalar(255, 0, 0), 3);
通过rect算出来他的center 并绘制到原始图像上 可以用下面的旋转矩形自带的中心点
//var centerpoint = new Point(_rect.x + _rect.width / 2, _rect.y + _rect.height / 2);
//Imgproc.circle(src, centerpoint, 5, new Scalar(0, 255, 0), -1);//circle 绘制一个球
//minAreaRect //获取包含轮廓最小的可旋转矩形
RotatedRect minRect = Imgproc.minAreaRect(new MatOfPoint2f(contours[i].toArray()));
Point[] vertices = new Point[4]; //定义4个点的数组
minRect.points(vertices); //将四个点存储到vertices数组中
for (int x = 0; x < vertices.Length; x++)
{
// 一条条线绘制
Imgproc.line(src, vertices[x], vertices[(x + 1) % vertices.Length], new Scalar(255, 0, 0), 3);
}
//circle 绘制一个球
Imgproc.circle(src, minRect.center, 5, new Scalar(0, 255, 0), -1);
}
#endregion
//---------------------------------------------------------------------------
// 定义一个 Texture2D 对象
Texture2D tex = new Texture2D(bw.cols(), bw.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(bw, tex);
// 将处理后的图片现实在 RawImage 组件上
m_img2.texture = tex;
//---------------------------------------------------------------------------
// 定义一个 Texture2D 对象
Texture2D tex2 = new Texture2D(src.cols(), src.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(src, tex2);
// 将处理后的图片现实在 RawImage 组件上
m_img3.texture = tex2;
}
public Vector2 轮廓识别2(Texture2D _t2d)
{
Vector2 result = Vector2.zero;
//先转成mat
Mat src = new Mat(new Size(_t2d.width, _t2d.height), CvType.CV_8UC3);
Utils.texture2DToMat(_t2d, src);
//---------------------------------------------------------------------------
#region 把Mat转为灰度的图像,并二值化
//创立一个Gray的Mat容器
Mat gray = new Mat();
//转化色彩空间为灰度图
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
//创了一个Bw的Mat容器
Mat bw = new Mat();
//把图片二值化后装入BW容器
Imgproc.threshold(gray, bw, 50, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
#endregion
//---------------------------------------------------------------------------
#region 二值化的后的图像,这样我们就能进行找轮廓的操作了
//这是返回值,他会返回轮廓的各种信息在这个Mat容器里
Mat hierarchy = new Mat();
//这个也是一个返回值,他包含了找到的所有轮廓的点的信息。
List<MatOfPoint> contours = new List<MatOfPoint>();
//findContours 查找所有轮廓
Imgproc.findContours(bw, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
//返回的所有轮廓的点信息都在contours这个列表,
//如果不出意外的话,他返回值应该是7
//Debug.Log(contours.Count);
#endregion
//---------------------------------------------------------------------------
#region 下面我们让他把轮廓在图像上表现出来,我们需要用到drawContours这个函数
//因为上面说contours这个列表里包含每个轮廓的信息
//所以我们要遍历这个列表 最后把每个轮廓放入src
for (int i = 0; i < contours.Count; i++)
{
//contourArea获取到轮廓的大小
var area = Imgproc.contourArea(contours[i]);
//Debug.Log(area);
//这里的值你可以自己写,根据你自己的图像需求。
if (area < 5000)
{
//如果大于20000就说明过大,不绘制。
continue;
}
绘制描边轮廓
第一个参数是彩色的原图,第二个是列表,
第三个参数是对应的IDX,指向列表里的第几个轮廓
第四个参数是色彩new scalar(r,g,b) -0 0 255就是蓝色
第五个参数是线的粗细thick
//Imgproc.drawContours(src, contours, i, new Scalar(0, 0, 255), 10);
//传入每个轮廓得到每个轮廓的矩形范围
//var _rect = Imgproc.boundingRect(contours[i]);//boundingRect //画出包含轮廓的矩形范围
//把轮廓绘制到原始的彩色图像上
//Imgproc.rectangle(src, _rect, new Scalar(255, 0, 0), 3);
通过rect算出来他的center 并绘制到原始图像上 可以用下面的旋转矩形自带的中心点
//var centerpoint = new Point(_rect.x + _rect.width / 2, _rect.y + _rect.height / 2);
//Imgproc.circle(src, centerpoint, 5, new Scalar(0, 255, 0), -1);//circle 绘制一个球
//minAreaRect //获取包含轮廓最小的可旋转矩形
RotatedRect minRect = Imgproc.minAreaRect(new MatOfPoint2f(contours[i].toArray()));
Point[] vertices = new Point[4]; //定义4个点的数组
minRect.points(vertices); //将四个点存储到vertices数组中
for (int x = 0; x < vertices.Length; x++)
{
// 一条条线绘制
Imgproc.line(src, vertices[x], vertices[(x + 1) % vertices.Length], new Scalar(255, 0, 0), 3);
}
//circle 绘制一个球
Imgproc.circle(src, minRect.center, 5, new Scalar(0, 255, 0), -1);
result.Set((float)minRect.center.x, (float)minRect.center.y);
}
#endregion
//---------------------------------------------------------------------------
// 定义一个 Texture2D 对象
Texture2D tex = new Texture2D(bw.cols(), bw.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(bw, tex);
// 将处理后的图片现实在 RawImage 组件上
m_img2.texture = tex;
//---------------------------------------------------------------------------
// 定义一个 Texture2D 对象
Texture2D tex2 = new Texture2D(src.cols(), src.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(src, tex2);
// 将处理后的图片现实在 RawImage 组件上
m_img3.texture = tex2;
return result;
}
public void 运动追踪()
{
//Mat rgbaMat = new Mat();
//capture.read(rgbaMat);
//
//
//
//
//Imgproc.cvtColor(rgbaMat, rgbMat, Imgproc.COLOR_RGBA2RGB);
//backgroundSubstractorMOG2.apply(rgbMat, fgmaskMat);
//
//Core.bitwise_not(fgmaskMat, fgmaskMat);
//rgbaMat.setTo(new Scalar(0, 0, 0, 0), fgmaskMat);
//
//
//Utils.fastMatToTexture2D(rgbaMat, m_t2d3);
//
//
将处理后的图片现实在 RawImage 组件上
//m_img4.texture = m_t2d3;
}
public void 差异点获取_MOG2()
{
if (webCamTextureToMatHelper.IsPlaying() && webCamTextureToMatHelper.DidUpdateThisFrame())
{
Mat rgbaMat = webCamTextureToMatHelper.GetMat();
Imgproc.cvtColor(rgbaMat, rgbMat, Imgproc.COLOR_RGBA2RGB);
backgroundSubstractorMOG2.apply(rgbMat, fgmaskMat);
Core.bitwise_not(fgmaskMat, fgmaskMat);
rgbaMat.setTo(new Scalar(0, 0, 0, 0), fgmaskMat);
Utils.fastMatToTexture2D(rgbaMat, texture);
m_img4.texture = texture;
m_nowPos = 轮廓识别2(texture);
}
}
/// <summary>
/// Raises the webcam texture to mat helper initialized event.
/// 将网络摄像头纹理提升到垫子助手初始化事件
/// </summary>
public void OnWebCamTextureToMatHelperInitialized()
{
Debug.Log("OnWebCamTextureToMatHelperInitialized");
Mat webCamTextureMat = webCamTextureToMatHelper.GetMat();
texture = new Texture2D(webCamTextureMat.cols(), webCamTextureMat.rows(), TextureFormat.RGBA32, false);
//gameObject.GetComponent<Renderer>().material.mainTexture = texture;
gameObject.transform.localScale = new Vector3(webCamTextureMat.cols(), webCamTextureMat.rows(), 1);
Debug.Log("Screen.width " + Screen.width + " Screen.height " + Screen.height + " Screen.orientation " + Screen.orientation);
if (fpsMonitor != null)
{
fpsMonitor.Add("width", webCamTextureMat.width().ToString());
fpsMonitor.Add("height", webCamTextureMat.height().ToString());
fpsMonitor.Add("orientation", Screen.orientation.ToString());
}
float width = webCamTextureMat.width();
float height = webCamTextureMat.height();
float widthScale = (float)Screen.width / width;
float heightScale = (float)Screen.height / height;
if (widthScale < heightScale)
{
Camera.main.orthographicSize = (width * (float)Screen.height / (float)Screen.width) / 2;
}
else
{
Camera.main.orthographicSize = height / 2;
}
rgbMat = new Mat(webCamTextureMat.rows(), webCamTextureMat.cols(), CvType.CV_8UC3);
fgmaskMat = new Mat(webCamTextureMat.rows(), webCamTextureMat.cols(), CvType.CV_8UC1);
}
/// <summary>
/// Raises the webcam texture to mat helper disposed event.
/// 引发网络摄像头纹理垫帮手 处置事件
/// </summary>
public void OnWebCamTextureToMatHelperDisposed()
{
Debug.Log("OnWebCamTextureToMatHelperDisposed");
if (rgbMat != null)
rgbMat.Dispose();
if (fgmaskMat != null)
fgmaskMat.Dispose();
if (texture != null)
{
Texture2D.Destroy(texture);
texture = null;
}
}
/// <summary>
/// Raises the webcam texture to mat helper error occurred event.
/// 将网络摄像头纹理提升到垫子助手错误发生事件。
/// </summary>
/// <param name="errorCode">Error code.</param>
public void OnWebCamTextureToMatHelperErrorOccurred(WebCamTextureToMatHelper.ErrorCode errorCode)
{
Debug.Log("OnWebCamTextureToMatHelperErrorOccurred " + errorCode);
}
void OnDestroy()
{
webCamTextureToMatHelper.Dispose();
}
public void 仿射变换()
{
#region 外部读取图片
//把图片读进三通道的容器 而flags则影响着你要读图像的通道数: 不支持中文路径
//当flags等于 - 1的时候会从透明通道开始读,则通道数为4。 png
//当flags等于0时候会只读灰色通道,就是灰度图,通道数为1。 灰度图
//当flags大于等于1时,通道数为3,则是没有透明通道的图片了。 jpg
//Mat src = Imgcodecs.imread(Application.streamingAssetsPath + "/test.png", 1);
#endregion
#region 查询StreamingAssets目录下对应文件 如果有就返回文件的路径
//这里是OpenCVForUnity提供的一个功能类,
//他去查询StreamingAssets目录下对应文件
//如果有就返回文件的路径
//var str = Utils.getFilePath("test.png");
//Debug.Log(str);
#endregion
#region texture2DToMat
Mat src = new Mat(new Size(m_t2d.width, m_t2d.height), CvType.CV_8UC3);
Utils.texture2DToMat(m_t2d, src);
#endregion
//计算转换矩阵
Vector2 tl = new Vector2(125, 301);//坐下
Vector2 tr = new Vector2(359, 307);//右下
Vector2 br = new Vector2(372, 93);//右上
Vector2 bl = new Vector2(128, 67);//左上
Mat srcRectMat = new Mat(4, 1, CvType.CV_32FC2);
Mat dstRectMat = new Mat(4, 1, CvType.CV_32FC2);
srcRectMat.put(0, 0, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y);
dstRectMat.put(0, 0, 0.0, src.rows(), src.cols(), src.rows(), 0.0, 0.0, src.rows(), 0);
Mat perspectiveTransform = Imgproc.getPerspectiveTransform(srcRectMat, dstRectMat);
Mat outputMat0 = src.clone();
//圈出四个顶点
//Point t = new Point(tl.x,tl.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(tr.x, tr.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(bl.x, bl.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(br.x, br.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//进行透视转换
Imgproc.warpPerspective(src, outputMat0, perspectiveTransform, new Size(src.rows(), src.cols()));
// 定义一个 Texture2D 对象
Texture2D tex = new Texture2D(outputMat0.cols(), outputMat0.rows(), TextureFormat.RGBA32, false);
// 将 dst 转换成这个 Texture2D 对象
Utils.matToTexture2D(outputMat0, tex);
// 将处理后的图片现实在 RawImage 组件上
m_img.texture = tex;
//m_img.SetNativeSize();
}
/// <summary>
/// Converts the screen point to texture point.
/// </summary>
/// <param name="screenPoint">Screen point.</param>
/// <param name="dstPoint">Dst point.</param>
/// <param name="texturQuad">Texture quad.</param>
/// <param name="textureWidth">Texture width.</param>
/// <param name="textureHeight">Texture height.</param>
/// <param name="camera">Camera.</param>
private Point ConvertScreenPointToTexturePoint(Vector2 _screenPoint, RectTransform _imgRect, int textureWidth , int textureHeight)
{
float beginX = _imgRect.position.x - (_imgRect.pivot.x * _imgRect.sizeDelta.x);
float beginY = _imgRect.position.y - (_imgRect.pivot.y * _imgRect.sizeDelta.y);
float resultX = LoveNeon_Math.lnMap(_screenPoint.x, beginX, beginX + _imgRect.sizeDelta.x, 0, textureWidth, false);
float resultY = LoveNeon_Math.lnMap(_screenPoint.y, beginY + _imgRect.sizeDelta.y, beginY, 0, textureHeight, false);
Point resule = new Point(resultX, resultY);
return resule;
}
}