瀏覽代碼

1、增加区分半自动、自动识别 2、修改算法的output图片 3、bug修复

ZIM 1 年之前
父節點
當前提交
0187efc23a

二進制
Assets/InfraredProject/WebCamera/Image/测试图片.png


+ 0 - 123
Assets/InfraredProject/WebCamera/Image/测试图片.png.meta

@@ -1,123 +0,0 @@
-fileFormatVersion: 2
-guid: d3a5d442a677a7f4aa8dead09e3904ac
-TextureImporter:
-  internalIDToNameTable: []
-  externalObjects: {}
-  serializedVersion: 12
-  mipmaps:
-    mipMapMode: 0
-    enableMipMap: 0
-    sRGBTexture: 1
-    linearTexture: 0
-    fadeOut: 0
-    borderMipMap: 0
-    mipMapsPreserveCoverage: 0
-    alphaTestReferenceValue: 0.5
-    mipMapFadeDistanceStart: 1
-    mipMapFadeDistanceEnd: 3
-  bumpmap:
-    convertToNormalMap: 0
-    externalNormalMap: 0
-    heightScale: 0.25
-    normalMapFilter: 0
-  isReadable: 1
-  streamingMipmaps: 0
-  streamingMipmapsPriority: 0
-  vTOnly: 0
-  ignoreMasterTextureLimit: 0
-  grayScaleToAlpha: 0
-  generateCubemap: 6
-  cubemapConvolution: 0
-  seamlessCubemap: 0
-  textureFormat: 1
-  maxTextureSize: 2048
-  textureSettings:
-    serializedVersion: 2
-    filterMode: 1
-    aniso: 1
-    mipBias: 0
-    wrapU: 1
-    wrapV: 1
-    wrapW: 0
-  nPOTScale: 0
-  lightmap: 0
-  compressionQuality: 50
-  spriteMode: 1
-  spriteExtrude: 1
-  spriteMeshType: 1
-  alignment: 0
-  spritePivot: {x: 0.5, y: 0.5}
-  spritePixelsToUnits: 100
-  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
-  spriteGenerateFallbackPhysicsShape: 1
-  alphaUsage: 1
-  alphaIsTransparency: 1
-  spriteTessellationDetail: -1
-  textureType: 8
-  textureShape: 1
-  singleChannelComponent: 0
-  flipbookRows: 1
-  flipbookColumns: 1
-  maxTextureSizeSet: 0
-  compressionQualitySet: 0
-  textureFormatSet: 0
-  ignorePngGamma: 0
-  applyGammaDecoding: 0
-  cookieLightType: 0
-  platformSettings:
-  - serializedVersion: 3
-    buildTarget: DefaultTexturePlatform
-    maxTextureSize: 2048
-    resizeAlgorithm: 0
-    textureFormat: -1
-    textureCompression: 1
-    compressionQuality: 50
-    crunchedCompression: 0
-    allowsAlphaSplitting: 0
-    overridden: 0
-    androidETC2FallbackOverride: 0
-    forceMaximumCompressionQuality_BC6H_BC7: 0
-  - serializedVersion: 3
-    buildTarget: Standalone
-    maxTextureSize: 2048
-    resizeAlgorithm: 0
-    textureFormat: -1
-    textureCompression: 1
-    compressionQuality: 50
-    crunchedCompression: 0
-    allowsAlphaSplitting: 0
-    overridden: 0
-    androidETC2FallbackOverride: 0
-    forceMaximumCompressionQuality_BC6H_BC7: 0
-  - serializedVersion: 3
-    buildTarget: Android
-    maxTextureSize: 2048
-    resizeAlgorithm: 0
-    textureFormat: -1
-    textureCompression: 1
-    compressionQuality: 50
-    crunchedCompression: 0
-    allowsAlphaSplitting: 0
-    overridden: 0
-    androidETC2FallbackOverride: 0
-    forceMaximumCompressionQuality_BC6H_BC7: 0
-  spriteSheet:
-    serializedVersion: 2
-    sprites: []
-    outline: []
-    physicsShape: []
-    bones: []
-    spriteID: 5e97eb03825dee720800000000000000
-    internalID: 0
-    vertices: []
-    indices: 
-    edges: []
-    weights: []
-    secondaryTextures: []
-    nameFileIdTable: {}
-  spritePackingTag: 
-  pSDRemoveMatte: 0
-  pSDShowRemoveMatteOption: 0
-  userData: 
-  assetBundleName: 
-  assetBundleVariant: 

+ 1 - 3
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredLocate.cs

@@ -124,8 +124,6 @@ namespace ZIM
                 new InfraredSpot(screenIdentification.Screen, InfraredMatch.Match1) };
                 new InfraredSpot(screenIdentification.Screen, InfraredMatch.Match1) };
             }
             }
 
 
-            ScreenPixelCheaker.ImageSize = new Vector2(screenIdentification.Size.x, screenIdentification.Size.y);
-
             //var watch = new System.Diagnostics.Stopwatch();
             //var watch = new System.Diagnostics.Stopwatch();
             //watch.Start();
             //watch.Start();
             //var times = new List<double>() { 0.0 };
             //var times = new List<double>() { 0.0 };
@@ -145,7 +143,7 @@ namespace ZIM
                     int ip = i * samplingScale;
                     int ip = i * samplingScale;
                     int jp = j * samplingScale;
                     int jp = j * samplingScale;
 
 
-                    if (!screenIdentification.Screen.Active && ScreenPixelCheaker.OutCollider2D(new Vector2(ip, jp)))
+                    if (!screenIdentification.Screen.Active && ScreenPixelCheaker.OutArea2D(new Vector2(ip, jp), screenIdentification.Size))
                         continue;
                         continue;
 
 
                     var index = mCameraInfo.CoordToIndex(ip, jp);
                     var index = mCameraInfo.CoordToIndex(ip, jp);

+ 8 - 2
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/LineIdentified.cs

@@ -17,21 +17,27 @@ namespace o0.Project
         public float Gradient;
         public float Gradient;
         // 线梯度方向, 0 - 180度
         // 线梯度方向, 0 - 180度
         public float GradientDegree;
         public float GradientDegree;
+        // 用于绘制的线, 为null就是不绘制
+        public Line DrawLine;
 
 
-        public LineIdentified(int batch, Line line, float gradient, float gradientDegree)
+        public LineIdentified(int batch, Line line, float gradient, float gradientDegree, bool drawAll = false)
         {
         {
             Batch = batch;
             Batch = batch;
             Line = line;
             Line = line;
             Gradient = gradient;
             Gradient = gradient;
             GradientDegree = gradientDegree;
             GradientDegree = gradientDegree;
+            if (drawAll)
+                DrawLine = line;
         }
         }
 
 
-        public LineIdentified(int batch, (Line line, float gradient, float gradientDegree) lineParams)
+        public LineIdentified(int batch, (Line line, float gradient, float gradientDegree) lineParams, bool drawAll = false)
         {
         {
             Batch = batch;
             Batch = batch;
             Line = lineParams.line;
             Line = lineParams.line;
             Gradient = lineParams.gradient;
             Gradient = lineParams.gradient;
             GradientDegree = lineParams.gradientDegree;
             GradientDegree = lineParams.gradientDegree;
+            if (drawAll)
+                DrawLine = lineParams.line;
         }
         }
 
 
         public void Offset(Vector offset)
         public void Offset(Vector offset)

+ 261 - 184
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenIdentification.cs

@@ -8,6 +8,7 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using UnityEngine;
 using UnityEngine;
+using UnityEngine.UIElements;
 using ZIM;
 using ZIM;
 using ZIM.Unity;
 using ZIM.Unity;
 
 
@@ -17,23 +18,29 @@ namespace o0.Project
     {
     {
         private const string TAG = "ScreenIdentification#";
         private const string TAG = "ScreenIdentification#";
 
 
-        //static Rect[][] LocateAreaData = new Rect[][] {
-        //     new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f),     new Rect(0f, 0f, 0.4f, 0.4f),     new Rect(0f, 0f, 0.5f, 0.5f),     new Rect(0f, 0f, 0.6f, 0.6f) },
-        //     new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f),   new Rect(0.6f, 0f, 0.4f, 0.4f),   new Rect(0.5f, 0f, 0.5f, 0.5f),   new Rect(0.4f, 0f, 0.6f, 0.6f) },
-        //     new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f),   new Rect(0f, 0.6f, 0.4f, 0.4f),   new Rect(0f, 0.5f, 0.5f, 0.5f),   new Rect(0f, 0.4f, 0.6f, 0.6f) },
-        //     new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f), new Rect(0.4f, 0.4f, 0.6f, 0.6f) }
-        //};
+        // LocateAreaData表示每次屏幕的色差变化的区域,可能有多次。通过设置LocateSingleStep可调整为仅识别一次色差
         static Rect[][] LocateAreaData = new Rect[][] {
         static Rect[][] LocateAreaData = new Rect[][] {
              new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f),     new Rect(0f, 0f, 0.4f, 0.4f),     new Rect(0f, 0f, 0.5f, 0.5f)     },
              new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f),     new Rect(0f, 0f, 0.4f, 0.4f),     new Rect(0f, 0f, 0.5f, 0.5f)     },
              new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f),   new Rect(0.6f, 0f, 0.4f, 0.4f),   new Rect(0.5f, 0f, 0.5f, 0.5f)   },
              new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f),   new Rect(0.6f, 0f, 0.4f, 0.4f),   new Rect(0.5f, 0f, 0.5f, 0.5f)   },
              new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f),   new Rect(0f, 0.6f, 0.4f, 0.4f),   new Rect(0f, 0.5f, 0.5f, 0.5f)   },
              new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f),   new Rect(0f, 0.6f, 0.4f, 0.4f),   new Rect(0f, 0.5f, 0.5f, 0.5f)   },
              new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f) }
              new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f) }
         };
         };
-        //static bool LocateDebug = false;
-        static bool LocateDebug = true;
+        //static Rect[][] LocateAreaData = new Rect[][] {
+        //     new Rect[] { new Rect(0f, 0f, 0.3f, 0.3f),     new Rect(0f, 0f, 0.4f, 0.4f),     new Rect(0f, 0f, 0.5f, 0.5f),     new Rect(0f, 0f, 0.6f, 0.6f) },
+        //     new Rect[] { new Rect(0.7f, 0f, 0.3f, 0.3f),   new Rect(0.6f, 0f, 0.4f, 0.4f),   new Rect(0.5f, 0f, 0.5f, 0.5f),   new Rect(0.4f, 0f, 0.6f, 0.6f) },
+        //     new Rect[] { new Rect(0f, 0.7f, 0.3f, 0.3f),   new Rect(0f, 0.6f, 0.4f, 0.4f),   new Rect(0f, 0.5f, 0.5f, 0.5f),   new Rect(0f, 0.4f, 0.6f, 0.6f) },
+        //     new Rect[] { new Rect(0.7f, 0.7f, 0.3f, 0.3f), new Rect(0.6f, 0.6f, 0.4f, 0.4f), new Rect(0.5f, 0.5f, 0.5f, 0.5f), new Rect(0.4f, 0.4f, 0.6f, 0.6f) }
+        //};
+
+        //static bool LocateSingleStep = false;
+        static bool LocateSingleStep = true;
 
 
         public Geometry2D.Vector<int> Size => ScreenLocate.Main.CameraSize;
         public Geometry2D.Vector<int> Size => ScreenLocate.Main.CameraSize;
 
 
+        public QuadrilateralInCamera QuadManual;
+        public QuadrilateralInCamera QuadAuto;      // 全自动,可以给用户选择(赋值给Screen.QuadInCamera即生效)
+        public QuadrilateralInCamera QuadSemiAuto;  // 半自动,可以给用户选择(赋值给Screen.QuadInCamera即生效)
+
         public ScreenMap Screen;       // 识别到的屏幕,用于执行透视变换
         public ScreenMap Screen;       // 识别到的屏幕,用于执行透视变换
 
 
         int capture = 0;
         int capture = 0;
@@ -45,14 +52,14 @@ namespace o0.Project
         Geometry.Vector<float>[] ScreenBlackTexture;
         Geometry.Vector<float>[] ScreenBlackTexture;
         Geometry.Vector<float>[] ScreenWhiteTexture;
         Geometry.Vector<float>[] ScreenWhiteTexture;
         int locateIndex = -1;
         int locateIndex = -1;
-        List<Rect> locateArea = new List<Rect> {
+        readonly List<Rect> locateArea = new List<Rect> {
             new Rect(0f, 0f, 0.5f, 0.5f), new Rect(0.5f, 0f, 0.5f, 0.5f), new Rect(0f, 0.5f, 0.5f, 0.5f), new Rect(0.5f, 0.5f, 0.5f, 0.5f)
             new Rect(0f, 0f, 0.5f, 0.5f), new Rect(0.5f, 0f, 0.5f, 0.5f), new Rect(0f, 0.5f, 0.5f, 0.5f), new Rect(0.5f, 0.5f, 0.5f, 0.5f)
         };      // 屏幕显示白色的区域大小
         };      // 屏幕显示白色的区域大小
 
 
         float areaPercent => locateArea[locateIndex].size.x;         // 当前白色区域的占比
         float areaPercent => locateArea[locateIndex].size.x;         // 当前白色区域的占比
         int areaSelected = -1;                                       // 选择哪个区域,顺序与Quadrilateral对应
         int areaSelected = -1;                                       // 选择哪个区域,顺序与Quadrilateral对应
-        List<float> sumTemp = new List<float>();
-        List<OrdinalQuadrilateral> quadTemp = new List<OrdinalQuadrilateral>();
+        readonly List<float> sumTemp = new List<float>();
+        readonly List<OrdinalQuadrilateral> quadTemp = new List<OrdinalQuadrilateral>();
 
 
         //public ScreenIdentification(WebCamTexture texture)
         //public ScreenIdentification(WebCamTexture texture)
         //{
         //{
@@ -61,17 +68,14 @@ namespace o0.Project
         //}
         //}
         public static UnityEngine.Color FloatValueToColor(float i)
         public static UnityEngine.Color FloatValueToColor(float i)
         {
         {
-            switch (i)
+            return i switch
             {
             {
-                case 1:
-                    return UnityEngine.Color.green;
-                case 2:
-                    return UnityEngine.Color.red;
-                case 3:
-                    return UnityEngine.Color.yellow;
-                default:
-                    return UnityEngine.Color.black;
-            }
+                1 => UnityEngine.Color.green,
+                2 => UnityEngine.Color.red,
+                3 => UnityEngine.Color.yellow,
+                4 => UnityEngine.Color.white,
+                _ => UnityEngine.Color.black,
+            };
         }
         }
 
 
         public ScreenIdentification()
         public ScreenIdentification()
@@ -87,6 +91,22 @@ namespace o0.Project
         public event Action OnLocateScreenEnd;
         public event Action OnLocateScreenEnd;
         public bool bStartLocateScreen { get; set; } = false;//是否进行捕获
         public bool bStartLocateScreen { get; set; } = false;//是否进行捕获
 
 
+        public bool SelectScreenAfterLocate(ScreenLocate.ScreenIdentificationTag tag)
+        {
+            QuadrilateralInCamera target = tag switch
+            {
+                ScreenLocate.ScreenIdentificationTag.Manual => QuadManual,
+                ScreenLocate.ScreenIdentificationTag.SemiAuto => QuadSemiAuto,
+                ScreenLocate.ScreenIdentificationTag.Auto => QuadAuto,
+                _ => null
+            };
+
+            if (target == null)
+                return false;
+            SetScreenQuad(target);
+            return true;
+        }
+
         // 自动识别开始的入口
         // 自动识别开始的入口
         public void LocateScreen(int Capture = 30, int Delay = 30)  //数值单位是frame
         public void LocateScreen(int Capture = 30, int Delay = 30)  //数值单位是frame
         {
         {
@@ -132,7 +152,7 @@ namespace o0.Project
 
 
         void DebugImage(List<Texture2D> images)
         void DebugImage(List<Texture2D> images)
         {
         {
-            QuadrilateralFit(images, 5);
+            QuadrilateralFit(images);
 
 
             //var watch = new System.Diagnostics.Stopwatch();
             //var watch = new System.Diagnostics.Stopwatch();
             //watch.Start();
             //watch.Start();
@@ -177,8 +197,8 @@ namespace o0.Project
 
 
         public void NextScreen()
         public void NextScreen()
         {
         {
-            // 测试用
-            if (LocateDebug && areaSelected == -1)
+            // 只识别一次色差变化
+            if (LocateSingleStep && areaSelected == -1)
             {
             {
                 LocateAreaData = new Rect[][] { new Rect[] { new Rect(0, 0, 1f, 1f) } };
                 LocateAreaData = new Rect[][] { new Rect[] { new Rect(0, 0, 1f, 1f) } };
                 locateIndex = 3;
                 locateIndex = 3;
@@ -202,7 +222,16 @@ namespace o0.Project
             }
             }
         }
         }
 
 
-        void Reset()
+        // 清除记录的屏幕识别数据(手动、自动等)
+        public void ClearQuadCache()
+        {
+            SetScreenQuad(null);
+            QuadManual = null;
+            QuadSemiAuto = null;
+            QuadAuto = null;
+        }
+
+        public void Reset()
         {
         {
             // bStartLocateScreen = false;
             // bStartLocateScreen = false;
             delay = 0;
             delay = 0;
@@ -211,7 +240,8 @@ namespace o0.Project
             ScreenBlackTexture = null;
             ScreenBlackTexture = null;
             locateIndex = -1;
             locateIndex = -1;
             areaSelected = -1;
             areaSelected = -1;
-            locateArea.RemoveRange(4, LocateAreaData[0].Length);
+            if (locateArea.Count > 4)
+                locateArea.RemoveRange(4, LocateAreaData[0].Length);
             quadTemp.Clear();
             quadTemp.Clear();
             sumTemp.Clear();
             sumTemp.Clear();
         }
         }
@@ -623,21 +653,21 @@ namespace o0.Project
             //ScreenLocate.DebugTexture(4, ScreenLocateTexG);
             //ScreenLocate.DebugTexture(4, ScreenLocateTexG);
             //ScreenLocate.DebugTexture(5, ScreenLocateTexB);
             //ScreenLocate.DebugTexture(5, ScreenLocateTexB);
 
 
-
-            //var watch = new System.Diagnostics.Stopwatch();
-            //watch.Start();
-            //var times = new List<double>() { 0.0 };
-
             //var ScreenLocateTexLightedMat = texture.Too0Mat();
             //var ScreenLocateTexLightedMat = texture.Too0Mat();
         }
         }
 
 
         /// <param name="lineWidth">识别的最小线段长度</param>
         /// <param name="lineWidth">识别的最小线段长度</param>
         /// <param name="debugImages">这个参数如果不为null且数量大于0,则执行debug操作</param>
         /// <param name="debugImages">这个参数如果不为null且数量大于0,则执行debug操作</param>
-        void QuadrilateralFit(List<Texture2D> debugImages = null, float lineWidth = 10)
+        void QuadrilateralFit(List<Texture2D> debugImages = null)
         {
         {
+            // 如果有旧的手动数据,刷新一下Size
+            QuadManual?.ReSize(new Vector(Size.x, Size.y), ScreenMap.ViewAspectRatioSetting);
             // 屏幕黑白差值,存放多批次的图像用于识别, 该List数量不能等于 0 
             // 屏幕黑白差值,存放多批次的图像用于识别, 该List数量不能等于 0 
             List<UnityEngine.Color[]> PixelsMultipleBatches = new List<UnityEngine.Color[]>();
             List<UnityEngine.Color[]> PixelsMultipleBatches = new List<UnityEngine.Color[]>();
 
 
+            var sw = new System.Diagnostics.Stopwatch();
+            sw.Start();
+
             //读取数据
             //读取数据
             if (debugImages != null && debugImages.Count != 0) 
             if (debugImages != null && debugImages.Count != 0) 
             {
             {
@@ -660,13 +690,19 @@ namespace o0.Project
                 var scale = 1.0f / maxWhite;        // 放大对比度
                 var scale = 1.0f / maxWhite;        // 放大对比度
 
 
                 var differPixel = new UnityEngine.Color[Size.x * Size.y];
                 var differPixel = new UnityEngine.Color[Size.x * Size.y];
-                Parallel.For(0, Size.x * Size.y, i =>
+                var whitePixel = new UnityEngine.Color[Size.x * Size.y];
+                Parallel.For(0, Size.x, x =>
                 {
                 {
-                    var pi = ScreenWhiteTexture[i] - ScreenBlackTexture[i];
-                    differPixel[i] = new UnityEngine.Color(pi.x, pi.y, pi.z) * scale;
+                    for (int y = 0; y < Size.y; y++) 
+                    {
+                        var i = y * Size.x + x;
+                        var d = ScreenWhiteTexture[i] - ScreenBlackTexture[i];
+                        differPixel[i] = new UnityEngine.Color(d.x, d.y, d.z) * scale;
+                        whitePixel[i] = new UnityEngine.Color(ScreenWhiteTexture[i].x, ScreenWhiteTexture[i].y, ScreenWhiteTexture[i].z) * scale;
+                    }
                 });
                 });
                 PixelsMultipleBatches.Add(differPixel);
                 PixelsMultipleBatches.Add(differPixel);
-                PixelsMultipleBatches.Add(ScreenWhiteTexture.Select((i) => new UnityEngine.Color(i.x, i.y, i.z) * scale).ToArray());
+                PixelsMultipleBatches.Add(whitePixel);
             }
             }
 
 
             int conSize = (int)Math.Ceiling(0.007f * Size.y) * 2 + 1;
             int conSize = (int)Math.Ceiling(0.007f * Size.y) * 2 + 1;
@@ -674,7 +710,7 @@ namespace o0.Project
 
 
             float minLength = conSize * 7.7f;
             float minLength = conSize * 7.7f;
             minLength = locateIndex == -1 ? minLength : minLength * areaPercent;    // minLength需要按比例缩小
             minLength = locateIndex == -1 ? minLength : minLength * areaPercent;    // minLength需要按比例缩小
-            string log = $"[ScreenLocate Auto] Size: ({Size.x},{Size.y}), 卷积核Size: {conSize}, 最小线段长度: {minLength}";
+            string log = $"[Log][ScreenLocate Auto] Size: ({Size.x},{Size.y}), 卷积核Size: {conSize}, 最小线段长度: {minLength}";
 
 
             var allLines = new List<LineIdentified>();
             var allLines = new List<LineIdentified>();
             List<Texture2D> LocateTexTemp = new List<Texture2D>();
             List<Texture2D> LocateTexTemp = new List<Texture2D>();
@@ -690,90 +726,129 @@ namespace o0.Project
             }
             }
             Texture2D ScreenLocateTexture = LocateTexTemp[0];       // for output
             Texture2D ScreenLocateTexture = LocateTexTemp[0];       // for output
 
 
-            // 过滤得到四边形的四条边, ScreenLocateMatList[0]默认是屏幕的黑白色差
-            var quadLines = FilterLines(ScreenLocateMatList, allLines, GetAvgPoint(ScreenLocateMatList[0]),
-                out Line[] oldLines, out List<Line> possibleLines,
-                Screen, conSize, conSize, minLength);
-
+            // 估算屏幕中点,如果已有手动定位数据,根据现有数据取平均即可,否则从色差计算,ScreenLocateMatList[0]默认是屏幕的黑白色差
+            Vector AvgPoint = QuadManual != null ? QuadManual.Quad.Centroid : GetAvgPoint(ScreenLocateMatList[0]);
+            // 过滤得到四边形的四条边,
+            var (quadLinesSemiAuto, quadLinesAuto) = FilterLines(ScreenLocateMatList, allLines, AvgPoint,
+                out LineIdentified[] manualLines, out List<LineIdentified> possibleLines,
+                conSize, conSize, minLength);
 
 
-            // 将 allLines 输出一张图片
-            var allLinesMap = new Matrix(Size, Tiling: true);
-            foreach (var l in allLines)
+            #region 全自动识别的结果
+            List<LineIdentified> LineIdentifiedAuto = new List<LineIdentified>();               // 线段顺序: 下、右、上、左
+            for (int i = 0; i < 4; i++)
             {
             {
-                if (l.Line != null)
-                    o0Extension.DrawLine(allLinesMap, l.Line, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true);
+                if (quadLinesAuto[i] != null)
+                    LineIdentifiedAuto.Add(quadLinesAuto[i]);
             }
             }
-            var allLinesTex = allLinesMap.ToTexRGBA(FloatValueToColor);
-            ScreenLocate.DebugTexture(1, allLinesTex);
-
-
+            if (LineIdentifiedAuto.Count == 4)            // 判断识别的线段能否拼成屏幕,能拼成则记录
+            {
+                var a = LineIdentifiedAuto[0].Line.Intersect(LineIdentifiedAuto[3].Line, false).Value;
+                var b = LineIdentifiedAuto[0].Line.Intersect(LineIdentifiedAuto[1].Line, false).Value;
+                var c = LineIdentifiedAuto[2].Line.Intersect(LineIdentifiedAuto[3].Line, false).Value;
+                var d = LineIdentifiedAuto[1].Line.Intersect(LineIdentifiedAuto[2].Line, false).Value;
+                QuadAuto = new QuadrilateralInCamera(a, b, c, d, new Vector(Size.x, Size.y));
+                if (!QuadAuto.IsQuadComplete())
+                    QuadAuto = null;
+            }
+            #endregion
 
 
-            // 将识别到的边画出来,并判断能否拼成屏幕,能拼成则设置ScreenMap
-            // 线段顺序: 下、右、上、左
-            List<Line> LineIdentified = new List<Line>();
+            #region 半自动识别
+            List<LineIdentified> LineIdentifiedSemiAuto = new List<LineIdentified>();               // 线段顺序: 下、右、上、左
+            bool[] newLines = new bool[4] { true, true, true, true };
             for (int i = 0; i < 4; i++)
             for (int i = 0; i < 4; i++)
             {
             {
-                if (quadLines[i] != null)
-                    LineIdentified.Add(quadLines[i]);
-                else if (oldLines != null)
-                    LineIdentified.Add(oldLines[i]);
+                if (quadLinesSemiAuto[i] != null)
+                    LineIdentifiedSemiAuto.Add(quadLinesSemiAuto[i]);
+                else if (manualLines != null)
+                {
+                    LineIdentifiedSemiAuto.Add(manualLines[i]);
+                    newLines[i] = false;
+                }
             }
             }
 
 
-            var drawScreenMap = new Matrix(Size, Tiling: true);
-            foreach (var l in LineIdentified)
-                o0Extension.DrawLine(drawScreenMap, l, (x, y) => 1, new Geometry2D.Float.Vector(0, lineWidth));
-            Texture2D ScreenQuadTex = drawScreenMap.ToTex();      // out ScreenQuadTex
-
-            QuadrilateralInCamera screenQuad = null;
-            if (LineIdentified.Count == 4)
+            if (LineIdentifiedSemiAuto.Count == 4)            // 判断识别的线段能否拼成屏幕,能拼成则记录
             {
             {
-                var a = LineIdentified[0].Intersect(LineIdentified[3], false).Value;
-                var b = LineIdentified[0].Intersect(LineIdentified[1], false).Value;
-                var c = LineIdentified[2].Intersect(LineIdentified[3], false).Value;
-                var d = LineIdentified[1].Intersect(LineIdentified[2], false).Value;
-                screenQuad = new QuadrilateralInCamera(a, b, c, d, new Vector(Size.x, Size.y));
-                if (!screenQuad.IsQuadComplete())
-                    screenQuad = null;
+                var a = LineIdentifiedSemiAuto[0].Line.Intersect(LineIdentifiedSemiAuto[3].Line, false).Value;
+                var b = LineIdentifiedSemiAuto[0].Line.Intersect(LineIdentifiedSemiAuto[1].Line, false).Value;
+                var c = LineIdentifiedSemiAuto[2].Line.Intersect(LineIdentifiedSemiAuto[3].Line, false).Value;
+                var d = LineIdentifiedSemiAuto[1].Line.Intersect(LineIdentifiedSemiAuto[2].Line, false).Value;
+                QuadSemiAuto = new QuadrilateralInCamera(a, b, c, d, new Vector(Size.x, Size.y));
+                if (!QuadSemiAuto.IsQuadComplete())
+                    QuadSemiAuto = null;
             }
             }
-            if (screenQuad == null && Screen.QuadInCamera != null)     // 如果可能,回退到上一个screen
+            #endregion
+
+            // 优先应用半自动的结果(也可以在外部手动设置)
+            if (QuadSemiAuto == null && QuadAuto == null && Screen.QuadInCamera != null)     // 如果可能,回退到上一个screen
             {
             {
                 Debug.Log("<color=aqua>[ScreenIdentification] 本次识别失败,回退到上次的识别结果</color>");
                 Debug.Log("<color=aqua>[ScreenIdentification] 本次识别失败,回退到上次的识别结果</color>");
                 quadTemp.Add(Screen.QuadInCamera.Quad);
                 quadTemp.Add(Screen.QuadInCamera.Quad);
             }
             }
-            else if (screenQuad != null)
+            else if (QuadSemiAuto != null)
+            {
+                Debug.Log("<color=aqua>[ScreenIdentification] 识别到四边形</color>");
+                quadTemp.Add(QuadSemiAuto.Quad);
+            }else if (QuadAuto != null)
             {
             {
                 Debug.Log("<color=aqua>[ScreenIdentification] 识别到四边形</color>");
                 Debug.Log("<color=aqua>[ScreenIdentification] 识别到四边形</color>");
-                quadTemp.Add(screenQuad.Quad);
+                quadTemp.Add(QuadAuto.Quad);
             }
             }
 
 
+            #region 绘制 output texture
+            // 绘制半自动
+            var ScreenQuadMap = new Matrix(Size, Tiling: true);     // 识别的到的屏幕四边形(半自动和自动在一张图上) 
+            foreach (var i in LineIdentifiedSemiAuto.Index())
+            {
+                if (newLines[i])
+                    o0Extension.DrawLine(ScreenQuadMap, LineIdentifiedSemiAuto[i].DrawLine, (x, y) => 2, new Geometry2D.Float.Vector(0, 10));
+                else
+                    o0Extension.DrawLine(ScreenQuadMap, LineIdentifiedSemiAuto[i].DrawLine, (x, y) => 1, new Geometry2D.Float.Vector(0, 6), true);
+            }
 
 
+            // 绘制全自动
+            foreach (var i in LineIdentifiedAuto.Index())
+                o0Extension.DrawLine(ScreenQuadMap, LineIdentifiedAuto[i].DrawLine, (x, y) => 4, new Geometry2D.Float.Vector(0, 4), true);
+
+            Texture2D ScreenQuad = ScreenQuadMap.ToTexRGBA(FloatValueToColor);
+            Texture2D ScreenQuadWithScreen = ScreenQuad.Overlay(ScreenLocateTexture);   // 叠加屏幕色差图
+
+            // 绘制allLines
+            var allLinesMap = new Matrix(Size, Tiling: true);
+            foreach (var l in allLines)
+            {
+                if (l.DrawLine != null)
+                    o0Extension.DrawLine(allLinesMap, l.DrawLine, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true);
+            }
+            var allLinesTex = allLinesMap.ToTexRGBA(FloatValueToColor);
+            ScreenLocate.DebugTexture(1, allLinesTex);
 
 
             // 还需要输出一张识别结果图,包含干扰线段
             // 还需要输出一张识别结果图,包含干扰线段
-            var LSDLineMap = new Matrix(Size, Tiling: true);
+            var ChoosableLineMap = new Matrix(Size, Tiling: true);
             foreach (var l in possibleLines)
             foreach (var l in possibleLines)
             {
             {
-                if (l != null && !quadLines.Contains(l))
-                    o0Extension.DrawLine(LSDLineMap, l, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true); // 其他的备选线段
+                if (l != null && !quadLinesSemiAuto.Contains(l) && !manualLines.Contains(l))
+                    o0Extension.DrawLine(ChoosableLineMap, l.DrawLine, (x, y) => 3, new Geometry2D.Float.Vector(0, 2), true); // 其他的备选线段
             }
             }
-            foreach (var l in quadLines)
+            foreach (var l in LineIdentifiedSemiAuto)
             {
             {
                 if (l != null)
                 if (l != null)
-                    o0Extension.DrawLine(LSDLineMap, l, (x, y) => 2, new Geometry2D.Float.Vector(0, 4)); // 这次识别到的线段
+                    o0Extension.DrawLine(ChoosableLineMap, l.DrawLine, (x, y) => 2, new Geometry2D.Float.Vector(0, 5)); // 识别的结果
             }
             }
-            if (oldLines != null)
+            if (manualLines != null)
             {
             {
-                foreach (var l in oldLines)
-                    o0Extension.DrawLine(LSDLineMap, l, (x, y) => 1, new Geometry2D.Float.Vector(0, 2), true);     // 旧的屏幕线段(例如上次手动识别的)
+                foreach (var l in manualLines)
+                    o0Extension.DrawLine(ChoosableLineMap, l.DrawLine, (x, y) => 1, new Geometry2D.Float.Vector(0, 2), true);     // 旧的屏幕线段(例如上次手动识别的)
             }
             }
-            Texture2D ChoosableLineTex = LSDLineMap.ToTexRGBA(FloatValueToColor);
-
+            Texture2D ChoosableLineTex = ChoosableLineMap.ToTexRGBA(FloatValueToColor);
+            #endregion
 
 
+            log += $"\r\n屏幕四边形_手动识别{QuadManual != null}\r\n屏幕四边形_半自动识别{QuadSemiAuto != null}\r\n屏幕四边形_全自动识别{QuadAuto != null}";
             Debug.Log(log);
             Debug.Log(log);
             // 是否将图片保存到本地
             // 是否将图片保存到本地
             if (ScreenLocate.Main.SaveToggle.isOn && ScreenLocate.Main.DebugOnZIMDemo)
             if (ScreenLocate.Main.SaveToggle.isOn && ScreenLocate.Main.DebugOnZIMDemo)
             {
             {
                 var FileDirectory = $"Debug_屏幕定位/";
                 var FileDirectory = $"Debug_屏幕定位/";
-                SaveImages(FileDirectory, log, ScreenLocateTexture, allLinesTex, ChoosableLineTex, ScreenQuadTex);
+                SaveImages(FileDirectory, log, ScreenLocateTexture, allLinesTex, ChoosableLineTex, ScreenQuad);
             }
             }
 
 
             //times.Add(watch.ElapsedMilliseconds);
             //times.Add(watch.ElapsedMilliseconds);
@@ -791,9 +866,8 @@ namespace o0.Project
 
 
             {
             {
                 ScreenLocate.DebugTexture(2, ScreenLocateTexture);
                 ScreenLocate.DebugTexture(2, ScreenLocateTexture);
-                ScreenLocate.DebugTexture(3, ScreenQuadTex);
-                // 融合线段和原图
-                ScreenLocate.DebugTexture(4, ScreenLocateTexture.Merge(ScreenQuadTex));
+                ScreenLocate.DebugTexture(3, ScreenQuad);
+                ScreenLocate.DebugTexture(4, ScreenQuadWithScreen);
                 ScreenLocate.DebugTexture(5, ChoosableLineTex);
                 ScreenLocate.DebugTexture(5, ChoosableLineTex);
             }
             }
             foreach (var i in LocateTexTemp)
             foreach (var i in LocateTexTemp)
@@ -840,17 +914,42 @@ namespace o0.Project
             return l.Count;
             return l.Count;
         }
         }
 
 
-        // 返回四边形的四条边,List长度一定是4 (如果没有识别到就是null),且线段顺序是: 下、右、上、左
-        List<Line> FilterLines(List<Matrix> screenLocateMatList, List<LineIdentified> allLines, Vector avgPoint,
-            out Line[] oldLines, out List<Line> possibleLines, 
-            ScreenMap screen, float conSize, float gradientLength, float minLength = 100)
+        // 返回四边形的四条边(半自动、全自动),List长度一定是4 (如果没有识别到就是null),且线段顺序是: 下、右、上、左
+        (List<LineIdentified>, List<LineIdentified>) FilterLines(List<Matrix> screenLocateMatList, List<LineIdentified> allLines, Vector avgPoint,
+            out LineIdentified[] manualLines, out List<LineIdentified> possibleLines, float conSize, float gradientLength, float minLength = 100)
         {
         {
             //Debug.Log("[IdentifyLineLSD] lines.Count: " + lines.Count);
             //Debug.Log("[IdentifyLineLSD] lines.Count: " + lines.Count);
-            // LSD计算得到的矩阵尺寸较小(因为卷积),这里必须进行位移
             var offset = new Vector((conSize - 1) / 2, (conSize - 1) / 2);
             var offset = new Vector((conSize - 1) / 2, (conSize - 1) / 2);
+            // LSD计算得到的矩阵尺寸较小(因为卷积),这里必须进行位移
             for (int i = 0; i < allLines.Count; i++)
             for (int i = 0; i < allLines.Count; i++)
                 allLines[i].Offset(offset);
                 allLines[i].Offset(offset);
 
 
+            // 筛掉椭圆框外的线段(超出一半会筛掉)
+            var innerLines = new List<LineIdentified>();
+            for (int i = 0; i < allLines.Count; i++)
+            {
+                List<Vector> InArea = new List<Vector>();
+                var dir = (allLines[i].Line.B - allLines[i].Line.A) / 4;
+                var points = new Vector[5] { allLines[i].Line.A, allLines[i].Line.A + dir, allLines[i].Line.A + dir * 2f, allLines[i].Line.A + dir * 3f, allLines[i].Line.B };  // A点、中间的点、B点
+                for (int pI = 0; pI < points.Length; pI++)
+                {
+                    if (!ScreenLocate.Main.ScreenPixelCheaker.OutArea2D(points[pI], Size))
+                        InArea.Add(points[pI]);
+                }
+
+                if (InArea.Count < 2)   // 少于2个点在内部
+                    continue;
+                else if (InArea.Count < points.Length)  // 不完全在内部
+                    allLines[i].DrawLine = new Line(InArea.First(), InArea.Last());        // 将部分线条设置为drawline,用于下一步的绘制
+                else   // 线段全部在椭圆内
+                    allLines[i].DrawLine = allLines[i].Line;
+
+                innerLines.Add(allLines[i]);
+            }
+
+            // 角度阈值,用来判断线段的梯度方向是否指向屏幕中心(avgPoint)
+            var avaAngleHalf = 75f;
+
             // 沿直线计算综合梯度(梯度乘以长度系数,再乘以距离系数), distanceRatio是实际距离除以最大距离
             // 沿直线计算综合梯度(梯度乘以长度系数,再乘以距离系数), distanceRatio是实际距离除以最大距离
             float estimateGradient(LineIdentified line, float distanceRatio)
             float estimateGradient(LineIdentified line, float distanceRatio)
             {
             {
@@ -867,143 +966,121 @@ namespace o0.Project
                     lg.Add(screenLocateMatList[line.Batch][(int)ga.x, (int)ga.y] - screenLocateMatList[line.Batch][(int)gb.x, (int)gb.y]);
                     lg.Add(screenLocateMatList[line.Batch][(int)ga.x, (int)ga.y] - screenLocateMatList[line.Batch][(int)gb.x, (int)gb.y]);
                 }
                 }
 
 
-                float e = (float)Math.Pow(Math.Ceiling(line.Line.Length / minLength), 0.2f);       // 长度系数,筛选时梯度更大、长度更长的线段更优
+                float e = (float)Math.Sqrt(Math.Ceiling(line.Line.Length / minLength));       // 长度系数,筛选时梯度更大、长度更长的线段更优
                 float d = (3 - distanceRatio) / 2;            // 距离系数,距离越近,系数越大
                 float d = (3 - distanceRatio) / 2;            // 距离系数,距离越近,系数越大
                 return e * d * Math.Abs(lg.Mean());
                 return e * d * Math.Abs(lg.Mean());
             }
             }
+            // 根据线段梯度的角度,判断是不是屏幕的边,out index代表是哪条边(顺序是: 下、右、上、左)
+            bool isScreenLine(LineIdentified line, out int index)
+            {
+                var a = (avgPoint - (line.Line.A + line.Line.B) / 2).DegreeToXAxis();
+                //Debug.Log(a + ", " + gradient + ", " + sum);
+                index = -1;
+                if (Math.Abs(a - line.GradientDegree) < avaAngleHalf || Math.Abs(a - 360 - line.GradientDegree) < avaAngleHalf || Math.Abs(a + 360 - line.GradientDegree) < avaAngleHalf)
+                {
+                    if (line.GradientDegree > 45 && line.GradientDegree < 135)     // 下
+                        index = 0;
+                    else if (line.GradientDegree > 135 && line.GradientDegree < 225) // 右
+                        index = 1;
+                    else if (line.GradientDegree > 225 && line.GradientDegree < 315)  // 上
+                        index = 2;
+                    else
+                        index = 3;
+                    return true;
+                }
+                return false;
+            }
+
 
 
-            // 下、右、上、左
-            var quadLines = new List<(float, Line)>[4] { new List<(float, Line)>(), new List<(float, Line)>(), new List<(float, Line)>(), new List<(float, Line)>() };
-            possibleLines = new List<Line>();
-            oldLines = null;
+            // 下、右、上、左, 半自动和自动
+            var quadLinesSemiAuto = new List<(float, LineIdentified)>[4] { new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>() };
+            var quadLinesAuto = new List<(float, LineIdentified)>[4] { new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>(), new List<(float, LineIdentified)>() };
+            possibleLines = new List<LineIdentified>();
+            manualLines = null;
 
 
-            // 如果已有定位数据,根据现有数据筛选线条
-            if (screen.QuadInCamera != null)
+            // 如果已有手动定位数据,根据现有数据筛选线条(半自动)
+            if (QuadManual != null)
             {
             {
                 Debug.Log("[IdentifyLineLSD] 根据已有定位数据做筛选");
                 Debug.Log("[IdentifyLineLSD] 根据已有定位数据做筛选");
-                screen.RefreshCameraSize(new Vector2(Size.x, Size.y));
-
                 var calibration = ScreenLocate.Main.ReDoLocateCalibrationRatio * Size.y;
                 var calibration = ScreenLocate.Main.ReDoLocateCalibrationRatio * Size.y;
-                oldLines = screen.QuadInCamera.GetLines();
+                manualLines = QuadManual.GetLines().Select((i) => new LineIdentified(0, i, 0, 0, true)).ToArray();
 
 
-                var pedals = oldLines.Select((i) => o0Extension.PointPedal(i, avgPoint, out _)).ToArray();     // 当前定位的垂足,下、右、上、左
+                var pedals = manualLines.Select((i) => o0Extension.PointPedal(i.Line, avgPoint, out _)).ToArray();     // 当前定位的垂足,下、右、上、左
 
 
-                foreach (var i in allLines)
+                foreach (var line in innerLines)
                 {
                 {
-                    float minDistance = float.MaxValue;
-                    int index = -1;
-                    foreach (var j in pedals.Index())
-                    {
-                        var d = (o0Extension.PointPedal(i.Line, avgPoint, out _) - pedals[j]).Length;
-                        if (d < minDistance)
-                        {
-                            minDistance = d;
-                            index = j;
-                        }
-                    }
-                    //Debug.Log(minDistance +", -----------"+ calibration);
-                    if (minDistance < calibration)      // 垂足的距离足够近
+                    // 筛选条件:1-梯度方向匹配,2-垂足的距离足够近, 3-新的线段的中点,到旧线段的垂足,要在旧线段内
+                    if (isScreenLine(line, out int index))
                     {
                     {
-                        // 另外满足,新的线段的中点,到旧线段的垂足,要在旧线段内
-                        var middleToOldLine = o0Extension.PointPedal(oldLines[index], (i.Line.A + i.Line.B) / 2, out bool inLineSegment);
-                        if (inLineSegment)
+                        var distanceToOld = (o0Extension.PointPedal(line.Line, avgPoint, out _) - pedals[index]).Length;
+                        if (distanceToOld < calibration)
                         {
                         {
-                            quadLines[index].Add((estimateGradient(i, minDistance / calibration), i.Line));
-                            possibleLines.Add(i.Line);
+                            var middleToOldLine = o0Extension.PointPedal(manualLines[index].Line, (line.Line.A + line.Line.B) / 2, out bool inLineSegment);
+                            if (inLineSegment)
+                            {
+                                quadLinesSemiAuto[index].Add((estimateGradient(line, distanceToOld / calibration), line));
+                                possibleLines.Add(line);
+                            }
                         }
                         }
                     }
                     }
                 }
                 }
             }
             }
-            else
+
+            // 全自动
+            foreach (var line in allLines)
             {
             {
-                var avaAngleHalf = 75f;
-                foreach (var line in allLines)
+                if (isScreenLine(line , out int index))
                 {
                 {
-                    possibleLines.Add(line.Line);
-
-                    var a = (avgPoint - (line.Line.A + line.Line.B) / 2).DegreeToXAxis();
-                    //Debug.Log(a + ", " + gradient + ", " + sum);
-                    int index = -1;
-                    if (Math.Abs(a - line.GradientDegree) < avaAngleHalf || Math.Abs(a - 360 - line.GradientDegree) < avaAngleHalf || Math.Abs(a + 360 - line.GradientDegree) < avaAngleHalf)
+                    if (line.Batch < 1)     // 全自动只处理第一张图,默认是色差图
                     {
                     {
-                        if (line.GradientDegree > 45 && line.GradientDegree < 135)     // 下
-                            index = 0;
-                        else if (line.GradientDegree > 135 && line.GradientDegree < 225) // 右
-                            index = 1;
-                        else if (line.GradientDegree > 225 && line.GradientDegree < 315)  // 上
-                            index = 2;
-                        else
-                            index = 3;
-
-                        //var g = Math.Abs(lg.Mean());
-                        //Debug.Log(gradient + ", " + g);
-
-                        //List<float> lp1 = new List<float>(), lp2 = new List<float>();    // 线两侧的值
-                        //for (float i = 0; i <= ll; i += step)
-                        //{
-                        //    var point = line.A + dir * i;
-                        //    var ga = point + vertical;
-                        //    var gb = point - vertical;
-                        //    lp1.Add(screenLocateMat[(int)ga.x, (int)ga.y]);
-                        //    lp2.Add(screenLocateMat[(int)gb.x, (int)gb.y]);
-                        //}
-
-                        //var avg1 = lp1.Mean();
-                        //var avg2 = lp2.Mean();
-                        //var v1 = lp1.Variance();
-                        //var v2 = lp2.Variance();
-
-                        //var lineGradient = Math.Abs(avg1 - avg2) / (v1 + v2 + 0.2f);       // 方差越小,梯度的价值越高
-                        ////var g = Math.Abs(lg.Mean());
-                        ////Debug.Log(gradient + ", " + g);
-                        //Debug.Log(v1 + ", " + v2 + ", " + lineGradient);
-
-                        //quadLines[index].Add((lineGradient, line));
-
-                        quadLines[index].Add((estimateGradient(line, 1), line.Line));
+                        quadLinesAuto[index].Add((estimateGradient(line, 1), line));
                     }
                     }
                 }
                 }
             }
             }
 
 
-            var result = new Line[4];
+            var resultSemiAuto = new LineIdentified[4];
+            var resultAuto = new LineIdentified[4];
             for (int i = 0; i < 4; i++)
             for (int i = 0; i < 4; i++)
             {
             {
-                if (quadLines[i].Count > 0)
-                    result[i] = quadLines[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
+                if (quadLinesSemiAuto[i].Count > 0)
+                    resultSemiAuto[i] = quadLinesSemiAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
+                if (quadLinesAuto[i].Count > 0)
+                    resultAuto[i] = quadLinesAuto[i].Max((a, b) => a.Item1.CompareTo(b.Item1)).Item2;
             }
             }
-            return result.ToList();
+            return (resultSemiAuto.ToList(), resultAuto.ToList());
         }
         }
 
 
-        void SaveImages(string FileDirectory, string log, Texture2D ScreenLocateTex, Texture2D allLinesTex, Texture2D ChoosableLineTex, Texture2D ScreenQuadTex)
+        void SaveImages(string FileDirectory, string log, 
+            Texture2D ScreenLocateTex, Texture2D allLinesTex, Texture2D ChoosableLineTex, Texture2D ScreenQuadTex)
         {
         {
             if (!Directory.Exists(FileDirectory))
             if (!Directory.Exists(FileDirectory))
                 Directory.CreateDirectory(FileDirectory);
                 Directory.CreateDirectory(FileDirectory);
             var time = DateTime.Now.ToString("yyyyMMdd_HHmmss");
             var time = DateTime.Now.ToString("yyyyMMdd_HHmmss");
 
 
-            var pngData = (ScreenLocate.Main.outputTexture2D[7] as Texture2D)?.EncodeToPNG();
+            var pngData = ScreenLocate.Main.OutputTextures[7]?.EncodeToPNG();
             if (pngData != null)
             if (pngData != null)
                 File.WriteAllBytes($"{FileDirectory}{time}A屏幕原图.png", pngData);
                 File.WriteAllBytes($"{FileDirectory}{time}A屏幕原图.png", pngData);
-            var pngData1 = ScreenLocateTex.EncodeToPNG();
+            var pngData1 = ScreenLocateTex?.EncodeToPNG();
             if (pngData1 != null)
             if (pngData1 != null)
                 File.WriteAllBytes($"{FileDirectory}{time}B黑白色差.png", pngData1);
                 File.WriteAllBytes($"{FileDirectory}{time}B黑白色差.png", pngData1);
-            var pngData2 = allLinesTex.EncodeToPNG();
+            var pngData2 = allLinesTex?.EncodeToPNG();
             if (pngData2 != null)
             if (pngData2 != null)
-                File.WriteAllBytes($"{FileDirectory}{time}C全部识别线段.png", pngData2);
-            var pngData3 = ChoosableLineTex.EncodeToPNG();
+                File.WriteAllBytes($"{FileDirectory}{time}C全部识别线段_半自动.png", pngData2);
+            var pngData3 = ChoosableLineTex?.EncodeToPNG();
             if (pngData3 != null)
             if (pngData3 != null)
-                File.WriteAllBytes($"{FileDirectory}{time}D备选线段.png", pngData3);
-            var pngData4 = ScreenQuadTex.EncodeToPNG();
+                File.WriteAllBytes($"{FileDirectory}{time}D备选线段_半自动.png", pngData3);
+            var pngData4 = ScreenQuadTex?.EncodeToPNG();
             if (pngData4 != null)
             if (pngData4 != null)
                 File.WriteAllBytes($"{FileDirectory}{time}E识别结果.png", pngData4);
                 File.WriteAllBytes($"{FileDirectory}{time}E识别结果.png", pngData4);
 
 
 
 
             Debug.Log($"<color=aqua>({time}) 屏幕识别图片保存至:程序根目录/{FileDirectory}</color>");
             Debug.Log($"<color=aqua>({time}) 屏幕识别图片保存至:程序根目录/{FileDirectory}</color>");
-            log += 
+            log +=
                 $"\r\n屏幕原图保存{pngData != null}, " +
                 $"\r\n屏幕原图保存{pngData != null}, " +
                 $"\r\n黑白色差保存{pngData1 != null}, " +
                 $"\r\n黑白色差保存{pngData1 != null}, " +
-                $"\r\n全部识别线段保存{pngData2 != null}, " +
-                $"\r\n备选线段保存{pngData3 != null}, " +
-                $"\r\n识别结果保存{pngData4 != null}, ";
+                $"\r\n全部识别线段(半自动)保存{pngData2 != null}, " +
+                $"\r\n备选线段(半自动)保存{pngData3 != null}, " +
+                $"\r\n识别结果保存{pngData4 != null}";
             File.WriteAllText($"{FileDirectory}{time}屏幕自动定位_日志.log", log);
             File.WriteAllText($"{FileDirectory}{time}屏幕自动定位_日志.log", log);
         }
         }
 
 

+ 4 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/Other/OrdinalQuadrilateral.cs

@@ -1,6 +1,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using o0.Geometry2D.Float;
 using o0.Geometry2D.Float;
+using Unity.VisualScripting;
 
 
 namespace ZIM
 namespace ZIM
 {
 {
@@ -33,6 +34,9 @@ namespace ZIM
 
 
         public static OrdinalQuadrilateral Identity { get; } = new OrdinalQuadrilateral(new Vector(0, 0), new Vector(1, 0), new Vector(0, 1), new Vector(1, 1));
         public static OrdinalQuadrilateral Identity { get; } = new OrdinalQuadrilateral(new Vector(0, 0), new Vector(1, 0), new Vector(0, 1), new Vector(1, 1));
 
 
+        /// <summary>四边形的质心(顶点平均值)</summary>
+        public Vector Centroid => new Vector(A.x + B.x + C.x + D.x, A.y + B.y + C.y + D.y) / 4f;
+
         public override Vector this[int index]
         public override Vector this[int index]
         {
         {
             get
             get

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/Other/QuadrilateralInCamera.cs

@@ -1,5 +1,6 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
 using o0.Geometry2D;
 using o0.Geometry2D;
 using o0.Geometry2D.Float;
 using o0.Geometry2D.Float;
 using UnityEngine;
 using UnityEngine;
@@ -33,6 +34,13 @@ namespace ZIM.Unity
             Quad = new OrdinalQuadrilateral(enumable);
             Quad = new OrdinalQuadrilateral(enumable);
         }
         }
 
 
+        // 固定的顶点顺序: 左下,右下,左上,右上
+        public QuadrilateralInCamera(IEnumerable<Vector2> enumable, Vector cameraSize)
+        {
+            CameraSize = cameraSize;
+            Quad = new OrdinalQuadrilateral(enumable.Select((i=>i.o0Vector())));
+        }
+
         public QuadrilateralInCamera(OrdinalQuadrilateral quad, Vector cameraSize)
         public QuadrilateralInCamera(OrdinalQuadrilateral quad, Vector cameraSize)
         {
         {
             CameraSize = cameraSize;
             CameraSize = cameraSize;

+ 61 - 42
Assets/InfraredProject/WebCamera/Script/ZIM/ScreenLocate.cs

@@ -39,6 +39,13 @@ public partial class ScreenLocate : MonoBehaviour
         Android
         Android
     }
     }
     Platform mPlatform = Platform.Android;
     Platform mPlatform = Platform.Android;
+    public enum ScreenIdentificationTag
+    {
+        // 屏幕定位的方式,手动、半自动、自动
+        Manual,
+        SemiAuto,
+        Auto
+    }
 
 
     // 2个灯,顺序根据红外灯的大小 由大到小, 坐标通过 InfraredSpot.ScreenUV 和 InfraredSpot.CameraLocation 获得
     // 2个灯,顺序根据红外灯的大小 由大到小, 坐标通过 InfraredSpot.ScreenUV 和 InfraredSpot.CameraLocation 获得
     public InfraredSpot[] InfraredSpots
     public InfraredSpot[] InfraredSpots
@@ -61,6 +68,17 @@ public partial class ScreenLocate : MonoBehaviour
     public InfraredSpot[] infraredSpotBuffer;
     public InfraredSpot[] infraredSpotBuffer;
     public string GetInfraredCount() { return infraredCount.ToString(); }
     public string GetInfraredCount() { return infraredCount.ToString(); }
 
 
+    /// <summary>
+    /// 定位之后,可能有3种结果(手动、半自动、自动),从中选择一种作为最终识别到的屏幕。
+    /// 如果选择的是null,即没有识别到屏幕,则返回false,否则返回true
+    /// </summary>
+    public bool SelectScreenAfterLocate(ScreenIdentificationTag tag) => ScreenIdentification.SelectScreenAfterLocate(tag);
+
+    /// <summary>
+    /// 获取算法执行过程中输出的纹理,0原图,1半自动识别到的全部线段,2屏幕黑白色差,3识别结果,4屏幕色差叠加识别结果,5半自动时的备选线段
+    /// </summary>
+    public Texture2D[] OutputTextures => outputTexture2D;
+
     /// <summary>
     /// <summary>
     /// CameraLocation 的偏移量
     /// CameraLocation 的偏移量
     /// </summary>
     /// </summary>
@@ -99,15 +117,15 @@ public partial class ScreenLocate : MonoBehaviour
 
 
     public bool ShowScreenQuad = false;
     public bool ShowScreenQuad = false;
 
 
-    // output的图像
-    // 图0是摄像机原图,图1是屏幕识别的全部可选线段,图2是识别出的屏幕画面,图3是识别出的屏幕四条边,图4是图2和图3的叠加,图5显示3种不同颜色的算法识别线段
+    // 显示在demo上的rawImage
     public List<RawImage> outputRawImages;
     public List<RawImage> outputRawImages;
-    [NonSerialized] public Texture[] outputTexture2D = new Texture[8];
+    readonly Texture2D[] outputTexture2D = new Texture2D[8];
     public RawImage FullScreenImage;
     public RawImage FullScreenImage;
 
 
     public PixelCheaker ScreenPixelCheaker;
     public PixelCheaker ScreenPixelCheaker;
     public InfraredSpotSettings InfraredSpotSettings;
     public InfraredSpotSettings InfraredSpotSettings;
 
 
+    // 全局记录当前算法中的CameraSize,红外识别和屏幕识别都会使用到
     public o0.Geometry2D.Vector<int> CameraSize { get; set; }
     public o0.Geometry2D.Vector<int> CameraSize { get; set; }
 
 
     public List<Texture2D> DebugScreenImages = new List<Texture2D>();
     public List<Texture2D> DebugScreenImages = new List<Texture2D>();
@@ -118,9 +136,9 @@ public partial class ScreenLocate : MonoBehaviour
     //是否单点显示
     //是否单点显示
     public bool bSinglePoint = true;//默认单点识别
     public bool bSinglePoint = true;//默认单点识别
 
 
-    [NonSerialized] public float ReDoLocateCalibrationRatio = 0.08f;  // 重复定位时校准的距离比例,例如先手动定位,再自动定位,会以手动的结果来校准
+    [NonSerialized] public float ReDoLocateCalibrationRatio = 0.12f;  // 半自动定位时校准的距离比例,以手动的结果来校准,离手动太远的线段会被舍弃
 
 
-    [NonSerialized] public InfraredCount infraredCount = InfraredCount.Single;
+    [NonSerialized] public InfraredCount infraredCount = InfraredCount.Single;  // 识别红外灯的数量,1个或者2个
 
 
     bool bIdentifyRed = true;//默认设备红色
     bool bIdentifyRed = true;//默认设备红色
 
 
@@ -151,15 +169,17 @@ public partial class ScreenLocate : MonoBehaviour
     public float pcContrast { get; set; } = 0.0f;
     public float pcContrast { get; set; } = 0.0f;
     #endregion
     #endregion
 
 
-
+    // 红外灯识别算法
     InfraredLocate infraredLocate;
     InfraredLocate infraredLocate;
+    // 屏幕识别算法
+    o0.Project.ScreenIdentification screenIdentification;
+    public o0.Project.ScreenIdentification ScreenIdentification => screenIdentification;
+
     RectTransform canvas;
     RectTransform canvas;
     Mode mode;
     Mode mode;
-    List<(Vector2 pos, GameObject go)> pointManual = new List<(Vector2, GameObject)>();
-
+    //List<(Vector2 pos, GameObject go)> pointManual = new List<(Vector2, GameObject)>();
     //o0.Project.WebCam o0WebCam = null;
     //o0.Project.WebCam o0WebCam = null;
-    o0.Project.ScreenIdentification screenIdentification;
-    public o0.Project.ScreenIdentification ScreenIdentification => screenIdentification;
+
 
 
     /// <summary>
     /// <summary>
     /// 正在识别的状态,自动识别时候记录
     /// 正在识别的状态,自动识别时候记录
@@ -186,7 +206,7 @@ public partial class ScreenLocate : MonoBehaviour
         }
         }
     }
     }
 
 
-    static public void DebugTexture(int index, Texture texture)
+    static public void DebugTexture(int index, Texture2D texture)
     {
     {
         LateDestory(Main.outputTexture2D[index]);
         LateDestory(Main.outputTexture2D[index]);
 
 
@@ -240,24 +260,23 @@ public partial class ScreenLocate : MonoBehaviour
         }
         }
     }
     }
 
 
-    public void ReSizeTexture(int width, int height)
-    {
-        Debug.Log("Cur mUVCTexture Size: [" + mUVCTexture.width + "," + mUVCTexture.height + "]");
-        return;
-        if (mUVCTexture.width < width || mUVCTexture.height < height)       // 如果当前分辨率太小,则重新new一个texture
-        {
-            Texture2D tex = new Texture2D(
-                            width, height,
-                            TextureFormat.ARGB32,
-                            false, /* mipmap */
-                            true /* linear */);
-            tex.filterMode = FilterMode.Point;
-            tex.Apply();
-            mUVCTexture = tex;
-            mUVCCameraInfo.previewTexture = tex;
-            var nativeTexPtr = mUVCCameraInfo.previewTexture.GetNativeTexturePtr();
-        }
-    }
+    //public void ReSizeTexture(int width, int height)
+    //{
+    //    Debug.Log("Cur mUVCTexture Size: [" + mUVCTexture.width + "," + mUVCTexture.height + "]");
+    //    if (mUVCTexture.width < width || mUVCTexture.height < height)       // 如果当前分辨率太小,则重新new一个texture
+    //    {
+    //        Texture2D tex = new Texture2D(
+    //                        width, height,
+    //                        TextureFormat.ARGB32,
+    //                        false, /* mipmap */
+    //                        true /* linear */);
+    //        tex.filterMode = FilterMode.Point;
+    //        tex.Apply();
+    //        mUVCTexture = tex;
+    //        mUVCCameraInfo.previewTexture = tex;
+    //        var nativeTexPtr = mUVCCameraInfo.previewTexture.GetNativeTexturePtr();
+    //    }
+    //}
 
 
     void Awake()
     void Awake()
     {
     {
@@ -429,7 +448,7 @@ public partial class ScreenLocate : MonoBehaviour
             }
             }
          
          
             if (DebugOnZIMDemo)
             if (DebugOnZIMDemo)
-                Main.ShowScreen(Main.ScreenQuad, screenIdentification.Screen.QuadInCamera);
+                Main.ShowScreen(screenIdentification.Screen.QuadInCamera);
 
 
         }
         }
 
 
@@ -905,18 +924,18 @@ public partial class ScreenLocate : MonoBehaviour
         }
         }
         Info.text = "已识别到屏幕";
         Info.text = "已识别到屏幕";
 
 
-        if (ScreenQuadObject && ScreenQuadObject.childCount >= 4)
-        {
-            ScreenQuadObject.gameObject.SetActive(true);
-            for (int i = 0; i < 4; i++)
-            {
-                if (DebugOnZIMDemo)
-                {
-                    RectTransform t = ScreenQuadObject.GetChild(i) as RectTransform;
-                    t.anchoredPosition = screen.Quad[i].pixelToLocalPosition_AnchorCenter(screen.CameraSize, ScreenQuadObject.rect);
-                }
-            }
-        }
+        //if (ScreenQuadObject && ScreenQuadObject.childCount >= 4)
+        //{
+        //    ScreenQuadObject.gameObject.SetActive(true);
+        //    for (int i = 0; i < 4; i++)
+        //    {
+        //        if (DebugOnZIMDemo)
+        //        {
+        //            RectTransform t = ScreenQuadObject.GetChild(i) as RectTransform;
+        //            t.anchoredPosition = screen.Quad[i].pixelToLocalPosition_AnchorCenter(screen.CameraSize, ScreenQuadObject.rect);
+        //        }
+        //    }
+        //}
         quadUnityVectorList = screen.GetUnityVertexNormalizedList();      // 记录四个点
         quadUnityVectorList = screen.GetUnityVertexNormalizedList();      // 记录四个点
         if (!ContainsNaN(quadUnityVectorList))
         if (!ContainsNaN(quadUnityVectorList))
         {
         {

+ 29 - 1
Assets/InfraredProject/WebCamera/Script/ZIM/ZIMUnity/Extension.cs

@@ -65,7 +65,12 @@ namespace ZIM.Unity
 
 
         public static float Brightness(this Color c)
         public static float Brightness(this Color c)
         {
         {
-            return 0.59f * c.r + 0.3f * c.r + 0.11f * c.r;    // 红色为主
+            return 0.59f * c.r + 0.3f * c.g + 0.11f * c.b;    // 红色为主
+        }
+
+        public static float Lightness(this Color c)
+        {
+            return Mathf.Max(c.r, c.g, c.b);
         }
         }
 
 
         // 转换为整数再计算
         // 转换为整数再计算
@@ -78,6 +83,7 @@ namespace ZIM.Unity
             return (int)(0.59f * r + 0.3f * g + 0.11f * b);    // 红色为主
             return (int)(0.59f * r + 0.3f * g + 0.11f * b);    // 红色为主
         }
         }
 
 
+        // a 融合b,颜色直接相加
         public static Texture2D Merge(this Texture2D a, Texture2D b)
         public static Texture2D Merge(this Texture2D a, Texture2D b)
         {
         {
             if (a.width != b.width || a.height != b.height)
             if (a.width != b.width || a.height != b.height)
@@ -93,6 +99,28 @@ namespace ZIM.Unity
             return texAdd;
             return texAdd;
         }
         }
 
 
+        // a 覆盖 b,会剔除a的黑色背景
+        public static Texture2D Overlay(this Texture2D a, Texture2D b)
+        {
+            if (a.width != b.width || a.height != b.height)
+                throw new InvalidOperationException("尺寸不同无法叠加texture");
+
+            var pixel0 = a.GetPixels();
+            var pixel1 = b.GetPixels();
+            for (int i = 0; i < pixel0.Length; i++)
+            {
+                float newA = (float)(Math.Clamp((pixel0[i].Lightness() - 0.2) / 0.6, 0, 1) * pixel0[i].a);
+                pixel0[i] = pixel0[i].RGBMutiply(newA) + pixel1[i] * (1 - newA);
+            }
+            var texAdd = new Texture2D(a.width, a.height);
+            texAdd.SetPixels(pixel0);
+            texAdd.Apply();
+            return texAdd;
+        }
+
+        // 长度平方,0-1
+        public static float LengthSquare(this Color c) => (c.r * c.r + c.g * c.g + c.b * c.b) / 3;
+
         // 像素坐标映射到Unity局部坐标
         // 像素坐标映射到Unity局部坐标
         public static Vector2 pixelToLocalPosition_AnchorCenter(this Vector2 pixel, Vector2 size, Rect dstRect)
         public static Vector2 pixelToLocalPosition_AnchorCenter(this Vector2 pixel, Vector2 size, Rect dstRect)
         {
         {

+ 4 - 2
Assets/InfraredProject/WebCamera/Script/ZIM/o0Extension/o0Extension.cs

@@ -7,6 +7,8 @@ namespace o0.Project
     {
     {
         public static void DrawLine(Matrix matrix, Line line, Func<int, int, float> setValue, Geometry2D.Float.Vector size = default, bool dashedLine = false)
         public static void DrawLine(Matrix matrix, Line line, Func<int, int, float> setValue, Geometry2D.Float.Vector size = default, bool dashedLine = false)
         {
         {
+            if (line == null || matrix == null)
+                return;
             if (size == default)
             if (size == default)
                 size = new Geometry2D.Float.Vector(0, 1);
                 size = new Geometry2D.Float.Vector(0, 1);
             if (size.x != 0)
             if (size.x != 0)
@@ -16,14 +18,14 @@ namespace o0.Project
                 line = new Geometry2D.Float.Line(line.A - offset, line.B + offset);
                 line = new Geometry2D.Float.Line(line.A - offset, line.B + offset);
             }
             }
 
 
-            int drawCount = 0;
             void _draw(int x, int y)
             void _draw(int x, int y)
             {
             {
                 try
                 try
                 {
                 {
                     if (dashedLine)
                     if (dashedLine)
                     {
                     {
-                        if (drawCount++ % 80 < 50)
+                        var d = (new Vector(x, y) - line.A).Length;
+                        if (d % 75 < 35)
                             matrix[x, y] = setValue(x, y);
                             matrix[x, y] = setValue(x, y);
                     }
                     }
                     else
                     else

文件差異過大導致無法顯示
+ 717 - 52
Assets/InfraredProject/WebCamera/zimWebCamera.unity


+ 27 - 17
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.cs

@@ -1,4 +1,5 @@
-using System;
+using o0.Geometry2D;
+using System;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using Unity.VisualScripting;
 using Unity.VisualScripting;
@@ -11,29 +12,38 @@ public class PixelCheaker : MonoBehaviour
     public Image Bordermage;            // 椭圆的图片
     public Image Bordermage;            // 椭圆的图片
     public RectTransform BorderArea;    // 椭圆的大小区域,调整这个gameobject的scale作为椭圆的区域
     public RectTransform BorderArea;    // 椭圆的大小区域,调整这个gameobject的scale作为椭圆的区域
 
 
-    private Vector2 _imageSize;
-    public Vector2 ImageSize
+    public void Start()
     {
     {
-        get => _imageSize;
-        set
-        {
-            if (_imageSize != value)
-            {
-                _imageSize = value;
-                ellipseRadius = BorderArea.localScale / 2f;
-            }
-        }
+        ellipseRadius = BorderArea.localScale / 2f;
     }
     }
 
 
-    private Vector2 ellipseRadius; 
+    private Vector2 ellipseRadius;
 
 
-    public bool OutCollider2D(Vector2 p)
+    public bool OutArea2D(Vector2 pixelPosition, Vector<int> imageSize, out float f) => OutArea2D(new Vector2(pixelPosition.x / imageSize.x, pixelPosition.y / imageSize.y), out f);
+    public bool OutArea2D(Vector2 pixelPosition, Vector2 imageSize, out float f) => OutArea2D(pixelPosition / imageSize, out f);
+    public bool OutArea2D(o0.Geometry2D.Float.Vector pixelPosition, Vector<int> imageSize, out float f) => OutArea2D(new Vector2(pixelPosition.x / imageSize.x, pixelPosition.y / imageSize.y), out f);
+    public bool OutArea2D(Vector2 pixelPosition, Vector<int> imageSize) => OutArea2D(new Vector2(pixelPosition.x / imageSize.x, pixelPosition.y / imageSize.y));
+    public bool OutArea2D(Vector2 pixelPosition, Vector2 imageSize) => OutArea2D(pixelPosition / imageSize);
+    public bool OutArea2D(o0.Geometry2D.Float.Vector pixelPosition, Vector<int> imageSize) => OutArea2D(new Vector2(pixelPosition.x / imageSize.x, pixelPosition.y / imageSize.y));
+
+    // out 椭圆的距离系数,越大表示离椭圆越远
+    private bool OutArea2D(Vector2 pixelUV, out float f)
     {
     {
         Vector2 center = new Vector2(0.5f, 0.5f);           // 椭圆中心
         Vector2 center = new Vector2(0.5f, 0.5f);           // 椭圆中心
+        float normalizedX = (pixelUV.x - center.x) / ellipseRadius.x;
+        float normalizedY = (pixelUV.y - center.y) / ellipseRadius.y;
 
 
-        p /= ImageSize;
-        float normalizedX = (p.x - center.x) / ellipseRadius.x;
-        float normalizedY = (p.y - center.y) / ellipseRadius.y;
+        float value = (normalizedX * normalizedX) + (normalizedY * normalizedY);    // 使用椭圆方程判断
+        f = (float)Math.Sqrt(value);
+        return value > 1.0f;
+    }
+
+    // 无out的版本
+    private bool OutArea2D(Vector2 pixelUV)
+    {
+        Vector2 center = new Vector2(0.5f, 0.5f);           // 椭圆中心
+        float normalizedX = (pixelUV.x - center.x) / ellipseRadius.x;
+        float normalizedY = (pixelUV.y - center.y) / ellipseRadius.y;
 
 
         float value = (normalizedX * normalizedX) + (normalizedY * normalizedY);    // 使用椭圆方程判断
         float value = (normalizedX * normalizedX) + (normalizedY * normalizedY);    // 使用椭圆方程判断
         return value > 1.0f;
         return value > 1.0f;

+ 41 - 11
Assets/SmartBow/Scripts/Views/InfraredViewParts/InfraredScreenPositioningView.cs

@@ -10,6 +10,7 @@ using Color = UnityEngine.Color;
 using InfraredManager;
 using InfraredManager;
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.Crmf;
 using ZIM.Unity;
 using ZIM.Unity;
+using o0.Project;
 
 
 public class LinePosition
 public class LinePosition
 {
 {
@@ -72,6 +73,8 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     [SerializeField] Color normalTextColor = Color.black;
     [SerializeField] Color normalTextColor = Color.black;
     [SerializeField] Color highlightedTextColor = Color.white;
     [SerializeField] Color highlightedTextColor = Color.white;
 
 
+    bool doLocateAuto;
+
     int DefaultResolutionIndex;
     int DefaultResolutionIndex;
     private void Awake()
     private void Awake()
     {
     {
@@ -81,6 +84,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
 
 
     void Start()
     void Start()
     {
     {
+        doLocateAuto = false;
 
 
         textTip1.SetActive(true);
         textTip1.SetActive(true);
         //设置btnAuto 高亮
         //设置btnAuto 高亮
@@ -230,6 +234,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     public void OnClick_Auto()
     public void OnClick_Auto()
     {
     {
         bAuto = true;
         bAuto = true;
+        doLocateAuto = true;
 
 
         textTip1.SetActive(false);
         textTip1.SetActive(false);
         textTip2.SetActive(true);
         textTip2.SetActive(true);
@@ -257,6 +262,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     public void onHandMovement()
     public void onHandMovement()
     {
     {
         bAuto = false;
         bAuto = false;
+        doLocateAuto = false;
         draggableParent.gameObject.SetActive(true);
         draggableParent.gameObject.SetActive(true);
         pointsParent.gameObject.SetActive(false);
         pointsParent.gameObject.SetActive(false);
 
 
@@ -416,6 +422,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         if (enterFromZimWebCamera)
         if (enterFromZimWebCamera)
         {
         {
             ConfirmScreenLocateManualTest();
             ConfirmScreenLocateManualTest();
+
             if (oldLinePosition.Count > 1) // 确保列表不为空
             if (oldLinePosition.Count > 1) // 确保列表不为空
             {
             {
                 LinePosition lastElement = oldLinePosition[oldLinePosition.Count - 1];
                 LinePosition lastElement = oldLinePosition[oldLinePosition.Count - 1];
@@ -497,7 +504,12 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         SetLinePos();
         SetLinePos();
 
 
         btnRecordInfrared.Reset();
         btnRecordInfrared.Reset();
-        ScreenLocate.Main.ScreenIdentification.SetScreenQuad(null);
+        ScreenLocate.Main.ScreenIdentification.ClearQuadCache();
+        if (enterFromZimWebCamera)
+        {
+            ScreenLocate.Main.ScreenQuad.gameObject.SetActive(false);
+            ScreenLocate.Main.UILineGenerator.Points = new Vector2[0];
+        }
     }
     }
 
 
     #endregion
     #endregion
@@ -579,8 +591,9 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         _screenLocate.QuitScreenLocateManual(_locatePointList);
         _screenLocate.QuitScreenLocateManual(_locatePointList);
         //FindObjectOfType<InfraredDemo>().SetLocatePointsToCameraRender(_locatePointList, _texWidth, _texHeight);
         //FindObjectOfType<InfraredDemo>().SetLocatePointsToCameraRender(_locatePointList, _texWidth, _texHeight);
 
 
-        setPointsLocation(_locatePointList,GameObject.Find("WebCameraView/CameraImage0/ScreenQuad"));
         setPointsLocation(_locatePointList,pointsParent.gameObject,!bAuto);
         setPointsLocation(_locatePointList,pointsParent.gameObject,!bAuto);
+        if (!doLocateAuto)  // 设置手动定位数据
+            setPointsManual(_locatePointList, GameObject.Find("WebCameraView/CameraImage0/ScreenQuad"));
 
 
         //同步数据
         //同步数据
         ScreenLocate.quadUnityVectorList.Clear();
         ScreenLocate.quadUnityVectorList.Clear();
@@ -591,22 +604,15 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         ScreenLocate.quadUnityVectorList.Add(new Vector2(_locatePointList[2].x, _locatePointList[2].y));
         ScreenLocate.quadUnityVectorList.Add(new Vector2(_locatePointList[2].x, _locatePointList[2].y));
         ScreenLocate.SaveScreenLocateVectorList();
         ScreenLocate.SaveScreenLocateVectorList();
 
 
-        var lo = new Vector2(-0.5f, -0.5f);
-        ScreenLocate.Main.UILineGenerator.Points = new Vector2[4] {
-            2 * (_locatePointList[0] + lo),
-            2 * (_locatePointList[1] + lo),
-            2 * (_locatePointList[2] + lo),
-            2 * (_locatePointList[3] + lo) };
     }
     }
 
 
-    void setPointsLocation(List<Vector2> targetList ,GameObject pointsTF2,bool active = true) {
-
+    void setPointsLocation(List<Vector2> targetList, GameObject pointsTF2, bool active = true)
+    {
         //GameObject pointsTF2 = GameObject.Find("WebCameraView/CameraImage0/ScreenQuad");
         //GameObject pointsTF2 = GameObject.Find("WebCameraView/CameraImage0/ScreenQuad");
         pointsTF2.SetActive(active);
         pointsTF2.SetActive(active);
 
 
         if (pointsTF2.transform.childCount == targetList.Count)
         if (pointsTF2.transform.childCount == targetList.Count)
         {
         {
-            Vector2 texSize = new Vector2(_texWidth, _texHeight);
             for (int i = 0; i < pointsTF2.transform.childCount; i++)
             for (int i = 0; i < pointsTF2.transform.childCount; i++)
             {
             {
                 Transform pointTF = pointsTF2.transform.GetChild(i);
                 Transform pointTF = pointsTF2.transform.GetChild(i);
@@ -615,8 +621,32 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
                 pointTF.gameObject.SetActive(true);
                 pointTF.gameObject.SetActive(true);
             }
             }
         }
         }
+        
     }
     }
 
 
+    void setPointsManual(List<Vector2> targetList, GameObject pointsTF2, bool active = true)
+    {
+        setPointsLocation(targetList, pointsTF2, active);
+
+        var lo = new Vector2(-0.5f, -0.5f);
+        ScreenLocate.Main.UILineGenerator.Points = new Vector2[4] {
+                2 * (targetList[0] + lo),
+                2 * (targetList[1] + lo),
+                2 * (targetList[2] + lo),
+                2 * (targetList[3] + lo) };
+
+        // 记录手动数据
+        var size = ScreenLocate.Main.getUVCCameraInfoSize;
+        var quad = new QuadrilateralInCamera(
+                new o0.Geometry2D.Float.Vector(targetList[0].x * size.x, targetList[0].y * size.y),
+                new o0.Geometry2D.Float.Vector(targetList[1].x * size.x, targetList[1].y * size.y),
+                new o0.Geometry2D.Float.Vector(targetList[3].x * size.x, targetList[3].y * size.y),
+                new o0.Geometry2D.Float.Vector(targetList[2].x * size.x, targetList[2].y * size.y),
+                size.o0Vector()
+            );
+        ScreenLocate.Main.ScreenIdentification.QuadManual = quad;
+        Debug.Log("<color=aqua>[ScreenLocate] 记录手动数据</color>");
+    }
 
 
     void SaveLocalPos()
     void SaveLocalPos()
     {
     {

部分文件因文件數量過多而無法顯示