瀏覽代碼

加个识别区域的圆框,增加可用红外灯进行手动定位屏幕的功能

ZIM 1 年之前
父節點
當前提交
b6a60bb8fc
共有 64 個文件被更改,包括 2720 次插入585 次删除
  1. 1 0
      Assets/BowArrow/InfraredCamera/InfraredDemo.cs
  2. 1 0
      Assets/BowArrow/Modules/InfraredGuider/InfraredLightGuider.cs
  3. 3 3
      Assets/InfraredProject/InfraredCamera/Scripts/UVCManager.cs
  4. 89 17
      Assets/InfraredProject/Resources/WebCameraView_Zim.prefab
  5. 二進制
      Assets/InfraredProject/TestImage/测试图片.png
  6. 二進制
      Assets/InfraredProject/WebCamera/Image/测试图片.png
  7. 10 22
      Assets/InfraredProject/WebCamera/Image/测试图片.png.meta
  8. 8 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan.meta
  9. 36 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Dbscan.cs
  10. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Dbscan.cs.meta
  11. 222 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanAlgorithm.cs
  12. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanAlgorithm.cs.meta
  13. 27 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanPoint.cs
  14. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanPoint.cs.meta
  15. 21 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanResult.cs
  16. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanResult.cs.meta
  17. 8 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing.meta
  18. 10 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/EmptyDbscanEventPublisher.cs
  19. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/EmptyDbscanEventPublisher.cs.meta
  20. 144 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/Events.cs
  21. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/Events.cs.meta
  22. 13 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventPublisher.cs
  23. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventPublisher.cs.meta
  24. 9 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventSubscriber.cs
  25. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventSubscriber.cs.meta
  26. 9 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/PointType.cs
  27. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/PointType.cs.meta
  28. 8 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding.meta
  29. 37 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding/DbscanResultBuilder.cs
  30. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding/DbscanResultBuilder.cs.meta
  31. 287 227
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredLocate.cs
  32. 3 3
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/InfraredSpot.cs
  33. 8 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old.meta
  34. 193 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/KMeans.cs
  35. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/KMeans.cs.meta
  36. 1 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelArea.cs
  37. 1 1
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelArea.cs.meta
  38. 0 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelCircleArea.cs
  39. 1 1
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelCircleArea.cs.meta
  40. 124 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelSpotAreaOld.cs
  41. 11 0
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelSpotAreaOld.cs.meta
  42. 59 143
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelSpotArea.cs
  43. 11 9
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenIdentification.cs
  44. 24 8
      Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenMap.cs
  45. 96 108
      Assets/InfraredProject/WebCamera/Script/ZIM/ScreenLocate.cs
  46. 33 9
      Assets/InfraredProject/WebCamera/Script/ZIM/ZIMUnity/Extension.cs
  47. 1 0
      Assets/InfraredProject/WebCamera/Script/ZIM/ZIMUnity/ZIMWebCamera.cs
  48. 16 15
      Assets/InfraredProject/WebCamera/Test.cs
  49. 266 1
      Assets/InfraredProject/WebCamera/zimWebCamera.unity
  50. 390 3
      Assets/SmartBow/Resources/SmartBow/Prefabs/Views/Home/InfraredScreenPositioningView.prefab
  51. 8 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM.meta
  52. 43 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/BtnRecordInfrared.cs
  53. 11 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/BtnRecordInfrared.cs.meta
  54. 64 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.cs
  55. 11 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.cs.meta
  56. 110 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.prefab
  57. 7 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.prefab.meta
  58. 二進制
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/screen_area.png
  59. 123 0
      Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/screen_area.png.meta
  60. 24 3
      Assets/SmartBow/Scripts/Views/InfraredViewParts/InfraredScreenPositioningView.cs
  61. 9 9
      Packages/packages-lock.json
  62. 1 1
      ProjectSettings/PackageManagerSettings.asset
  63. 5 0
      ProjectSettings/ProjectSettings.asset
  64. 2 2
      ProjectSettings/ProjectVersion.txt

+ 1 - 0
Assets/BowArrow/InfraredCamera/InfraredDemo.cs

@@ -8,6 +8,7 @@ using System.Linq;
 using SLAMUVC;
 using System;
 using SmartBowSDK;
+using ZIM.Unity;
 
 public class InfraredDemo : MonoBehaviour
 {

+ 1 - 0
Assets/BowArrow/Modules/InfraredGuider/InfraredLightGuider.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.UI;
 using ZIM;
+using ZIM.Unity;
 
 //处理光源检测部分
 public class InfraredLightGuider : MonoBehaviour

+ 3 - 3
Assets/InfraredProject/InfraredCamera/Scripts/UVCManager.cs

@@ -31,8 +31,8 @@ namespace SLAMUVC
         protected UVCInterface _interface;
 
         /**
-		 * 保持正在使用的相机信息
-		 */
+         * 保持正在使用的相机信息
+         */
         public class CameraInfo
         {
             //internal readonly UVCDevice device;
@@ -155,7 +155,7 @@ namespace SLAMUVC
                 if (width != currentWidth || height != currentHeight)
                 {
                     bChange = true;
-                    uvcInterface.ChangeCameraInfo(width, height);
+                    uvcInterface?.ChangeCameraInfo(width, height);
                 }
                 return bChange;
             }

+ 89 - 17
Assets/InfraredProject/Resources/WebCameraView_Zim.prefab

@@ -546,7 +546,8 @@ RectTransform:
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
-  m_Children: []
+  m_Children:
+  - {fileID: 1816618028747326785}
   m_Father: {fileID: 1816618027770806189}
   m_RootOrder: 6
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -607,7 +608,7 @@ GameObject:
   m_Icon: {fileID: 0}
   m_NavMeshLayer: 0
   m_StaticEditorFlags: 0
-  m_IsActive: 0
+  m_IsActive: 1
 --- !u!224 &1816618027212933218
 RectTransform:
   m_ObjectHideFlags: 0
@@ -625,7 +626,7 @@ RectTransform:
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
-  m_AnchoredPosition: {x: 605, y: -143.15001}
+  m_AnchoredPosition: {x: -779, y: -829}
   m_SizeDelta: {x: 720, y: 420}
   m_Pivot: {x: 0.5, y: 0.5}
 --- !u!222 &1816618027212933223
@@ -1055,7 +1056,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 11
+  m_RootOrder: 12
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
@@ -1602,7 +1603,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 13
+  m_RootOrder: 14
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 1, y: 1}
@@ -1946,6 +1947,7 @@ RectTransform:
   - {fileID: 1816618027165090663}
   - {fileID: 1816618028593834239}
   - {fileID: 1816618027212933218}
+  - {fileID: 3618060985824429039}
   - {fileID: 1816618028674252834}
   - {fileID: 1816618028634983695}
   - {fileID: 1816618027391060325}
@@ -1979,6 +1981,7 @@ Canvas:
   m_OverrideSorting: 0
   m_OverridePixelPerfect: 0
   m_SortingBucketNormalizedSize: 0
+  m_VertexColorAlwaysGammaSpace: 0
   m_AdditionalShaderChannelsFlag: 25
   m_SortingLayerID: 0
   m_SortingOrder: 0
@@ -2056,8 +2059,6 @@ MonoBehaviour:
   m_Script: {fileID: 11500000, guid: bbb133b9c79cd2d49970b410adbdf946, type: 3}
   m_Name: 
   m_EditorClassIdentifier: 
-  mUVCManager: {fileID: 0}
-  mUVCDrawer: {fileID: 0}
   Info: {fileID: 1816618028674252833}
   CrosshairInCamera:
   - {fileID: 1816618027331440740}
@@ -2084,7 +2085,6 @@ MonoBehaviour:
   updateInterval: 0.5
   m_FPS: {fileID: 1816618028690878102}
   filterDis: 3
-  PreferH264: 0
 --- !u!1 &1816618027789859626
 GameObject:
   m_ObjectHideFlags: 0
@@ -3013,8 +3013,7 @@ RectTransform:
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
-  m_Children:
-  - {fileID: 1816618028747326785}
+  m_Children: []
   m_Father: {fileID: 1816618027770806189}
   m_RootOrder: 7
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -3089,7 +3088,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 14
+  m_RootOrder: 15
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
@@ -3304,7 +3303,7 @@ RectTransform:
   - {fileID: 1816618027076889066}
   - {fileID: 1816618027006703082}
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 10
+  m_RootOrder: 11
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
@@ -3390,7 +3389,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 9
+  m_RootOrder: 10
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0.5}
   m_AnchorMax: {x: 0, y: 0.5}
@@ -3470,7 +3469,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 16
+  m_RootOrder: 17
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 1}
   m_AnchorMax: {x: 0, y: 1}
@@ -3632,7 +3631,7 @@ RectTransform:
   - {fileID: 1816618027696445180}
   - {fileID: 1816618028488416755}
   - {fileID: 1816618029068553909}
-  m_Father: {fileID: 1816618028593834239}
+  m_Father: {fileID: 1816618027165090663}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
@@ -3839,7 +3838,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 15
+  m_RootOrder: 16
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 1}
   m_AnchorMax: {x: 0, y: 1}
@@ -4076,7 +4075,7 @@ RectTransform:
   - {fileID: 1816618027063368961}
   - {fileID: 1816618027631545811}
   m_Father: {fileID: 1816618027770806189}
-  m_RootOrder: 12
+  m_RootOrder: 13
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 1, y: 1}
@@ -4337,3 +4336,76 @@ MonoBehaviour:
     y: 0
     width: 1
     height: 1
+--- !u!1 &8629019211747603175
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3618060985824429039}
+  - component: {fileID: 8038113556625826093}
+  - component: {fileID: 4268820032225842432}
+  m_Layer: 5
+  m_Name: CameraImage6
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &3618060985824429039
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629019211747603175}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 1816618027770806189}
+  m_RootOrder: 9
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 15, y: -829}
+  m_SizeDelta: {x: 720, y: 420}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8038113556625826093
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629019211747603175}
+  m_CullTransparentMesh: 1
+--- !u!114 &4268820032225842432
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629019211747603175}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Texture: {fileID: 0}
+  m_UVRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1

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


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


+ 10 - 22
Assets/InfraredProject/TestImage/测试图片.png.meta → Assets/InfraredProject/WebCamera/Image/测试图片.png.meta

@@ -1,12 +1,12 @@
 fileFormatVersion: 2
-guid: f894060e71995eb41a8fd983d40deeb3
+guid: f0f6c5286b3b4524baae12fbf5cfa115
 TextureImporter:
   internalIDToNameTable: []
   externalObjects: {}
   serializedVersion: 12
   mipmaps:
     mipMapMode: 0
-    enableMipMap: 1
+    enableMipMap: 0
     sRGBTexture: 1
     linearTexture: 0
     fadeOut: 0
@@ -36,13 +36,13 @@ TextureImporter:
     filterMode: 1
     aniso: 1
     mipBias: 0
-    wrapU: 0
-    wrapV: 0
+    wrapU: 1
+    wrapV: 1
     wrapW: 0
-  nPOTScale: 1
+  nPOTScale: 0
   lightmap: 0
   compressionQuality: 50
-  spriteMode: 0
+  spriteMode: 1
   spriteExtrude: 1
   spriteMeshType: 1
   alignment: 0
@@ -51,9 +51,9 @@ TextureImporter:
   spriteBorder: {x: 0, y: 0, z: 0, w: 0}
   spriteGenerateFallbackPhysicsShape: 1
   alphaUsage: 1
-  alphaIsTransparency: 0
+  alphaIsTransparency: 1
   spriteTessellationDetail: -1
-  textureType: 0
+  textureType: 8
   textureShape: 1
   singleChannelComponent: 0
   flipbookRows: 1
@@ -67,18 +67,6 @@ TextureImporter:
   platformSettings:
   - serializedVersion: 3
     buildTarget: DefaultTexturePlatform
-    maxTextureSize: 512
-    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
@@ -90,7 +78,7 @@ TextureImporter:
     androidETC2FallbackOverride: 0
     forceMaximumCompressionQuality_BC6H_BC7: 0
   - serializedVersion: 3
-    buildTarget: Server
+    buildTarget: Standalone
     maxTextureSize: 2048
     resizeAlgorithm: 0
     textureFormat: -1
@@ -119,7 +107,7 @@ TextureImporter:
     outline: []
     physicsShape: []
     bones: []
-    spriteID: 
+    spriteID: 5e97eb03825dee720800000000000000
     internalID: 0
     vertices: []
     indices: 

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f12f935d3ae2be54a940fd228da3f018
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 36 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Dbscan.cs

@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using UnityEngine;
+using System;
+
+namespace DbscanImplementation
+{
+    public class Dbscan
+    {
+        public static DbscanResult<Vector2> Run(List<Vector2> features, Func<Vector2, Vector2, double> metricFunc, double epsilon, int minimumPoints)
+        {
+            var simpleDbscan = new DbscanAlgorithm<Vector2>(metricFunc);
+
+            return simpleDbscan.ComputeClusterDbscan(allPoints: features.ToArray(),
+                epsilon: epsilon, minimumPoints: minimumPoints);
+
+            //Console.WriteLine($"Noise: {result.Noise.Count}");
+            //Console.WriteLine($"# of Clusters: {result.Clusters.Count}");
+        }
+
+        /// <summary>
+        /// Example distance function
+        /// </summary>
+        /// <param name="feature1"></param>
+        /// <param name="feature2"></param>
+        /// <returns></returns>
+        private static double ExampleDistanceFunction(Vector2 feature1, Vector2 feature2)
+        {
+            return Mathf.Sqrt(
+                    ((feature1.x - feature2.x) * (feature1.x - feature2.x)) +
+                    ((feature1.y - feature2.y) * (feature1.y - feature2.y))
+                );
+        }
+    }
+
+
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Dbscan.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 671c2bf65ecff6043a8606872b221ac1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 222 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanAlgorithm.cs

@@ -0,0 +1,222 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using DbscanImplementation.Eventing;
+using DbscanImplementation.ResultBuilding;
+
+namespace DbscanImplementation
+{
+    /// <summary>
+    /// DBSCAN algorithm implementation type
+    /// </summary>
+    /// <typeparam name="TF">Takes dataset item row (features, preferences, vector)</typeparam>
+    public class DbscanAlgorithm<TF>
+    {
+        /// <summary>
+        /// distance calculation metric function between two feature
+        /// </summary>
+        public readonly Func<TF, TF, double> MetricFunction;
+
+        /// <summary>
+        /// Curried Function that checking two feature as neighbor 
+        /// </summary>
+        public readonly Func<TF, double, Func<DbscanPoint<TF>, bool>> RegionQueryPredicate;
+
+        private readonly IDbscanEventPublisher publisher;
+
+        /// <summary>
+        /// Takes metric function to compute distances between two <see cref="TF"/>
+        /// </summary>
+        /// <param name="metricFunc"></param>
+        public DbscanAlgorithm(Func<TF, TF, double> metricFunc)
+        {
+            MetricFunction = metricFunc ?? throw new ArgumentNullException(nameof(metricFunc));
+
+            RegionQueryPredicate =
+                (mainFeature, epsilon) => relatedPoint => MetricFunction(mainFeature, relatedPoint.Feature) <= epsilon;
+
+            publisher = new EmptyDbscanEventPublisher();
+        }
+
+        public DbscanAlgorithm(Func<TF, TF, double> metricFunc, IDbscanEventPublisher publisher)
+            : this(metricFunc)
+        {
+            this.publisher = publisher ?? throw new ArgumentNullException(nameof(publisher));
+        }
+
+        public Task<DbscanResult<TF>> ComputeClusterDbscanAsync(TF[] allPoints, double epsilon, int minimumPoints,
+            CancellationToken cancellationToken)
+        {
+            return Task.Factory.StartNew(() =>
+                ComputeClusterDbscan(allPoints, epsilon, minimumPoints),
+                    cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
+        }
+
+        /// <summary>
+        /// Performs the DBSCAN clustering algorithm.
+        /// </summary>
+        /// <param name="allPoints">feature set</param>
+        /// <param name="epsilon">Desired region ball radius</param>
+        /// <param name="minimumPoints">Minimum number of points to be in a region</param>
+        /// <returns>Overall result of cluster compute operation</returns>
+        public DbscanResult<TF> ComputeClusterDbscan(TF[] allPoints, double epsilon, int minimumPoints)
+        {
+            if (epsilon <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(epsilon), "Must be greater than zero");
+            }
+
+            if (minimumPoints <= 0)
+            {
+                throw new ArgumentOutOfRangeException(nameof(minimumPoints), "Must be greater than zero");
+            }
+
+            var allPointsDbscan = allPoints.Select(x => new DbscanPoint<TF>(x)).ToArray();
+
+            int clusterId = 0;
+
+            var computeId = Guid.NewGuid();
+
+            publisher.Publish(new ComputeStarted(computeId));
+
+            for (int i = 0; i < allPointsDbscan.Length; i++)
+            {
+                var currentPoint = allPointsDbscan[i];
+
+                if (currentPoint.PointType.HasValue)
+                {
+                    publisher.Publish(new PointAlreadyProcessed<TF>(currentPoint));
+
+                    continue;
+                }
+
+                publisher.Publish(new PointProcessStarted<TF>(currentPoint));
+
+                publisher.Publish(new RegionQueryStarted<TF>(currentPoint, epsilon, minimumPoints));
+
+                var neighborPoints = RegionQuery(allPointsDbscan, currentPoint.Feature, epsilon);
+
+                publisher.Publish(new RegionQueryFinished<TF>(currentPoint, neighborPoints));
+
+                if (neighborPoints.Length < minimumPoints)
+                {
+                    currentPoint.PointType = PointType.Noise;
+
+                    publisher.Publish(new PointTypeAssigned<TF>(currentPoint, PointType.Noise));
+
+                    publisher.Publish(new PointProcessFinished<TF>(currentPoint));
+
+                    continue;
+                }
+
+                clusterId++;
+
+                currentPoint.ClusterId = clusterId;
+
+                currentPoint.PointType = PointType.Core;
+
+                publisher.Publish(new PointTypeAssigned<TF>(currentPoint, PointType.Core));
+
+                publisher.Publish(new PointProcessFinished<TF>(currentPoint));
+
+                publisher.Publish(
+                    new ClusteringStarted<TF>(currentPoint, neighborPoints, clusterId, epsilon, minimumPoints));
+
+                ExpandCluster(allPointsDbscan, neighborPoints, clusterId, epsilon, minimumPoints);
+
+                publisher.Publish(
+                    new ClusteringFinished<TF>(currentPoint, neighborPoints, clusterId, epsilon, minimumPoints));
+            }
+
+            publisher.Publish(new ComputeFinished(computeId));
+
+            var resultBuilder = new DbscanResultBuilder<TF>();
+
+            foreach (var p in allPointsDbscan)
+            {
+                resultBuilder.Process(p);
+            }
+
+            return resultBuilder.Result;
+        }
+
+        /// <summary>
+        /// Checks current cluster for expanding it
+        /// </summary>
+        /// <param name="allPoints">Dataset</param>
+        /// <param name="neighborPoints">other points in same region</param>
+        /// <param name="clusterId">given clusterId</param>
+        /// <param name="epsilon">Desired region ball radius</param>
+        /// <param name="minimumPoints">Minimum number of points to be in a region</param>
+        private void ExpandCluster(DbscanPoint<TF>[] allPoints, DbscanPoint<TF>[] neighborPoints,
+            int clusterId, double epsilon, int minimumPoints)
+        {
+            for (int i = 0; i < neighborPoints.Length; i++)
+            {
+                var currentPoint = neighborPoints[i];
+
+                publisher.Publish(new PointProcessStarted<TF>(currentPoint));
+
+                if (currentPoint.PointType == PointType.Noise)
+                {
+                    currentPoint.ClusterId = clusterId;
+
+                    currentPoint.PointType = PointType.Border;
+
+                    publisher.Publish(new PointTypeAssigned<TF>(currentPoint, PointType.Border));
+
+                    publisher.Publish(new PointProcessFinished<TF>(currentPoint));
+
+                    continue;
+                }
+
+                if (currentPoint.PointType.HasValue)
+                {
+                    publisher.Publish(new PointAlreadyProcessed<TF>(currentPoint));
+
+                    continue;
+                }
+
+                currentPoint.ClusterId = clusterId;
+
+                publisher.Publish(new RegionQueryStarted<TF>(currentPoint, epsilon, minimumPoints));
+
+                var otherNeighborPoints = RegionQuery(allPoints, currentPoint.Feature, epsilon);
+
+                publisher.Publish(new RegionQueryFinished<TF>(currentPoint, otherNeighborPoints));
+
+                if (otherNeighborPoints.Length < minimumPoints)
+                {
+                    currentPoint.PointType = PointType.Border;
+
+                    publisher.Publish(new PointTypeAssigned<TF>(currentPoint, PointType.Border));
+
+                    publisher.Publish(new PointProcessFinished<TF>(currentPoint));
+
+                    continue;
+                }
+
+                currentPoint.PointType = PointType.Core;
+
+                publisher.Publish(new PointTypeAssigned<TF>(currentPoint, PointType.Core));
+
+                publisher.Publish(new PointProcessFinished<TF>(currentPoint));
+
+                neighborPoints = neighborPoints.Union(otherNeighborPoints).ToArray();
+            }
+        }
+
+        /// <summary>
+        /// Checks and searchs neighbor points for given point
+        /// </summary>
+        /// <param name="allPoints">Dbscan points converted from feature set</param>
+        /// <param name="mainFeature">Focused feature to be searched neighbors</param>
+        /// <param name="epsilon">Desired region ball radius</param>
+        /// <returns>Calculated neighbor points</returns>
+        public DbscanPoint<TF>[] RegionQuery(DbscanPoint<TF>[] allPoints, TF mainFeature, double epsilon)
+        {
+            return allPoints.Where(RegionQueryPredicate(mainFeature, epsilon)).ToArray();
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanAlgorithm.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1f604995f311b1b49a00e2e2dd632085
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 27 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanPoint.cs

@@ -0,0 +1,27 @@
+using UnityEngine;
+using ZIM;
+
+namespace DbscanImplementation
+{
+    /// <summary>
+    /// Algorithm point definition
+    /// </summary>
+    /// <typeparam name="TF">Feature data contribute into algorithm</typeparam>
+    public class DbscanPoint<TF> : IPixel<TF>
+    {
+        public TF Feature { get; internal set; }
+
+        public int? ClusterId { get; internal set; }
+
+        public PointType? PointType { get; internal set; }
+
+        public TF Point => Feature;
+
+        public DbscanPoint(TF feature)
+        {
+            Feature = feature;
+            ClusterId = null;
+            PointType = null;
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanPoint.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 337615715baa44e4eb3573f51d9abbb6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 21 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanResult.cs

@@ -0,0 +1,21 @@
+using System.Collections.Generic;
+
+namespace DbscanImplementation
+{
+    /// <summary>
+    /// Result object of algorithm after clusters computed
+    /// </summary>
+    /// <typeparam name="TFeature">Feature data contribute into algorithm</typeparam>
+    public class DbscanResult<TFeature>
+    {
+        public DbscanResult()
+        {
+            Noise = new List<DbscanPoint<TFeature>>();
+            Clusters = new Dictionary<int, List<DbscanPoint<TFeature>>>();
+        }
+
+        public Dictionary<int, List<DbscanPoint<TFeature>>> Clusters { get; private set; }
+
+        public List<DbscanPoint<TFeature>> Noise { get; private set; }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/DbscanResult.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fc3ebd599fce1a240b6f9edbe5f5ac98
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: efb486701df4005439c3af3e411a6224
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 10 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/EmptyDbscanEventPublisher.cs

@@ -0,0 +1,10 @@
+namespace DbscanImplementation.Eventing
+{
+    public class EmptyDbscanEventPublisher : IDbscanEventPublisher
+    {
+        public void Publish<TQ>(TQ @event)
+        {
+
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/EmptyDbscanEventPublisher.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 16eed76e5751e044eb03b0b4a3613487
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 144 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/Events.cs

@@ -0,0 +1,144 @@
+
+using System;
+
+namespace DbscanImplementation.Eventing
+{
+    public class ComputeFinished
+    {
+        public Guid ComputeId { get; }
+
+        public ComputeFinished(Guid computeId)
+        {
+            ComputeId = computeId;
+        }
+    }
+
+    public class ComputeStarted
+    {
+        public Guid ComputeId { get; }
+
+        public ComputeStarted(Guid computeId)
+        {
+            ComputeId = computeId;
+        }
+    }
+
+    public class PointProcessFinished<TF>
+    {
+        public DbscanPoint<TF> Point { get; }
+
+        public PointProcessFinished(DbscanPoint<TF> point)
+        {
+            Point = point;
+        }
+    }
+
+    public class ClusteringFinished<TF>
+    {
+        public DbscanPoint<TF> Point { get; }
+
+        public DbscanPoint<TF>[] NeighborPoints { get; }
+
+        public int ClusterId { get; }
+
+        public double Epsilon { get; }
+
+        public int MinimumPoints { get; }
+
+        public ClusteringFinished(DbscanPoint<TF> point, DbscanPoint<TF>[] neighborPoints,
+            int clusterId, double epsilon, int minimumPoints)
+        {
+            Point = point;
+            NeighborPoints = neighborPoints;
+            ClusterId = clusterId;
+            Epsilon = epsilon;
+            MinimumPoints = minimumPoints;
+        }
+    }
+
+    public class ClusteringStarted<TF>
+    {
+
+        public DbscanPoint<TF> Point { get; }
+
+        public DbscanPoint<TF>[] NeighborPoints { get; }
+
+        public int ClusterId { get; }
+
+        public double Epsilon { get; }
+
+        public int MinimumPoints { get; }
+
+        public ClusteringStarted(DbscanPoint<TF> point, DbscanPoint<TF>[] neighborPoints,
+            int clusterId, double epsilon, int minimumPoints)
+        {
+            Point = point;
+            NeighborPoints = neighborPoints;
+            ClusterId = clusterId;
+            Epsilon = epsilon;
+            MinimumPoints = minimumPoints;
+        }
+    }
+
+    public class PointTypeAssigned<TF>
+    {
+        public DbscanPoint<TF> Point { get; }
+
+        public PointType AssignedType { get; }
+
+        public PointTypeAssigned(DbscanPoint<TF> point, PointType assignedType)
+        {
+            Point = point;
+            AssignedType = assignedType;
+        }
+    }
+
+    public class RegionQueryFinished<TF>
+    {
+        public DbscanPoint<TF> Point { get; }
+
+        public DbscanPoint<TF>[] NeighborPoints { get; }
+
+        public RegionQueryFinished(DbscanPoint<TF> point, DbscanPoint<TF>[] neighborPoints)
+        {
+            Point = point;
+            NeighborPoints = neighborPoints;
+        }
+    }
+
+    public class RegionQueryStarted<TF>
+    {
+        public DbscanPoint<TF> Point { get; private set; }
+
+        public double Epsilon { get; }
+
+        public int MinimumPoints { get; }
+
+        public RegionQueryStarted(DbscanPoint<TF> point, double epsilon, int minimumPoints)
+        {
+            Point = point;
+            Epsilon = epsilon;
+            MinimumPoints = minimumPoints;
+        }
+    }
+
+    public class PointAlreadyProcessed<TF>
+    {
+        public DbscanPoint<TF> Point { get; private set; }
+
+        public PointAlreadyProcessed(DbscanPoint<TF> point)
+        {
+            Point = point;
+        }
+    }
+
+    public class PointProcessStarted<TF>
+    {
+        public DbscanPoint<TF> Point { get; private set; }
+
+        public PointProcessStarted(DbscanPoint<TF> point)
+        {
+            Point = point;
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/Events.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a2c3b3f6dd9a01347ac976bea949acbe
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 13 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventPublisher.cs

@@ -0,0 +1,13 @@
+namespace DbscanImplementation.Eventing
+{
+    /// <summary>
+    /// A basic interface for publishing occurring events inside dbscan algorithm.
+    /// </summary>
+    public interface IDbscanEventPublisher
+    {
+        /// <summary>
+        /// Use for publishing single event
+        /// </summary>
+        void Publish<TObj>(TObj @event);
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventPublisher.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3cf4f6636bf8c7947bfb97a5cdfa81b4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventSubscriber.cs

@@ -0,0 +1,9 @@
+using System.Threading.Tasks;
+
+namespace DbscanImplementation.Eventing
+{
+    public interface IDbscanEventSubscriber<TR>
+    {
+        Task<TR> Subscribe();
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/Eventing/IDbscanEventSubscriber.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a68ad8414fe71464c8d5fe5edc1c8816
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 9 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/PointType.cs

@@ -0,0 +1,9 @@
+namespace DbscanImplementation
+{
+    public enum PointType
+    {        
+        Noise = 0,
+        Core = 1,
+        Border = 2
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/PointType.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9bb23d5758859104a8b97fdfca984214
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 062661903b946864aa5bed2985a28968
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 37 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding/DbscanResultBuilder.cs

@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+
+namespace DbscanImplementation.ResultBuilding
+{
+    public class DbscanResultBuilder<TF>
+    {
+        public DbscanResult<TF> Result { get; private set; }
+
+        public DbscanResultBuilder()
+        {
+            Result = new DbscanResult<TF>();
+        }
+         
+        public void Process(DbscanPoint<TF> point)
+        {
+            if (point.ClusterId.HasValue && !Result.Clusters.ContainsKey(point.ClusterId.Value))
+            {
+                Result.Clusters.Add(point.ClusterId.Value, new List<DbscanPoint<TF>>());
+            }
+
+            switch (point)
+            {
+                case DbscanPoint<TF> core when core.PointType == PointType.Core:
+                    Result.Clusters[core.ClusterId.Value].Add(core);
+                    break;
+
+                case DbscanPoint<TF> border when border.PointType == PointType.Border:
+                    Result.Clusters[border.ClusterId.Value].Add(border);
+                    break;
+
+                case DbscanPoint<TF> noise when noise.PointType == PointType.Noise:
+                    Result.Noise.Add(noise);
+                    break;
+            }
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/Dbscan/ResultBuilding/DbscanResultBuilder.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0ea30c00c1d119641a89b73397e86fd4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 287 - 227
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredLocate.cs

@@ -1,3 +1,4 @@
+using DbscanImplementation;
 using o0;
 using o0.Project;
 using SixLabors.ImageSharp;
@@ -7,6 +8,7 @@ using System.Linq;
 using System.Runtime.Serialization.Formatters;
 using System.Threading.Tasks;
 using UnityEngine;
+using UnityEngine.UI;
 using ZIM.Unity;
 using Color = UnityEngine.Color;
 using Debug = UnityEngine.Debug;
@@ -49,7 +51,7 @@ namespace ZIM
         }
 
         readonly float[] spotBrightness = new float[] { 0.93f, 0.5f };      // 亮点阈值
-        readonly int samplingScale = 1;
+        int samplingScale = 1;
         //const float circleVariance = 30f;
         //const int brightAreaRadius = 30; 
         //const int LeastBrightPoint = 10000;
@@ -57,6 +59,8 @@ namespace ZIM
         SLAMUVC.UVCManager.CameraInfo mCameraInfo;
         ScreenIdentification screenIdentification;
         InfraredSpotSettings infraredSpotSettings;
+        //KMeans kMeans;
+        PixelCheaker ScreenPixelCheaker;
 
         InfraredSpot[] InfraredSpots;
         public InfraredSpot GetSpot(InfraredMatch match)
@@ -78,12 +82,14 @@ namespace ZIM
             spotBrightness[0] = brightnessThreshold; spotBrightness[1] = (float)Math.Min(Math.Exp(1.5 * brightnessThreshold - 1.8), brightnessThreshold);       // 周围泛光的亮度用指数函数直接算
         }
 
-        public InfraredLocate(SLAMUVC.UVCManager.CameraInfo cameraInfo, ScreenIdentification infraredIdentification, InfraredSpotSettings infraredSpotSettings)
+        public InfraredLocate(SLAMUVC.UVCManager.CameraInfo cameraInfo, ScreenIdentification infraredIdentification, InfraredSpotSettings infraredSpotSettings, PixelCheaker screenPixelCheaker)
         {
             this.mCameraInfo = cameraInfo;
             this.screenIdentification = infraredIdentification;
             this.infraredSpotSettings = infraredSpotSettings;
+            this.ScreenPixelCheaker = screenPixelCheaker;
 
+            //this.kMeans = new KMeans();
             //samplingScale = 2;
         }
 
@@ -109,6 +115,9 @@ namespace ZIM
         // New, 返回由大到小排序的点
         public List<PixelSpotArea> LocateToScreen(Color[] pixels, Rect rect)
         {
+            if (!screenIdentification.Screen.Active)
+                rect = new Rect(0, 0, screenIdentification.Size.x, screenIdentification.Size.y);
+
             if (InfraredSpots == null)
             {
                 InfraredSpots = new InfraredSpot[2] {
@@ -116,44 +125,17 @@ namespace ZIM
                 new InfraredSpot(screenIdentification.Screen, InfraredMatch.Match1) };
             }
 
+            ScreenPixelCheaker.ImageSize = new Vector2(screenIdentification.Size.x, screenIdentification.Size.y);
+
             //var watch = new System.Diagnostics.Stopwatch();
             //watch.Start();
             //var times = new List<double>() { 0.0 };
 
-            /* 根据亮点情况调整samplingScale
-
-            (int x, int y) rectMin = ((int)rect.min.x / samplingScale, (int)rect.min.y / samplingScale);
-            (int x, int y) rectMax = ((int)rect.max.x / samplingScale, (int)rect.max.y / samplingScale);
-            int brightCount = 0;
-            Parallel.For(rectMin.x, rectMax.x, (i) =>
-            {
-                for (int j = rectMin.y; j < rectMax.y; j++)
-                {
-                    int ip = i * samplingScale;
-                    int jp = j * samplingScale;
-                    var index = camera.Vector2ToIndex(ip, jp);
-                    var b = pixels[index].Brightness(64);
-
-                    if (b > minBrightness)
-                    {
-                        lock (locker)
-                        {
-                            brightCount++;
-                        }
-                    }
-                }
-            });
-            if (brightCount > 1000)
-            {
-                samplingScale = (int)Math.Ceiling(samplingScale * Math.Sqrt(brightCount / 1000.0));       // 如果亮点太多,控制亮点数量在1000左右
-            }
-            /**/
-
             (int x, int y) rectMin = ((int)rect.min.x / samplingScale, (int)rect.min.y / samplingScale);
             (int x, int y) rectMax = ((int)rect.max.x / samplingScale, (int)rect.max.y / samplingScale);
 
-            var spotPoint = new List<Vector2>((int)rect.width * (int)rect.height / 256);     // 预估的初始容量
-            var brightPoint = new List<Vector2>((int)rect.width * (int)rect.height / 64);
+            var spotPoint = new List<Vector2>(200);     // 预估的初始容量
+            var brightPoint = new List<Vector2>(1000);
 
             Parallel.For(rectMin.x, rectMax.x, (i) =>
             {
@@ -163,6 +145,10 @@ namespace ZIM
                 {
                     int ip = i * samplingScale;
                     int jp = j * samplingScale;
+
+                    if (ScreenPixelCheaker.OutCollider2D(new Vector2(ip, jp)))
+                        continue;
+
                     var index = mCameraInfo.CoordToIndex(ip, jp);
                     //var b = brightness[index];
                     //var b = (int)Math.Round(ConvBrightness(brightness, (ip, jp)));
@@ -183,6 +169,20 @@ namespace ZIM
                     brightPoint.AddRange(points1);
                 }
             });
+
+            //Debug.Log("---spotPoint.Count: " + spotPoint.Count);
+            //Debug.Log(samplingScale);
+            if (spotPoint.Count > 200)     // 如果亮点太多,控制亮点数量在200左右
+            {
+                samplingScale = (int)Math.Ceiling(samplingScale * Math.Sqrt(spotPoint.Count / 200.0));
+                return new List<PixelSpotArea>();
+            }
+            else if (samplingScale > 1 && spotPoint.Count < 20) 
+            {
+                samplingScale = Math.Max((int)Math.Ceiling(samplingScale * Math.Sqrt(spotPoint.Count / 80.0)), 1);
+                return new List<PixelSpotArea>();
+            }
+
             //Debug.Log("spotPoint Count: " + spotPoint.Count + ", brightPoint Count: " + brightPoint.Count);
 
             //times.Add(watch.ElapsedMilliseconds);
@@ -192,60 +192,43 @@ namespace ZIM
             Parallel.For(0, spotPoint.Count, (i) => spotPoint[i] = screenIdentification.Screen.TransformToScreen(spotPoint[i]));
             Parallel.For(0, brightPoint.Count, (i) => brightPoint[i] = screenIdentification.Screen.TransformToScreen(brightPoint[i]));
 
-            //{
-            //    var texure = new Texture2D((int)screenIdentification.Screen.UVSize.x + 1, (int)screenIdentification.Screen.UVSize.y + 1);
-            //    var pixels2 = new Color[texure.width * texure.height];
-            //    foreach (var i in pixels2.Index())
-            //        pixels2[i] = Color.white;
-
-            //    foreach (var i in spotPoint)
-            //    {
-            //        if (!screenIdentification.Screen.UVInScreen(i))
-            //            continue;
-            //        pixels2[(int)i.y * texure.width + (int)i.x] = Color.red;
-            //    }
-            //    foreach (var i in brightPoint)
-            //    {
-            //        if (!screenIdentification.Screen.UVInScreen(i))
-            //            continue;
-            //        pixels2[(int)i.y * texure.width + (int)i.x] = Color.black;
-            //    }
-            //    texure.filterMode = FilterMode.Point;
-            //    texure.SetPixels(pixels2);
-            //    texure.Apply();
-            //    ScreenLocate.DebugTexture(2, texure);
-            //}
+            // 筛掉屏幕外的点
+            var temp0 = new List<Vector2>(spotPoint.Count);
+            var temp1 = new List<Vector2>(spotPoint.Count);
+            foreach (var p in spotPoint)
+            {
+                if (screenIdentification.Screen.UVInScreen(p))
+                    temp0.Add(p);
+            }
+            foreach (var p in brightPoint)
+            {
+                if (screenIdentification.Screen.UVInScreen(p))
+                    temp1.Add(p);
+            }
+            spotPoint = temp0;
+            brightPoint = temp1;
 
             if (spotPoint.Count > 0)
             {
                 //times.Add(watch.ElapsedMilliseconds);
                 //UnityEngine.Debug.Log("time2: " + (times[times.Count - 1] - times[times.Count - 2]));
 
-                var spotArea = PixelSpotArea.Cluster(spotPoint, brightPoint, screenIdentification.Screen.UVInScreen);
+                var db = Dbscan.Run(spotPoint, ZIM.Unity.Extension.LengthManhattan, samplingScale * 2, 3);
+                var spotArea = DbscanToSpotAreas(db, brightPoint);
 
-                //times.Add(watch.ElapsedMilliseconds);
-                //UnityEngine.Debug.Log("time3: " + (times[times.Count - 1] - times[times.Count - 2]));
+                if (ScreenLocate.Main.DebugOnEditorWin)
+                    DebugAreas(spotArea);
 
-                if (spotArea.Count == 0)
-                    return spotArea;
 
-                // 亮度较低的部分合并到区域中
-                for (int i = 0; i < brightPoint.Count; i++)
-                {
-                    var p = brightPoint[i];
-                    var grid = PixelSpotArea.GetGrid1(p);
-                    for (int j = 0; j < spotArea.Count; j++)
-                    {
-                        var area = spotArea[j];
-                        if (area.Include1(grid))
-                            area.Add(p);
-                    }
-                }
-
-                //Debug.Log("Area Count" + spotArea.Count);
+                //Debug.Log("db: " + db.Clusters.Count);
+                //Debug.Log("db noise: " + db.Noise.Count);
+                //foreach (var i in spotArea)
+                //{
+                //    Debug.Log("i.Radius" + i.Radius);
+                //}
 
                 //times.Add(watch.ElapsedMilliseconds);
-                //UnityEngine.Debug.Log("time4: " + (times[times.Count - 1] - times[times.Count - 2]));
+                //UnityEngine.Debug.Log("time3: " + (times[times.Count - 1] - times[times.Count - 2]));
 
                 return spotArea;
 
@@ -253,7 +236,7 @@ namespace ZIM
                 foreach (var i in spotArea)
                 {
                     var r0 = i.Radius / 2 / mCameraInfo.CurrentWidth;
-                    var center = i.Center;
+                    var center = i.Centroid;
                     var offset1 = center + new Vector2(i.Radius / 2, 0);
                     var offset2 = center - new Vector2(i.Radius / 2, 0);
                     var offset3 = center + new Vector2(0, i.Radius / 2);
@@ -289,168 +272,81 @@ namespace ZIM
             return new List<PixelSpotArea>();
         }
 
-        public List<Vector2> GetOld(int[] brightness)        // 取整后的亮度图
-        {
-            return LocateOld(brightness, new Rect(0, 0, mCameraInfo.CurrentWidth, mCameraInfo.CurrentHeight));
-        }
-        public List<Vector2> LocateOld(int[] brightness, Rect rect)
+        private void DebugAreas(List<PixelSpotArea> areas)
         {
-            var brightPixelDic = new Dictionary<float, List<Vector2>>();
-            (int x, int y) origin = ((int)rect.min.x + 1, (int)rect.min.y + 1);
-            Parallel.For(origin.x / samplingScale, (origin.x + (int)rect.width) / samplingScale, (i) =>
+            Texture2D texture = new Texture2D(ScreenLocate.Main.CameraSize.x, ScreenLocate.Main.CameraSize.y);
+            Color[] blackPixels = new Color[texture.width * texture.height];
+            for (int i = 0; i < blackPixels.Length; i++)
+                blackPixels[i] = Color.black;
+            texture.SetPixels(blackPixels);
+
+            Color[] colors = new Color[4] { Color.yellow, Color.white, Color.green, Color.blue };
+            int index = 0;
+            foreach (var i in areas)
             {
-                for (int j = origin.y / samplingScale; j < (origin.y + rect.height) / samplingScale; j++)
-                {
-                    int ip = i * samplingScale;
-                    int jp = j * samplingScale;
-                    var index = mCameraInfo.CoordToIndex(ip, jp);
-                    //var b = brightness[index];
-                    //var b = (int)Math.Round(ConvBrightness(brightness, (ip, jp)));
-                    var b = ConvBrightness(brightness, (ip, jp), 3);
+                foreach (var p in i.Pixels0)
+                    texture.SetPixel((int)p.Point.x, (int)p.Point.y, colors[index]);
+                index++;
+                foreach (var p in i.Pixels1)
+                    texture.SetPixel((int)p.x, (int)p.y, colors[index]);
+                index++;
+                if (index >= 4)
+                    break;
+            }
 
-                    if (b > 32)
-                    {
-                        lock (brightPixelDic)
-                        {
-                            if (brightPixelDic.TryGetValue(b, out List<Vector2> list))
-                                list.Add(new Vector2(ip, jp));
-                            else
-                                brightPixelDic.Add(b, new List<Vector2> { new Vector2(ip, jp) });
-                        }
-                    }
-                }
-            });
+            texture.Apply();
+            ScreenLocate.DebugTexture(5, texture);
+        }
 
-            //Parallel.For(0, pixels.Length / 7, (i) =>
-            //{
-            //    i = i * 7;
-            //    var b = getBrightness(pixels[i]);
-            //    if (b >= maxBrightness)
-            //    {
-            //        lock (brightSpots)
-            //        {
-            //            if (b != maxBrightness)
-            //                brightSpots.Clear();
-            //            brightSpots.Add(indexToVector2(i));
-            //        }
-            //        maxBrightness = b;
-            //    }
-            //});
+        private List<PixelSpotArea> DbscanToSpotAreas(DbscanResult<Vector2> db, List<Vector2> brightPoint)
+        {
+            if (db.Clusters.Count == 0)
+                return new List<PixelSpotArea>();
 
-            if (brightPixelDic.Count > 0)
+            // 用dbscan的结果创建SpotArea,先用高亮区算一遍半径
+            var clusters = new List<PixelSpotArea>();
+            var areaIncludeRadius = new List<(Vector2, float)>();
+            foreach (var i in db.Clusters.Values)
             {
-                // 取亮度最高的像素
-                var keys = brightPixelDic.Keys.ToList();
-                var maxIndex = o0.o0.MaxIndex(keys);
-                //keys.Sort((a, b) => -a.CompareTo(b));
-                var brightPixels = new List<Vector2>();
-                for (int i = 0; i < Math.Min(1, keys.Count); i++)
-                    brightPixels.AddRange(brightPixelDic[keys[maxIndex]]);
-
-                //Debug.Log("max brightness: " + keys[maxIndex]);
-                //Debug.Log("brightPixels.Count: " + brightPixels.Count);
-
-                //var testTexture = new Texture2D((int)_textureSize.x, (int)_textureSize.y);
-                //testTexture.wrapMode = TextureWrapMode.Clamp;
-                //testTexture.filterMode = FilterMode.Point;
-                //foreach (var i in brightPixels)
-                //    testTexture.SetPixel((int)i.x, (int)i.y, Color.black);
-                //testTexture.Apply();
-                //testImage.texture = testTexture;
-
-                var brightArea = new List<PixelCircleArea>() { new PixelCircleArea(brightPixels.First()) };
-                for (int i = 1; i < brightPixels.Count; i++)
-                {
-                    var p = brightPixels[i];
-                    var grid = PixelArea.GetGrid(p);
-                    var join = new SortedList<int, PixelCircleArea>();
-                    for (int j = 0; j < brightArea.Count; j++)
-                    {
-                        var area = brightArea[j];
-                        if (area.Include(grid))
-                            join.Add(j, area);
-                    }
-                    if (join.Count == 0)
-                        brightArea.Add(new PixelCircleArea(p));
-                    else if (join.Count == 1)
-                        join.First().Value.Add(p, grid);
-                    else
-                    {
-                        var combine = new PixelCircleArea(join.Values);
-                        combine.Add(p, grid);
-                        brightArea.Add(combine);
-                        for (int j = join.Count - 1; j >= 0; j--)
-                            brightArea.RemoveAt(join.ElementAt(j).Key);
-                    }
+                var area = new PixelSpotArea(i);
+                areaIncludeRadius.Add((area.Centroid, area.Radius));
+                clusters.Add(area);
+            }
+            foreach (var i in db.Noise)
+                areaIncludeRadius.Add((i.Feature, 0));
 
-                    //foreach (var j in brightArea)
-                    //{
-                    //    var offset = (p - j.Center);
-                    //    if (offset.magnitude < brightAreaRadius)     // 距离近的并入该区域
-                    //    {
-                    //        j.Pixels.Add(p);
-                    //        j.Center += offset / j.Pixels.Count;
-                    //        join = true;
-                    //        break;
-                    //    }
-                    //}
-                    //if (!join)
-                    //    brightArea.Add(new PixelArea(p));
-                }
+            // 将泛光点添加到最近的area
+            foreach (var i in brightPoint)
+            {
+                var index = FindNearestAreaIndex(i, areaIncludeRadius);
+                if (index < clusters.Count)
+                    clusters[index].Add1(i);
+            }
+            // 添加了泛光点后,重新计算半径
+            foreach (var i in clusters)
+                i.Radius = i.CalculateRadius();
 
-                //var sum = new Vector2(0, 0);
-                //foreach (var i in brightPixels)
-                //{
-                //    sum += i;
-                //}
-                //var middle = sum/brightPixels.Count;
-                //Debug.Log("brightArea.Count: " + brightArea.Count);
+            return clusters;
+        }
 
-                //if (brightArea.Count <= 1)
-                //    return brightArea.FirstOrDefault().Center;
+        private int FindNearestAreaIndex(Vector2 a, List<(Vector2, float)> areas)
+        {
+            if (areas == null || areas.Count == 0)
+                return -1;
 
-                //PixelArea maxBrightArea = brightArea.First();
-                //float maxCircleBrightness = 10000f;
-                var CircleAreas = new List<PixelCircleArea>();
+            int nearestIndex = 0;
+            float nearestDistance = Vector2.Distance(a, areas[0].Item1) - areas[0].Item2;
 
-                for (int i = 0; i < brightArea.Count; i++)
+            for (int i = 1; i < areas.Count; i++)
+            {
+                float distance = Vector2.Distance(a, areas[i].Item1) - areas[i].Item2;
+                if (distance < nearestDistance)
                 {
-                    var area = brightArea[i];
-                    var value = area.CircleBrightness(out float variance, brightness, ConvBrightness, 3);
-                    //Debug.Log(counter + "Variance: " + variance);
-                    //Debug.Log(counter + "CircleBrightness: " + value);
-                    //Debug.Log(counter + "CircleRadius: " + area.MaxRadius);
-
-                    if (variance < 30)
-                    {
-                        CircleAreas.Add(area);
-                    }
-
-                    //if (maxCircleBrightness == 10000f) 
-                    //{
-                    //    maxCircleBrightness = value;
-                    //    maxBrightArea = area;
-                    //}
-                    //else if (maxCircleBrightness < value || (maxCircleBrightness == value && area.Pixels.Count > maxBrightArea.Pixels.Count)) 
-                    //{
-                    //    maxCircleBrightness = value;
-                    //    maxBrightArea = area;
-                    //}
+                    nearestDistance = distance;
+                    nearestIndex = i;
                 }
-                //Debug.Log("CricleBrightness: " + maxCircleBrightness);
-
-                //Debug.Log(counter + "CircleAreas: " + CircleAreas.Count);
-                //counter++;
-                if (CircleAreas.Count == 0)
-                    return null;
-                if (CircleAreas.Count == 1)
-                    return new List<Vector2> { CircleAreas[0].Center };
-
-                CircleAreas.Sort((a, b) => b.MaxRadius.CompareTo(a.MaxRadius));
-                return new List<Vector2> { CircleAreas[0].Center, CircleAreas[1].Center };
             }
-
-            return null;
+            return nearestIndex;
         }
 
         InfraredSpot[] MatchInfraredRaySingle(List<PixelSpotArea> spotArea)
@@ -632,7 +528,7 @@ namespace ZIM
                     {
                         if (i != overlapAera)
                         {
-                            var distance = (i.Center - overlapAera.Center).magnitude;
+                            var distance = (i.Centroid - overlapAera.Centroid).magnitude;
                             if (distance < overlapAera.Radius * 2 && distance < split.Item2)
                             {
                                 split = (i, distance);
@@ -649,7 +545,7 @@ namespace ZIM
                     }
                     else
                     {
-                        if ((InfraredSpots[1].Predict.Value - overlapAera.Center).magnitude > overlapAera.Radius / 2)
+                        if ((InfraredSpots[1].Predict.Value - overlapAera.Centroid).magnitude > overlapAera.Radius / 2)
                             InfraredSpots[1].Disappear = true;
 
                         //InfraredSpots[0].Update(overlapAera);
@@ -703,6 +599,170 @@ namespace ZIM
             return sum / (kernel_size * kernel_size);
         }
 
+        public List<Vector2> GetOld(int[] brightness)        // 取整后的亮度图
+        {
+            return LocateOld(brightness, new Rect(0, 0, mCameraInfo.CurrentWidth, mCameraInfo.CurrentHeight));
+        }
+        public List<Vector2> LocateOld(int[] brightness, Rect rect)
+        {
+            var brightPixelDic = new Dictionary<float, List<Vector2>>();
+            (int x, int y) origin = ((int)rect.min.x + 1, (int)rect.min.y + 1);
+            Parallel.For(origin.x / samplingScale, (origin.x + (int)rect.width) / samplingScale, (i) =>
+            {
+                for (int j = origin.y / samplingScale; j < (origin.y + rect.height) / samplingScale; j++)
+                {
+                    int ip = i * samplingScale;
+                    int jp = j * samplingScale;
+                    var index = mCameraInfo.CoordToIndex(ip, jp);
+                    //var b = brightness[index];
+                    //var b = (int)Math.Round(ConvBrightness(brightness, (ip, jp)));
+                    var b = ConvBrightness(brightness, (ip, jp), 3);
+
+                    if (b > 32)
+                    {
+                        lock (brightPixelDic)
+                        {
+                            if (brightPixelDic.TryGetValue(b, out List<Vector2> list))
+                                list.Add(new Vector2(ip, jp));
+                            else
+                                brightPixelDic.Add(b, new List<Vector2> { new Vector2(ip, jp) });
+                        }
+                    }
+                }
+            });
+
+            //Parallel.For(0, pixels.Length / 7, (i) =>
+            //{
+            //    i = i * 7;
+            //    var b = getBrightness(pixels[i]);
+            //    if (b >= maxBrightness)
+            //    {
+            //        lock (brightSpots)
+            //        {
+            //            if (b != maxBrightness)
+            //                brightSpots.Clear();
+            //            brightSpots.Add(indexToVector2(i));
+            //        }
+            //        maxBrightness = b;
+            //    }
+            //});
+
+            if (brightPixelDic.Count > 0)
+            {
+                // 取亮度最高的像素
+                var keys = brightPixelDic.Keys.ToList();
+                var maxIndex = o0.o0.MaxIndex(keys);
+                //keys.Sort((a, b) => -a.CompareTo(b));
+                var brightPixels = new List<Vector2>();
+                for (int i = 0; i < Math.Min(1, keys.Count); i++)
+                    brightPixels.AddRange(brightPixelDic[keys[maxIndex]]);
+
+                //Debug.Log("max brightness: " + keys[maxIndex]);
+                //Debug.Log("brightPixels.Count: " + brightPixels.Count);
+
+                //var testTexture = new Texture2D((int)_textureSize.x, (int)_textureSize.y);
+                //testTexture.wrapMode = TextureWrapMode.Clamp;
+                //testTexture.filterMode = FilterMode.Point;
+                //foreach (var i in brightPixels)
+                //    testTexture.SetPixel((int)i.x, (int)i.y, Color.black);
+                //testTexture.Apply();
+                //testImage.texture = testTexture;
+
+                var brightArea = new List<PixelCircleArea>() { new PixelCircleArea(brightPixels.First()) };
+                for (int i = 1; i < brightPixels.Count; i++)
+                {
+                    var p = brightPixels[i];
+                    var grid = PixelArea.GetGrid(p);
+                    var join = new SortedList<int, PixelCircleArea>();
+                    for (int j = 0; j < brightArea.Count; j++)
+                    {
+                        var area = brightArea[j];
+                        if (area.Include(grid))
+                            join.Add(j, area);
+                    }
+                    if (join.Count == 0)
+                        brightArea.Add(new PixelCircleArea(p));
+                    else if (join.Count == 1)
+                        join.First().Value.Add(p, grid);
+                    else
+                    {
+                        var combine = new PixelCircleArea(join.Values);
+                        combine.Add(p, grid);
+                        brightArea.Add(combine);
+                        for (int j = join.Count - 1; j >= 0; j--)
+                            brightArea.RemoveAt(join.ElementAt(j).Key);
+                    }
+
+                    //foreach (var j in brightArea)
+                    //{
+                    //    var offset = (p - j.Center);
+                    //    if (offset.magnitude < brightAreaRadius)     // 距离近的并入该区域
+                    //    {
+                    //        j.Pixels.Add(p);
+                    //        j.Center += offset / j.Pixels.Count;
+                    //        join = true;
+                    //        break;
+                    //    }
+                    //}
+                    //if (!join)
+                    //    brightArea.Add(new PixelArea(p));
+                }
+
+                //var sum = new Vector2(0, 0);
+                //foreach (var i in brightPixels)
+                //{
+                //    sum += i;
+                //}
+                //var middle = sum/brightPixels.Count;
+                //Debug.Log("brightArea.Count: " + brightArea.Count);
+
+                //if (brightArea.Count <= 1)
+                //    return brightArea.FirstOrDefault().Center;
+
+                //PixelArea maxBrightArea = brightArea.First();
+                //float maxCircleBrightness = 10000f;
+                var CircleAreas = new List<PixelCircleArea>();
+
+                for (int i = 0; i < brightArea.Count; i++)
+                {
+                    var area = brightArea[i];
+                    var value = area.CircleBrightness(out float variance, brightness, ConvBrightness, 3);
+                    //Debug.Log(counter + "Variance: " + variance);
+                    //Debug.Log(counter + "CircleBrightness: " + value);
+                    //Debug.Log(counter + "CircleRadius: " + area.MaxRadius);
+
+                    if (variance < 30)
+                    {
+                        CircleAreas.Add(area);
+                    }
+
+                    //if (maxCircleBrightness == 10000f) 
+                    //{
+                    //    maxCircleBrightness = value;
+                    //    maxBrightArea = area;
+                    //}
+                    //else if (maxCircleBrightness < value || (maxCircleBrightness == value && area.Pixels.Count > maxBrightArea.Pixels.Count)) 
+                    //{
+                    //    maxCircleBrightness = value;
+                    //    maxBrightArea = area;
+                    //}
+                }
+                //Debug.Log("CricleBrightness: " + maxCircleBrightness);
+
+                //Debug.Log(counter + "CircleAreas: " + CircleAreas.Count);
+                //counter++;
+                if (CircleAreas.Count == 0)
+                    return null;
+                if (CircleAreas.Count == 1)
+                    return new List<Vector2> { CircleAreas[0].Center };
+
+                CircleAreas.Sort((a, b) => b.MaxRadius.CompareTo(a.MaxRadius));
+                return new List<Vector2> { CircleAreas[0].Center, CircleAreas[1].Center };
+            }
+
+            return null;
+        }
+
         //(int max, int second) MaxAreaIndex(Color[] pixels, List<PixelArea> list)
         //{
         //    var maxValue = float.MinValue;

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

@@ -83,7 +83,7 @@ namespace ZIM
             var predict = Predict.Value;
             foreach (var i in areas)
             {
-                var len = (i.Center - predict).magnitude;
+                var len = (i.Centroid - predict).magnitude;
                 if (len < MinVerifyLength)
                 {
                     if (len < minLength)
@@ -123,9 +123,9 @@ namespace ZIM
                 return;
             }
 
-            var predict = estimation.Update(area.Center);
+            var predict = estimation.Update(area.Centroid);
             //ScreenLocation = area.Center;
-            ScreenLocation = area.Center * 0.8f + predict * 0.2f;
+            ScreenLocation = area.Centroid * 0.8f + predict * 0.2f;
             spots.Add(area);
             if (spots.Count > 15)
             {

+ 8 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 31edbd1ac472bca4aaa80cff37dd7817
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 193 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/KMeans.cs

@@ -0,0 +1,193 @@
+using o0;
+using o0.Num;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using UnityEngine;
+using static ScreenLocate;
+
+namespace ZIM 
+{ 
+    public class KMeans
+    {
+        public System.Random Rand = new System.Random();
+
+        // 用轮廓系数评价K值
+        public List<PixelSpotAreaOld> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, InfraredCount spotMaxCount, Func<Vector2, bool> PointFilter = null)
+        {
+            if (PointFilter != null)    // 筛掉屏幕外的点
+            {
+                var temp0 = new List<Vector2>(spotPoint.Count);
+                var temp1 = new List<Vector2>(spotPoint.Count);
+                foreach (var p in spotPoint)
+                {
+                    if (PointFilter(p))
+                        temp0.Add(p);
+                }
+                foreach (var p in brightPoint)
+                {
+                    if (PointFilter(p))
+                        temp1.Add(p);
+                }
+                spotPoint = temp0;
+                brightPoint = temp1;
+            }
+
+            // 从1-k,计算多次聚类,再评估选出best k
+            List<PixelSpotAreaOld>[] ClustersArray = new List<PixelSpotAreaOld>[(int)spotMaxCount];   // 各个K对应的Clusters
+            float[] silhouetteScores = new float[(int)spotMaxCount];                    // 轮廓系数
+
+            for (int i = 0; i < (int)spotMaxCount; i++)
+            {
+                int k = i + 1;
+                var clusters = Cluster(spotPoint, brightPoint, k);
+                Debug.Log("k: " + k);
+                foreach (var j in clusters)
+                {
+                    Debug.Log(j.Centroid + ", " + j.Radius);
+                }
+                silhouetteScores[i] = CalculateSilhouetteScore(spotPoint, clusters);
+                ClustersArray[i] = clusters;
+                Debug.Log("silhouetteScore: " + silhouetteScores[i]);
+            }
+
+            int best = silhouetteScores.MaxIndex();
+
+            foreach (var j in ClustersArray[best])
+            {
+                Debug.Log("select: " + j.Centroid + ", " + j.Radius);
+            }
+
+            return ClustersArray[best];
+        }
+
+        // 计算轮廓系数
+        public float CalculateSilhouetteScore(List<Vector2> points, List<PixelSpotAreaOld> clusters)
+        {
+            int numPoints = points.Count;
+            float totalSilhouetteScore = 0f;
+
+            Dictionary<Vector2, int> pointToClusterMap = new Dictionary<Vector2, int>();
+            for (int i = 0; i < clusters.Count; i++)
+            {
+                foreach (var p in clusters[i].Pixels0)
+                    pointToClusterMap[p] = i;
+            }
+
+            foreach (var point in points)
+            {
+                int clusterIndex = pointToClusterMap[point];
+                float a = AverageDistance(point, clusters[clusterIndex]);
+                float b = float.MaxValue;
+
+                for (int i = 0; i < clusters.Count; i++)
+                {
+                    if (i == clusterIndex) continue;
+                    float distance = AverageDistance(point, clusters[i]);
+                    if (distance < b)
+                    {
+                        b = distance;
+                    }
+                }
+
+                float s = (b - a) / Mathf.Max(a, b);
+                totalSilhouetteScore += s;
+            }
+
+            return totalSilhouetteScore / numPoints;
+        }
+
+        private float AverageDistance(Vector2 point, PixelSpotAreaOld cluster)
+        {
+            float totalDistance = 0f;
+            foreach (var c in cluster.Pixels0)
+                totalDistance += Vector2.Distance(point, c);
+            return totalDistance / cluster.Pixels0.Count;
+        }
+
+        // KMeans聚类
+        private List<PixelSpotAreaOld> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, int k, int maxIterations = 10)
+        {
+            if (spotPoint.Count < k)
+                return new List<PixelSpotAreaOld>();
+
+            var clusters = InitializeClusters(spotPoint, k);
+
+            bool centroidsChanged = true;
+            int iterations = 0;
+
+            while (centroidsChanged && iterations < maxIterations)
+            {
+                foreach (var i in clusters)
+                    i.Clear();
+
+                foreach (var point in spotPoint)
+                {
+                    int closestCentroidIndex = GetClosestClusterIndex(point, clusters);
+                    clusters[closestCentroidIndex].Add0(point);
+                }
+
+                centroidsChanged = false;
+                foreach (var i in clusters)
+                {
+
+                    if (i.UpdateCentroid())
+                        centroidsChanged = true;
+                }
+
+                iterations++;
+            }
+
+            // 聚类完成,添加泛光点
+            foreach (var point in brightPoint)
+            {
+                int closestCentroidIndex = GetClosestClusterIndex(point, clusters);
+                clusters[closestCentroidIndex].Add1(point);
+            }
+
+            return clusters;
+        }
+
+        private List<PixelSpotAreaOld> InitializeClusters(List<Vector2> points, int k)
+        {
+            Vector2[] centroids = new Vector2[k];
+            HashSet<int> chosenIndices = new HashSet<int>();
+
+            for (int i = 0; i < k; i++)
+            {
+                int index;
+                do
+                {
+                    index = Rand.Next(points.Count);
+                } while (chosenIndices.Contains(index));
+
+                chosenIndices.Add(index);
+                centroids[i] = points[index];
+            }
+
+            List<PixelSpotAreaOld> clusters = new List<PixelSpotAreaOld>(k);
+            foreach (var i in centroids)
+                clusters.Add(new PixelSpotAreaOld(i));
+
+            return clusters;
+        }
+
+        private int GetClosestClusterIndex(Vector2 point, List<PixelSpotAreaOld> centroids)
+        {
+            int closestIndex = 0;
+            float minDistance = Vector2.Distance(point, centroids[0].Centroid);
+
+            for (int i = 1; i < centroids.Count; i++)
+            {
+                float distance = Vector2.Distance(point, centroids[i].Centroid);
+                if (distance < minDistance)
+                {
+                    minDistance = distance;
+                    closestIndex = i;
+                }
+            }
+            return closestIndex;
+        }
+    }
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/KMeans.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aa4ac7acf6b3bc241ac7008b84c3db3f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelArea.cs → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelArea.cs

@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using System.Threading.Tasks;
 using Unity.VisualScripting;
 using UnityEngine;
+using ZIM.Unity;
 using Color = UnityEngine.Color;
 
 namespace ZIM

+ 1 - 1
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelArea.cs.meta → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelArea.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 538d0287447cd0741b9a4a744cbf3008
+guid: 35b90440cecf6c94e8b55aca37d6c9c2
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 0 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelCircleArea.cs → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelCircleArea.cs


+ 1 - 1
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelCircleArea.cs.meta → Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelCircleArea.cs.meta

@@ -1,5 +1,5 @@
 fileFormatVersion: 2
-guid: 98eb535bebc4df44bb2b62af2c0482ae
+guid: 036c5680965637440ad4a410bb7a3c26
 MonoImporter:
   externalObjects: {}
   serializedVersion: 2

+ 124 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelSpotAreaOld.cs

@@ -0,0 +1,124 @@
+using o0;
+using o0.Num;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using UnityEngine;
+using ZIM.Unity;
+using static ScreenLocate;
+using Color = UnityEngine.Color;
+
+namespace ZIM
+{
+    // 亮区的点用来定位(计算center),泛光区域的点用来计算radius
+    public class PixelSpotAreaOld
+    {
+        // 中心
+        public Vector2 Centroid { get; private set; }
+
+        // 亮区的半径,计算含泛光点
+        float radius = 0;
+        public float Radius
+        {
+            set
+            {
+                radius = value;
+            }
+            get
+            {
+                if (radius != 0)
+                    return radius;
+
+                //var radiusDic = new Dictionary<int, float>() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } };
+                var radiusDic = new float[8];
+                foreach (var p in Pixels0)
+                {
+                    var dir = p - Centroid;
+                    var radius = dir.magnitude;
+                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
+                    if (degreeI > 7) degreeI = 0;       // 防止正好360度
+
+                    if (radius > radiusDic[degreeI])
+                        radiusDic[degreeI] = radius;
+                }
+
+                foreach (var p in Pixels1)
+                {
+                    var dir = p - Centroid;
+                    var radius = dir.magnitude;
+                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
+                    if (degreeI > 7) degreeI = 0;
+
+                    if (radius > radiusDic[degreeI])
+                        radiusDic[degreeI] = radius;
+                }
+                return radius = radiusDic.Mean();
+            }
+        }
+
+        public List<Vector2> Pixels0 = new List<Vector2>();
+        public List<Vector2> Pixels1 = new List<Vector2>();
+
+        public PixelSpotAreaOld(Vector2 center)        // kmeans中用随机点初始化,作为中心
+        {
+            Centroid = center;
+        }
+
+        public void Add0(Vector2 point)
+        {
+            Pixels0.Add(point);
+        }
+
+        public void Add1(Vector2 point)
+        {
+            Pixels1.Add(point);
+        }
+
+        // 计算中心点,用亮区计算, 如果中心改变则输出true,不变输出false
+        public bool UpdateCentroid()
+        {
+            Vector2 sum = Vector2.zero;
+            foreach (var p in Pixels0)
+                sum += p;
+            var newCentroid = sum / Pixels0.Count;
+            if (Centroid == newCentroid)
+                return false;
+            Centroid = newCentroid;
+            return true;
+        }
+
+        // 不影响center
+        public void Clear()
+        {
+            Pixels0.Clear();
+            Pixels1.Clear();
+        }
+
+        public float TotalBrightness(Color[] pixels, Func<int, int, int> Vector2ToIndex, int outer_size = 3)
+        {
+            (int x, int y) rectMin = ((int)(Centroid.x - Radius - outer_size), (int)(Centroid.y - Radius - outer_size));
+            (int x, int y) rectMax = ((int)(Centroid.x + Radius + outer_size), (int)(Centroid.y + Radius + outer_size));
+
+            var total = 0f;
+            Parallel.For(rectMin.x, rectMax.x, (i) =>
+            {
+                var t = 0;
+                for (int j = rectMin.y; j < rectMax.y; j++)
+                {
+                    var index = Vector2ToIndex(i, j);
+                    var b = pixels[index].Brightness(64);
+                    t += b;
+                }
+                lock (InfraredLocate.locker)
+                {
+                    total += t;
+                }
+            });
+            //total /= (rectMax.y - rectMin.y) * (rectMax.x - rectMin.x);
+
+            return total;
+        }
+    }
+
+}

+ 11 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/Old/PixelSpotAreaOld.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eda55292905d54c4f9ded91b4ca8f510
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 59 - 143
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/InfraredSpot/PixelSpotArea.cs

@@ -5,82 +5,37 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading.Tasks;
 using UnityEngine;
+using ZIM.Unity;
+using static ScreenLocate;
 using Color = UnityEngine.Color;
 
 namespace ZIM
 {
+    public interface IPixel<T>
+    {
+        // 像素坐标点
+        T Point { get;}
+    }
+
     // 亮区的点用来定位(计算center),泛光区域的点用来计算radius
     public class PixelSpotArea
     {
-        //public static float gridRatio0 = 6f / 1280;
-        //public static float gridRatio1 = 20f / 720;
-
-        public static int gridLength0 = 2;      // 亮区
-        public static int gridLength1 = 10;     // 边缘泛光
-
-        public static (int x, int y) GetGrid0(Vector2 v)
-        {
-            var m = (int)(v.x / gridLength0);
-            var n = (int)(v.y / gridLength0);
-            return (m, n);
-        }
-
-        public static (int x, int y) GetGrid1(Vector2 v)
-        {
-            var m = (int)(v.x / gridLength1);
-            var n = (int)(v.y / gridLength1);
-            return (m, n);
-        }
-
-        // 亮点聚类到区域
-        public static List<PixelSpotArea> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, Func<Vector2, bool> PointFilter = null)
-        {
-            var spotArea = new List<PixelSpotArea>();
-            for (int i = 0; i < spotPoint.Count; i++)
-            {
-                var p = spotPoint[i];
-                if (PointFilter != null && !PointFilter(p))        // 筛选是否在屏幕内
-                    continue;
-
-                var grid = PixelSpotArea.GetGrid0(p);
-                var join = new SortedList<int, PixelSpotArea>();
-                for (int j = 0; j < spotArea.Count; j++)
-                {
-                    var area = spotArea[j];
-                    if (area.Include0(grid))
-                        join.Add(j, area);
-                }
-                if (join.Count == 0)
-                    spotArea.Add(new PixelSpotArea(p, grid, PixelSpotArea.GetGrid1(p)));
-                else if (join.Count == 1)
-                    join.First().Value.Join(p, grid, PixelSpotArea.GetGrid1(p));
-                else
-                {
-                    var combine = new PixelSpotArea(join.Values);
-                    combine.Join(p, grid, PixelSpotArea.GetGrid1(p));
-                    spotArea.Add(combine);
-                    for (int j = join.Count - 1; j >= 0; j--)
-                        spotArea.RemoveAt(join.ElementAt(j).Key);
-                }
-            }
-            return spotArea;
-        }
-
-        Vector2 center = default;       // 该项目里center不可能等于0,0
-        public Vector2 Center
+        // 中心
+        Vector2 centroid = default;       // 该项目里center不可能等于0,0
+        public Vector2 Centroid
         {
             get
             {
-                if (center != default)
-                    return center;
+                if (centroid != default)
+                    return centroid;
 
                 foreach (var p in Pixels0)
-                {
-                    center += p;
-                }
-                return center /= Pixels0.Count;
+                    centroid += p.Point;
+                return centroid /= Pixels0.Count;
             }
         }
+
+        // 亮区的半径,计算含泛光点
         float radius = 0;
         public float Radius
         {
@@ -93,108 +48,69 @@ namespace ZIM
                 if (radius != 0)
                     return radius;
 
-                //var radiusDic = new Dictionary<int, float>() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } };
-                var radiusDic = new float[8];
-                foreach (var p in Pixels0)
-                {
-                    var dir = p - Center;
-                    var radius = dir.magnitude;
-                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
-                    if (degreeI > 7) degreeI = 0;       // 防止正好360度
+                return radius = CalculateRadius();
+            }
+        }
+        public float CalculateRadius()
+        {
+            //var radiusDic = new Dictionary<int, float>() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } };
+            var radiusDic = new float[8];
+            foreach (var p in Pixels0)
+            {
+                var dir = p.Point - Centroid;
+                var radius = dir.magnitude;
+                var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
+                if (degreeI > 7) degreeI = 0;       // 防止正好360度
 
-                    if (radius > radiusDic[degreeI])
-                        radiusDic[degreeI] = radius;
-                }
+                if (radius > radiusDic[degreeI])
+                    radiusDic[degreeI] = radius;
+            }
 
-                foreach (var p in Pixels1)
-                {
-                    var dir = p - Center;
-                    var radius = dir.magnitude;
-                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
-                    if (degreeI > 7) degreeI = 0;
+            foreach (var p in Pixels1)
+            {
+                var dir = p - Centroid;
+                var radius = dir.magnitude;
+                var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
+                if (degreeI > 7) degreeI = 0;
 
-                    if (radius > radiusDic[degreeI])
-                        radiusDic[degreeI] = radius;
-                }
-                return radius = radiusDic.Mean();
+                if (radius > radiusDic[degreeI])
+                    radiusDic[degreeI] = radius;
             }
+            return radiusDic.Mean();
         }
 
-        public List<Vector2> Pixels0;
-        public List<Vector2> Pixels1;
-
-        HashSet<(int, int)> grids0;
-        HashSet<(int, int)> grids1;
+        // 中心亮点
+        public List<IPixel<Vector2>> Pixels0 = new List<IPixel<Vector2>>();
+        // 泛光
+        public List<Vector2> Pixels1 = new List<Vector2>();
 
-        public PixelSpotArea(Vector2 location, (int, int) grid0, (int, int) grid1)
+        public PixelSpotArea(IEnumerable<IPixel<Vector2>> p)        // kmeans中用随机点初始化,作为中心
         {
-            Pixels0 = new List<Vector2>(1000);       // 预估的初始容量
-            Pixels1 = new List<Vector2>(5000);
-            this.grids0 = new HashSet<(int, int)>();        // hashset如果设置了太大的容量会降低效率
-            this.grids1 = new HashSet<(int, int)>();
-            Join(location, grid0, grid1);
+            Pixels0.AddRange(p);
         }
 
-        public PixelSpotArea(IList<PixelSpotArea> areas)
+        // 中心亮点
+        public void Add0(IPixel<Vector2> p)
         {
-            Pixels0 = new List<Vector2>(1000);
-            Pixels1 = new List<Vector2>(5000);
-            grids0 = new HashSet<(int, int)>();
-            grids1 = new HashSet<(int, int)>();
-            foreach (var a in areas)
-            {
-                Pixels0.AddRange(a.Pixels0);
-                grids0.AddRange(a.grids0);
-                grids1.AddRange(a.grids1);
-                //Center += a.Center * a.SpotPointCount;
-                //SpotPointCount += a.SpotPointCount;
-            }
-            //Center /= SpotPointCount;
+            Pixels0.Add(p);
         }
 
-        public bool Include0((int, int) grid) => grids0.Contains(grid);
-        public bool Include1((int, int) grid) => grids1.Contains(grid);
-
-        public void Join(Vector2 point) => Join(point, GetGrid0(point), GetGrid1(point));
-        public void Join(Vector2 point, (int x, int y) grid0, (int x, int y) grid1)
+        // 泛光
+        public void Add1(Vector2 point)
         {
-            Pixels0.Add(point);
-            //SpotPointCount++;
-            //Center += (point - Center) / SpotPointCount;
-            grids0.AddRange(new (int, int)[]
-            {
-                    (grid0.x + -1, grid0.y +  0) , (grid0.x + 1 , grid0.y + 0),
-                    (grid0.x + 1,  grid0.y + -1) , (grid0.x + -1, grid0.y + 1),
-                    (grid0.x + 0,  grid0.y + -1) , (grid0.x + 0 , grid0.y + 1),
-                    (grid0.x + -1, grid0.y + -1) , (grid0.x + 1 , grid0.y + 1),
-                    grid0
-            });
-            grids1.AddRange(new (int, int)[]
-            {
-                    (grid1.x + -1, grid1.y +  0) , (grid1.x + 1 , grid1.y + 0),
-                    (grid1.x + 1,  grid1.y + -1) , (grid1.x + -1, grid1.y + 1),
-                    (grid1.x + 0,  grid1.y + -1) , (grid1.x + 0 , grid1.y + 1),
-                    (grid1.x + -1, grid1.y + -1) , (grid1.x + 1 , grid1.y + 1),
-                    grid1
-            });
+            Pixels1.Add(point);
         }
 
-        // 不再join之后,才可以用add添加周围泛光
-        public void Add(Vector2 point)
+        public void Clear()
         {
-            //var radius = (point - Center).LengthManhattan();
-            //if (radius > MaxRadius)
-            //    MaxRadius = radius;
-            Pixels1.Add(point);
-            //var radius = (point - Center).LengthManhattan();
-            //if (radius > MaxRadius)
-            //    MaxRadius = radius;
+            Pixels0.Clear();
+            Pixels1.Clear();
         }
 
         public float TotalBrightness(Color[] pixels, Func<int, int, int> Vector2ToIndex, int outer_size = 3)
         {
-            (int x, int y) rectMin = ((int)(Center.x - Radius - outer_size), (int)(Center.y - Radius - outer_size));
-            (int x, int y) rectMax = ((int)(Center.x + Radius + outer_size), (int)(Center.y + Radius + outer_size));
+            (int x, int y) rectMin = ((int)(Centroid.x - Radius - outer_size), (int)(Centroid.y - Radius - outer_size));
+            (int x, int y) rectMax = ((int)(Centroid.x + Radius + outer_size), (int)(Centroid.y + Radius + outer_size));
 
             var total = 0f;
             Parallel.For(rectMin.x, rectMax.x, (i) =>

+ 11 - 9
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenIdentification.cs

@@ -30,7 +30,8 @@ namespace o0.Project
         //static bool LocateDebug = false;
         static bool LocateDebug = true;
 
-        public Geometry2D.Vector<int> Size { get; private set; }
+        public Geometry2D.Vector<int> Size => ScreenLocate.Main.CameraSize;
+
         public ScreenMap Screen;       // 识别到的屏幕,用于执行透视变换
 
         int capture = 0;
@@ -74,7 +75,7 @@ namespace o0.Project
         {
             if (ScreenLocate.Main.DebugScreenImage != null && ScreenLocate.Main.DebugOnEditorWin)     // 这段仅用于测试图片
             {
-                Size = new Geometry2D.Vector<int>(ScreenLocate.Main.DebugScreenImage.width, ScreenLocate.Main.DebugScreenImage.height);
+                ScreenLocate.Main.CameraSize = new Geometry2D.Vector<int>(ScreenLocate.Main.DebugScreenImage.width, ScreenLocate.Main.DebugScreenImage.height);
                 DebugImage(ScreenLocate.Main.DebugScreenImage);
                 Screen.QuadInCamera = new QuadrilateralInCamera(quadTemp[0], new Vector(ScreenLocate.Main.DebugScreenImage.width, ScreenLocate.Main.DebugScreenImage.height));
                 ScreenLocate.SetScreen(null);
@@ -114,8 +115,8 @@ namespace o0.Project
         void DebugImage(Texture2D image)
         {
             QuadrilateralFit(out Texture2D LocateTex, out Texture2D DrawLineTex, 5, image);
-            ScreenLocate.DebugTexture(2, LocateTex);
-            ScreenLocate.DebugTexture(3, DrawLineTex);
+            ScreenLocate.DebugTexture(1, LocateTex);
+            ScreenLocate.DebugTexture(2, DrawLineTex);
 
             // 融合线段和原图
             var pixel0 = image.GetPixels();
@@ -125,7 +126,7 @@ namespace o0.Project
             var texAdd = new Texture2D(image.width, image.height);
             texAdd.SetPixels(pixel0);
             texAdd.Apply();
-            ScreenLocate.DebugTexture(4, texAdd);
+            ScreenLocate.DebugTexture(3, texAdd);
 
             //var watch = new System.Diagnostics.Stopwatch();
             //watch.Start();
@@ -136,7 +137,8 @@ namespace o0.Project
             if (quadTemp.Count > 0)
             {
                 var quad = quadTemp[0];
-                ScreenLocate.Main.ShowScreen(ScreenLocate.Main.rawImage4.transform.GetChild(0) as RectTransform, new QuadrilateralInCamera(quad, image.Size().o0Vector()));
+                ScreenLocate.Main.ShowScreen(ScreenLocate.Main.outputRawImages[3].transform.GetChild(0) as RectTransform, 
+                    new QuadrilateralInCamera(quad, image.Size().o0Vector()));
 
                 // 透视变换
                 var srcWidth = LocateTex.width;
@@ -341,7 +343,7 @@ namespace o0.Project
                 delay--;
                 if (delay == 0)
                 {
-                    Size = new Geometry2D.Vector<int>(cam.width, cam.height);          // 记录当前的分辨率
+                    ScreenLocate.Main.CameraSize = new Geometry2D.Vector<int>(cam.width, cam.height);          // 记录当前的分辨率
                     Debug.Log("[ScreenIdentification] 采样纹理,分辨率: [" + Size.x + ", " + Size.y + "]");
                 }
                 return true;
@@ -650,8 +652,8 @@ namespace o0.Project
             //var (edge, edgeDir) = ScreenLocateTexLightedMat.IdentifyEdge();
             int conSize = 15;
             var (edge, edgeDir) = ScreenLocateTexLightedMat.zimIdentifyEdgeGradientAny(conSize);
-            //ScreenLocate.DebugTexture(4, ScreenLocateTexLighted.Too0Mat().IdentifyEdgeGradient().ToTex());
-            //ScreenLocate.DebugTexture(4, edge.ToTex());
+            ScreenLocate.DebugTexture(5, edgeDir.ToTex());
+            ScreenLocate.DebugTexture(4, edge.ToTex());
 
             var drawLineMap = new Matrix(ScreenLocateTexLightedMat.Size, Tiling: true);
             var minLength = locateIndex == -1 ? 50 : 50 * areaPercent;

+ 24 - 8
Assets/InfraredProject/WebCamera/Script/ZIM/InfraredLocate/ScreenMap.cs

@@ -30,8 +30,6 @@ namespace ZIM.Unity
             {
                 quadInCamera = value;
                 InitByQuad();
-                Debug.Log("QuadRect:" + QuadRect);
-                //Debug.Log(QuadRect);
             }
         }
 
@@ -61,6 +59,8 @@ namespace ZIM.Unity
 
                 var aabb = quad.AABBRect();
                 QuadRect = new Rect(aabb.Item1.x, aabb.Item1.y, aabb.Item2.x - aabb.Item1.x, aabb.Item2.y - aabb.Item1.y);
+                Debug.Log("Complete Screen Identification");
+                //Debug.Log("QuadRect:" + QuadRect);
             }
         }
 
@@ -78,25 +78,41 @@ namespace ZIM.Unity
 
         public bool UVInScreen(Vector2 v)
         {
-            return v.x > 0 && v.x < UVSize.x && v.y > 0 && v.y < UVSize.y;
+            return Active ? v.x > 0 && v.x < UVSize.x && v.y > 0 && v.y < UVSize.y : true;
         }
 
         // UV归一化到[0, 1]范围,仅用于输出最终结果
         public Vector2 UVNormalized(Vector2 location)
         {
-            return new Vector2(location.x / UVSize.x, location.y / UVSize.y);
+            return Active ? 
+                new Vector2(location.x / UVSize.x, location.y / UVSize.y) : 
+                new Vector2(location.x / ScreenLocate.Main.CameraSize.x, location.y / ScreenLocate.Main.CameraSize.y);
         }
 
         public Vector2 TransformToScreen(Vector2 vIn)
         {
-            var v = perspective.Transform(vIn.x, vIn.y);
-            return new Vector2(v.x, v.y);
+            if (Active)
+            {
+                var v = perspective.Transform(vIn.x, vIn.y);
+                return new Vector2(v.x, v.y);
+            }
+            else
+            {
+                return vIn;
+            }
         }
 
         public Vector2 TransformToCamera(Vector2 vIn)
         {
-            var v = perspective.TransformInverse(vIn.x, vIn.y);
-            return new Vector2(v.x, v.y);
+            if (Active)
+            {
+                var v = perspective.TransformInverse(vIn.x, vIn.y);
+                return new Vector2(v.x, v.y);
+            }
+            else
+            {
+                return vIn;
+            }
         }
     }
 }

+ 96 - 108
Assets/InfraredProject/WebCamera/Script/ZIM/ScreenLocate.cs

@@ -4,6 +4,7 @@ using InfraredManager;
 using o0;
 using SLAMUVC;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using UnityEngine;
@@ -18,17 +19,18 @@ public partial class ScreenLocate : MonoBehaviour
 {
     public InfraredCameraHelper InfraredCameraHelper;
     private const string TAG = "ScreenLocate#";
+
+    public enum InfraredCount : int
+    {
+        Single = 1,
+        Double = 2
+    }
     enum Mode
     {
         InfraredLocate,
         ScreenMap,
         ScreenLocateManual
     }
-    enum InfraredCount
-    {
-        Single,
-        Double
-    }
     enum Platform
     {
         Window,
@@ -55,7 +57,6 @@ public partial class ScreenLocate : MonoBehaviour
         }
     }
     public InfraredSpot[] infraredSpotBuffer;
-    InfraredCount infraredCount;
     public string GetInfraredCount() { return infraredCount.ToString(); }
 
     /// <summary>
@@ -92,21 +93,15 @@ public partial class ScreenLocate : MonoBehaviour
     public RectTransform ScreenQuad;
     public Toggle SaveToggle;
 
-    public Vector2 ScreenLocateCameraSize;       // 屏幕识别需要的目标分辨率,摄像机分辨率变化时该分辨率也会跟着调整
-
     public bool ShowScreenQuad = false;
 
-    public RawImage rawImage;
-    public RawImage rawImage1;
-    public RawImage rawImage2;
-    public RawImage rawImage3;
-    public RawImage rawImage4;
-    public RawImage rawImage5;
+    public List<RawImage> outputRawImages;
     public RawImage FullScreenImage;
 
+    public PixelCheaker ScreenPixelCheaker;
     public InfraredSpotSettings InfraredSpotSettings;
 
-    //public ZIMWebCamera zimWebCamera => GetComponent<ZIMWebCamera>();
+    public o0.Geometry2D.Vector<int> CameraSize { get; set; }
 
     public Texture2D DebugScreenImage;
     public bool DebugOnEditorWin = false;
@@ -116,6 +111,8 @@ public partial class ScreenLocate : MonoBehaviour
     //是否单点显示
     public bool bSinglePoint = true;//默认单点识别
 
+    public InfraredCount infraredCount = InfraredCount.Single;
+
     bool bIdentifyRed = true;//默认设备红色
 
     bool bIdentifyGreen = true;
@@ -149,7 +146,7 @@ public partial class ScreenLocate : MonoBehaviour
     InfraredLocate infraredLocate;
     RectTransform canvas;
     Mode mode;
-    List<Vector2> pointManual = new List<Vector2>();
+    List<(Vector2 pos, GameObject go)> pointManual = new List<(Vector2, GameObject)>();
 
     //o0.Project.WebCam o0WebCam = null;
     o0.Project.ScreenIdentification screenIdentification;
@@ -162,51 +159,62 @@ public partial class ScreenLocate : MonoBehaviour
     bool bAutomaticRecognitionStart { get; set; } = false;//是否进行捕获
     bool bAutomaticRecognitionEnd { get; set; } = false;//是否结束捕获
 
+    public RectTransform BackQuad = null;
+
     static public ScreenLocate Main;
-    static public List<RawImage> DebugImage = new List<RawImage>();
-    static public RectTransform BackQuad = null;
+
+    static public void AutoLightPixels(Color[] pixels, int width, int height)
+    {
+        var newTex = pixels.zimAutoLightSimple(width, height);
+        DebugTexture(6, newTex);
+        Main.FullScreenImage.texture = newTex;
+    }
+
     static public void DebugTexture(int index, Texture texture)
     {
-        Destroy(DebugImage[index].texture);
-        DebugImage[index].texture = texture;
+        //Application.
+        LateDestory(Main.outputRawImages[index].texture);
+        Main.outputRawImages[index].texture = texture;
     }
+    static void LateDestory(UnityEngine.Object o) => Main.StartCoroutine(Main.LateDestoryIEnum(o));
+
     static public void SetScreen(UnityEngine.Color? color = null)
     {
-        if (BackQuad == null)
+        if (Main.BackQuad == null)
         {
             var canvas = GameObject.Find("WebCameraView").GetComponent<RectTransform>();
             var background = canvas.Find("Background");
-            BackQuad = background.GetChild(0).GetComponent<RectTransform>();
+            Main.BackQuad = background.GetChild(0).GetComponent<RectTransform>();
         }
-        BackQuad.parent.gameObject.SetActive(color != null);
-        BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
+        Main.BackQuad.parent.gameObject.SetActive(color != null);
+        Main.BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
         //Debug.Log("Set Screen " + color.GetColorName());
     }
     static public void SetScreen(Rect rect, UnityEngine.Color? color = null)
     {
-        if (BackQuad == null)
+        if (Main.BackQuad == null)
         {
             var canvas = GameObject.Find("WebCameraView").GetComponent<RectTransform>();
             var background = canvas.Find("Background");
-            BackQuad = background.GetChild(0).GetComponent<RectTransform>();
+            Main.BackQuad = background.GetChild(0).GetComponent<RectTransform>();
         }
-        BackQuad.parent.gameObject.SetActive(color != null);
-        BackQuad.anchorMin = rect.min;
-        BackQuad.anchorMax = rect.max;
-        BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
+        Main.BackQuad.parent.gameObject.SetActive(color != null);
+        Main.BackQuad.anchorMin = rect.min;
+        Main.BackQuad.anchorMax = rect.max;
+        Main.BackQuad.GetComponent<RawImage>().color = color ?? Color.black;
         //Debug.Log("Set Screen " + color.GetColorName());
     }
     static void DebugBackQuad(Rect? rect = null)
     {
-        if (BackQuad)
+        if (Main.BackQuad)
         {
-            BackQuad.parent.GetComponent<RawImage>().enabled = false;
-            BackQuad.GetComponent<RawImage>().color = Color.white;
-            BackQuad.parent.gameObject.SetActive(!BackQuad.parent.gameObject.activeSelf);
+            Main.BackQuad.parent.GetComponent<RawImage>().enabled = false;
+            Main.BackQuad.GetComponent<RawImage>().color = Color.white;
+            Main.BackQuad.parent.gameObject.SetActive(!Main.BackQuad.parent.gameObject.activeSelf);
             if (rect.HasValue)
             {
-                BackQuad.anchorMin = rect.Value.min;
-                BackQuad.anchorMax = rect.Value.max;
+                Main.BackQuad.anchorMin = rect.Value.min;
+                Main.BackQuad.anchorMax = rect.Value.max;
             }
         }
     }
@@ -252,14 +260,6 @@ public partial class ScreenLocate : MonoBehaviour
     {
         //mainContext = SynchronizationContext.Current;
 
-        DebugImage.Add(rawImage);
-        DebugImage.Add(rawImage1);
-        DebugImage.Add(rawImage2);
-        DebugImage.Add(rawImage3);
-        DebugImage.Add(rawImage4);
-        DebugImage.Add(rawImage5);
-        DebugImage.Add(FullScreenImage);
-
         canvas = transform.GetComponent<RectTransform>();
 
         mode = Mode.InfraredLocate;
@@ -282,6 +282,15 @@ public partial class ScreenLocate : MonoBehaviour
         #endregion
     }
 
+    IEnumerator LateDestoryIEnum(UnityEngine.Object o)
+    {
+        while (true)
+        {
+            yield return new WaitForEndOfFrame();
+            Destroy(o);
+        }
+    }
+
     //ZIMWebCamera场景使用
     public void WebCamIsReady(Texture texture)
     {
@@ -326,7 +335,6 @@ public partial class ScreenLocate : MonoBehaviour
         if (bAutomaticRecognitionEnd) {
             bAutomaticRecognitionEnd = false;
             Debug.Log("[ScreenLocate] UVCUpdate 结束捕获,当前摄像机分辨率为: " + mUVCCameraInfo.Size);
-            ScreenLocateCameraSize = mUVCCameraInfo.Size;
             bAutomaticRecognition = false;
         }
     }
@@ -375,7 +383,7 @@ public partial class ScreenLocate : MonoBehaviour
         }
         if (infraredLocate == null)
         {
-            infraredLocate = new InfraredLocate(mUVCCameraInfo, screenIdentification, InfraredSpotSettings);
+            infraredLocate = new InfraredLocate(mUVCCameraInfo, screenIdentification, InfraredSpotSettings, ScreenPixelCheaker);
             //InfraredDemo 初始化
             //float redfilterValue = PlayerPrefs.GetFloat("Init redFilterSliderValue", 0.8f);
             //Debug.Log("Init Red filterValue:" + redfilterValue);
@@ -405,39 +413,6 @@ public partial class ScreenLocate : MonoBehaviour
 
         }
 
-        if (mode == Mode.ScreenLocateManual)
-        {
-            //if (Input.GetMouseButtonDown(0))
-            //{
-            //    var mouse = Input.mousePosition;
-            //    var u = mouse.x / Screen.width;
-            //    var v = mouse.y / Screen.height;
-            //    u = Math.Clamp(u, 0, 1);
-            //    v = Math.Clamp(v, 0, 1);
-            //    pointManual.Add(new Vector2(u * mUVCTexture.width, v * mUVCTexture.height));
-            //    var obj = Instantiate(Resources.Load("Point")) as GameObject;
-            //    obj.transform.SetParent(FullScreenImage.transform);
-            //    obj.transform.localPosition = new Vector2(u, v).pixelToLocalPosition_AnchorCenter(new Vector2(1, 1), FullScreenImage.rectTransform.rect);
-
-            //    if (pointManual.Count == 1)
-            //        Info.text = "左键单击屏幕 右下角";
-            //    else if (pointManual.Count == 2)
-            //        Info.text = "左键单击屏幕 右上角";
-            //    else if (pointManual.Count == 3)
-            //        Info.text = "左键单击屏幕 左上角";
-            //    else if (pointManual.Count == 4)
-            //    {
-            //        screenIdentification.LocateScreenManual(new OrdinalQuadrilateral(pointManual[0].o0Vector(), pointManual[1].o0Vector(), pointManual[3].o0Vector(), pointManual[2].o0Vector()));
-            //        pointManual.Clear();
-            //        ShowScreen(screenIdentification.Screen.Quad);
-            //        foreach (Transform i in FullScreenImage.transform)
-            //            Destroy(i.gameObject);
-            //        ToMode(Mode.InfraredLocate);
-            //    }
-            //}
-            return;
-        }
-
         //var t0 = Time.realtimeSinceStartup;
 
         /* New*/
@@ -464,6 +439,7 @@ public partial class ScreenLocate : MonoBehaviour
             //        Debug.Log("[ScreenLocate] log2:[" + (int)getUVCCameraInfoSize.x + ", " + (int)getUVCCameraInfoSize.y + "]");
             //    }
             //}
+
             //如果是连接了蓝牙设备,并且不是9轴设备。不进行识别算法处理
             if (BluetoothAim.ins?.status == BluetoothStatusEnum.ConnectSuccess && AimHandler.ins && AimHandler.ins.bRuning9Axis()) return;
 
@@ -471,28 +447,35 @@ public partial class ScreenLocate : MonoBehaviour
             CreateUVCTexture2DIfNeeded((int)getUVCCameraInfoSize.x, (int)getUVCCameraInfoSize.y);
             if (!screenIdentification.Update(mUVCTexture2D))
             {
-                if (!screenIdentification.Screen.Active)
-                {
-                    //DebugTexture(1, mUVCTexture2D.zimAutoLightSimple());
-                    return;
-                }
-                //if (mUVCCameraInfo.Size != ScreenLocateCameraSize)   // 摄像机分辨率发生改变时执行
-                //{
-                //    RefreshScreenQuad(mUVCCameraInfo.Size);
-                //    return;
-                //}
-                if (mode == Mode.InfraredLocate)
-                {
-                    //0,0, cameraTexture2D.width, cameraTexture2D.height,0
-                    var pixels = mUVCTexture2D.GetPixels();       // 从左往右、从下往上
+                CameraSize = new o0.Geometry2D.Vector<int>((int)getUVCCameraInfoSize.x, (int)getUVCCameraInfoSize.y);
+                var pixels = mUVCTexture2D.GetPixels();       // 从左往右、从下往上
 
-                    //InfraredSpots = infraredLocate.Update(pixels);
-                    if (bSinglePoint)
-                        infraredSpotBuffer = infraredLocate.UpdateSingle(pixels);
-                    else
-                        infraredSpotBuffer = infraredLocate.Update(pixels);
+                AutoLightPixels(pixels, CameraSize.x, CameraSize.y);
+                //return;
+                //InfraredSpots = infraredLocate.Update(pixels);
 
+                if (bSinglePoint)
+                    infraredSpotBuffer = infraredLocate.UpdateSingle(pixels);
+                else
+                    infraredSpotBuffer = infraredLocate.Update(pixels);
 
+                if (mode == Mode.ScreenLocateManual)
+                {
+                    for (int i = 0; i < infraredSpotBuffer.Length; i++)
+                    {
+                        if (infraredSpotBuffer[i].CameraLocation != null)
+                        {
+                            // 检测到光点
+                            var posInCanvas = infraredSpotBuffer[i].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(CameraSize, FullScreenImage.rectTransform.rect);
+                            CrosshairInCamera[i].gameObject.SetActive(true);
+                            CrosshairInCamera[i].anchoredPosition = posInCanvas;
+                        }
+                        else
+                            CrosshairInCamera[i].gameObject.SetActive(false);
+                    }
+                }
+                else if(mode == Mode.InfraredLocate)
+                {
                     if (mPlatform == Platform.Window) //渲染ui上面的点。进入游戏可以隐藏
                     {
                         for (int i = 0; i < infraredSpotBuffer.Length; i++)
@@ -500,7 +483,7 @@ public partial class ScreenLocate : MonoBehaviour
                             if (infraredSpotBuffer[i].CameraLocation != null)
                             {
                                 // 检测到光点
-                                var posInCanvas = infraredSpotBuffer[i].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(mUVCCameraInfo.Size, rawImage.rectTransform.rect);
+                                var posInCanvas = infraredSpotBuffer[i].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(CameraSize, outputRawImages[0].rectTransform.rect);
                                 CrosshairInCamera[i].gameObject.SetActive(true);
                                 CrosshairInCamera[i].anchoredPosition = posInCanvas;
                             }
@@ -591,12 +574,6 @@ public partial class ScreenLocate : MonoBehaviour
                 }
                 else if (mode == Mode.ScreenMap && DebugOnEditorWin)
                 {
-                    var pixels = mUVCTexture2D.GetPixels();
-                    if (infraredCount == InfraredCount.Single)
-                        infraredSpotBuffer = infraredLocate.UpdateSingle(pixels);
-                    else if (infraredCount == InfraredCount.Double)
-                        infraredSpotBuffer = infraredLocate.Update(pixels);
-
                     for (int i = 0; i < infraredSpotBuffer.Length; i++)
                     {
                         if (infraredSpotBuffer[i].ScreenUV != null)
@@ -722,6 +699,7 @@ public partial class ScreenLocate : MonoBehaviour
         if (DebugScreenImage)
         {
             screenIdentification = new o0.Project.ScreenIdentification();
+            CameraSize = new o0.Geometry2D.Vector<int>(DebugScreenImage.width, DebugScreenImage.height);
             WebCamIsReady(DebugScreenImage);
 
             CreateUVCTexture2DIfNeeded();
@@ -744,6 +722,10 @@ public partial class ScreenLocate : MonoBehaviour
         EnterResolution = mUVCCameraInfo.Size;// 记录一下进入前的分辨率(游戏场景的分辨率,比识别时更低)
         Vector2 _HighResolution = mUVCCameraInfo.CurrentCalibrationResolution; //最高的分辨率
         Resize((int)_HighResolution.x, (int)_HighResolution.y);
+
+        if (DebugOnEditorWin)
+            screenIdentification.LocateScreen();
+
         //CreateUVCTexture2DIfNeeded();
         // log1 = true;
         // log2 = true;
@@ -842,6 +824,13 @@ public partial class ScreenLocate : MonoBehaviour
     {
         ToMode(Mode.ScreenLocateManual);
     }
+
+    // 重置屏幕识别的数据
+    public void ResetScreenIdentification()
+    {
+        screenIdentification.Screen.QuadInCamera = null;
+    }
+
     /// <summary>
     /// 固定的顶点顺序: 左下,右下,左上,右上
     /// </summary>
@@ -1008,11 +997,9 @@ public partial class ScreenLocate : MonoBehaviour
             foreach (var i in CrosshairInScreen)
                 i.gameObject.SetActive(false);
             FullScreenImage.gameObject.SetActive(false);
+            ScreenPixelCheaker.HideImage();
             Info.transform.SetSiblingIndex(transform.childCount - 4);
             this.mode = Mode.InfraredLocate;
-            DebugTexture(6, null);
-            //DebugTexture(1, null); //null
-            // rawImage1.texture = null;
 
 #if (!NDEBUG && DEBUG && ENABLE_LOG)
             Console.WriteLine($"{TAG} Mode.InfraredLocate:已识别到屏幕:{screenIdentification.Screen.Active}");
@@ -1022,6 +1009,7 @@ public partial class ScreenLocate : MonoBehaviour
         {
             Info.text = "左键单击屏幕 左下角";
             FullScreenImage.gameObject.SetActive(true);
+            ScreenPixelCheaker.ShowImage();
             Info.transform.SetSiblingIndex(transform.childCount - 1);
 
             // var newTex = WebCamera.webCamTexture.AutoLight(10);

+ 33 - 9
Assets/InfraredProject/WebCamera/Script/ZIM/ZIMUnity/Extension.cs

@@ -7,7 +7,7 @@ using Unity.VisualScripting;
 using UnityEngine;
 using UnityEngine.Internal;
 
-namespace ZIM
+namespace ZIM.Unity
 {
     public static partial class Extension
     {
@@ -31,6 +31,11 @@ namespace ZIM
             return new o0.Color(c.r, c.g, c.b, c.a);
         }
 
+        public static Color RGBMutiply(this UnityEngine.Color c, float multiplier)
+        {
+            return new Color(c.r * multiplier, c.g * multiplier, c.b * multiplier, c.a);
+        }
+
         public static Vector2 Size(this Texture tex) => new Vector2(tex.width, tex.height);
 
         private static Dictionary<Color, string> colorNames = new Dictionary<Color, string>()
@@ -125,6 +130,8 @@ namespace ZIM
             return (float)a;
         }
 
+        public static double LengthManhattan(this Vector2 a, Vector2 b) => Mathf.Abs(a.x - b.x) + Mathf.Abs(a.y - b.y);
+
         public static Texture2D zimAutoLightSimple(this WebCamTexture texture)
         {
             var pixel = texture.GetPixels();
@@ -142,31 +149,48 @@ namespace ZIM
             return new Vector2(texture.width, texture.height);
         }
 
+        // 亮点放大到1
+        public static Texture2D zimAutoLightSpot(this WebCamTexture texture)
+        {
+            var pixel = texture.GetPixels();
+            float[] brightness = new float[pixel.Length];
+            Parallel.For(0, pixel.Length, (i) => brightness[i] = pixel[i].Brightness());
+
+            var max = Enumerable.Max(brightness);
+            var scale = max >= 0.5f ? 1 / max : 1.3f;        // 设个阈值 0.5
+
+            Parallel.For(0, pixel.Length, (i) => pixel[i] = pixel[i].RGBMutiply(scale));
+
+            var tex = new Texture2D(texture.width, texture.height);
+            tex.SetPixels(pixel);
+            tex.Apply();
+            return tex;
+        }
 
-        // 效果不太好
+        // 效率高,效果一般,benny写的效果更
         public static Texture2D zimAutoLightSimple(this Color[] pixel, int width, int height)
         {
             Color[] p = new Color[pixel.Length];
 
-            var brightness16 = new int[256];
-            for (int i = 0; i < width / 7; i++)
+            var brightness256 = new int[256];
+            for (int i = 0; i < width / 7; i++)     // 间隔采样,得到256色彩下的直方图
             {
                 for (int j = 0; j < height / 7; j++)
                 {
                     var index = j * 7 * width + i * 7;
                     var b = pixel[index].Brightness(256);
-                    brightness16[b]++;
+                    brightness256[b]++;
                 }
             }
             var limit = (int)(width * height / 49 * 0.001f);    // 取亮度大约在前0.1%的值(不取最大的因为可能会被个别亮点影响)
             int select = 255;
             for (; select >= 0; select--)
             {
-                limit -= brightness16[select];
+                limit -= brightness256[select];
                 if (limit <= 0)
                     break;
             }
-            var scale = 0.85f / (select / 256f);
+            var scale = 0.85f / (select / 256f);        // 找到高亮的像素,计算放大系数
 
             //var avgBrightness = 0f;
             //Parallel.For(0, pixel.Length / 256, (i) => avgBrightness += pixel[i * 256].Brightness());
@@ -175,7 +199,7 @@ namespace ZIM
 
             if (float.IsInfinity(scale))
                 return null;
-            Parallel.For(0, p.Length, (i) => p[i] = pixel[i] * scale);
+            Parallel.For(0, p.Length, (i) => p[i] = pixel[i].RGBMutiply(scale));
 
             var tex = new Texture2D(width, height);
             tex.SetPixels(p);
@@ -201,7 +225,7 @@ namespace ZIM
             var scale = 1 / Enumerable.Max(p) * (2 + brightness);
             if (float.IsInfinity(scale))
                 return null;
-            Parallel.For(0, p.Length, (i) => pixel[i] *= scale);        // 归一化
+            Parallel.For(0, p.Length, (i) => pixel[i] = pixel[i].RGBMutiply(scale));        // 归一化
 
             var tex = new Texture2D(width, height);
             tex.SetPixels(pixel);

+ 1 - 0
Assets/InfraredProject/WebCamera/Script/ZIM/ZIMUnity/ZIMWebCamera.cs

@@ -150,6 +150,7 @@ public class ZIMWebCamera : MonoBehaviour
         InfraredScreenPositioningView _infraredScreenPositioningView = FindObjectOfType<InfraredScreenPositioningView>();
         _infraredScreenPositioningView.enterFromZimWebCamera = true;
         _infraredScreenPositioningView.transform.SetParent(mParent);
+        ScreenLocate.Main.ResetScreenIdentification();
     }
 
 

+ 16 - 15
Assets/InfraredProject/WebCamera/Test.cs

@@ -1,8 +1,9 @@
-using System.Collections;
+锘縰sing System.Collections;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine.UI;
 using ZIM;
+using ZIM.Unity;
 
 public class Test : MonoBehaviour
 {
@@ -45,7 +46,7 @@ public class Test : MonoBehaviour
 
         if (_webCamTexture != null)
         {
-            Debug.LogError("开启失败,请先关闭正在使用的摄像头!");
+            Debug.LogError("寮€鍚�け璐ワ紝璇峰厛鍏抽棴姝e湪浣跨敤鐨勬憚鍍忓ご锛�");
             return;
         }
         if (Application.HasUserAuthorization(UserAuthorization.WebCam))
@@ -58,7 +59,7 @@ public class Test : MonoBehaviour
             }
             if (devices.Length == 0)
             {
-                Debug.LogError("开启失败,没找到可用的摄像头!");
+                Debug.LogError("寮€鍚�け璐ワ紝娌℃壘鍒板彲鐢ㄧ殑鎽勫儚澶达紒");
                 return;
             }
 
@@ -66,12 +67,12 @@ public class Test : MonoBehaviour
             _webCamTexture = new WebCamTexture(deviceName, 1280, 720, 30);
             _webCamTexture.Play();
             if (rawImage) rawImage.texture = _webCamTexture;
-            Debug.Log("成功开启摄像头 " + deviceName);
+            Debug.Log("鎴愬姛寮€鍚�憚鍍忓ご " + deviceName);
 
         }
         else
         {
-            Debug.LogError("开启失败,用户未授予摄像头权限!");
+            Debug.LogError("寮€鍚�け璐ワ紝鐢ㄦ埛鏈�巿浜堟憚鍍忓ご鏉冮檺锛�");
         }
     }
 
@@ -204,22 +205,22 @@ public class Test : MonoBehaviour
         {
             int width = source.width;
             int height = source.height;
-            // 创建临时的RenderTexture
+            // 鍒涘缓涓存椂鐨凴enderTexture
             RenderTexture renderTex = RenderTexture.GetTemporary(width, height, 24, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
-            // 复制source的纹理到RenderTexture里
+            // 澶嶅埗source鐨勭汗鐞嗗埌RenderTexture閲�
             Graphics.Blit(source, renderTex);
-            // 开启当前RenderTexture激活状态
+            // 寮€鍚�綋鍓峈enderTexture婵€娲荤姸鎬�
             RenderTexture previous = RenderTexture.active;
             RenderTexture.active = renderTex;
-            // 创建修改后的纹理,保持与源纹理相同压缩格式
+            // 鍒涘缓淇�敼鍚庣殑绾圭悊锛屼繚鎸佷笌婧愮汗鐞嗙浉鍚屽帇缂╂牸寮�
             Texture2D resizedTexture = new Texture2D(width, height, TextureFormat.ARGB32, false,true);
-            // 读取像素到创建的纹理中
+            // 璇诲彇鍍忕礌鍒板垱寤虹殑绾圭悊涓�
             resizedTexture.ReadPixels(new Rect(0, 0, width, height), 0, 0);
-            // 应用修改到GPU上
+            // 搴旂敤淇�敼鍒癎PU涓�
             resizedTexture.Apply();
-            // 停止当前RenderTexture工作
+            // 鍋滄�褰撳墠RenderTexture宸ヤ綔
             RenderTexture.active = previous;
-            // 释放内存
+            // 閲婃斁鍐呭瓨
             RenderTexture.ReleaseTemporary(renderTex);
             return resizedTexture;
         }
@@ -266,8 +267,8 @@ public class Test : MonoBehaviour
         if (sourceTexture != null)
         {
             Texture2D newTextureByRawData = new Texture2D(sourceTexture.width, sourceTexture.height, sourceTexture.format, false);
-            // 对于运行时纹理生成,也可以通过GetRawTextureData直接写入纹理数据,返回一个Unity.Collections.NativeArray
-            // 这可以更快,因为它避免了 LoadRawTextureData 会执行的内存复制。
+            // 瀵逛簬杩愯�鏃剁汗鐞嗙敓鎴愶紝涔熷彲浠ラ€氳繃GetRawTextureData鐩存帴鍐欏叆绾圭悊鏁版嵁锛岃繑鍥炰竴涓猆nity.Collections.NativeArray
+            // 杩欏彲浠ユ洿蹇�紝鍥犱负瀹冮伩鍏嶄簡 LoadRawTextureData 浼氭墽琛岀殑鍐呭瓨澶嶅埗銆�
             newTextureByRawData.LoadRawTextureData(sourceTexture.GetRawTextureData());
             newTextureByRawData.Apply();
             return newTextureByRawData;

+ 266 - 1
Assets/InfraredProject/WebCamera/zimWebCamera.unity

@@ -38,7 +38,6 @@ RenderSettings:
   m_ReflectionIntensity: 1
   m_CustomReflection: {fileID: 0}
   m_Sun: {fileID: 0}
-  m_IndirectSpecularColor: {r: 0.37311953, g: 0.38074014, b: 0.3587274, a: 1}
   m_UseRadianceAmbientProbe: 0
 --- !u!157 &3
 LightmapSettings:
@@ -123,6 +122,181 @@ NavMeshSettings:
     debug:
       m_Flags: 0
   m_NavMeshData: {fileID: 0}
+--- !u!1001 &36100229
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1889926945}
+    m_Modifications:
+    - target: {fileID: 2705055563425357305, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_Enabled
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357306, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_Name
+      value: PixelCheaker
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_Pivot.x
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_Pivot.y
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_RootOrder
+      value: 18
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchorMax.x
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchorMax.y
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchorMin.x
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchorMin.y
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_SizeDelta.x
+      value: 1920
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_SizeDelta.y
+      value: 1080
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalScale.x
+      value: 2.48
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalScale.y
+      value: 1.8
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchoredPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_AnchoredPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2705055563425357307, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+--- !u!114 &36100230 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 2705055563425357311, guid: ab30c51d9dbef7d4db660e2ae2075902, type: 3}
+  m_PrefabInstance: {fileID: 36100229}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 85aeb100c8367e948b420ca29c1cfd66, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!114 &173003435 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618027212933216, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!114 &419631730 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618028593834237, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!114 &1215516380 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618028782547288, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!114 &1414780811 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618027396250555, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!114 &1574699272 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618027165090661, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
 --- !u!1 &1650142577
 GameObject:
   m_ObjectHideFlags: 0
@@ -218,6 +392,22 @@ Transform:
   m_Father: {fileID: 0}
   m_RootOrder: 2
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1796194231 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 1816618028071740130, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+--- !u!224 &1889926945 stripped
+RectTransform:
+  m_CorrespondingSourceObject: {fileID: 1816618027770806189, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
 --- !u!1 &2001504855
 GameObject:
   m_ObjectHideFlags: 0
@@ -325,6 +515,18 @@ PrefabInstance:
       propertyPath: m_AnchoredPosition.y
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 1816618027212933218, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_AnchoredPosition.x
+      value: -776
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618027212933218, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_AnchoredPosition.y
+      value: -834
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618027212933219, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_IsActive
+      value: 1
+      objectReference: {fileID: 0}
     - target: {fileID: 1816618027243654231, guid: a749571a9e509ee42a24695575e9a483, type: 3}
       propertyPath: m_AnchorMax.y
       value: 0
@@ -473,6 +675,46 @@ PrefabInstance:
       propertyPath: m_LocalEulerAnglesHint.z
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: DebugScreenImage
+      value: 
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: ScreenPixelCheaker
+      value: 
+      objectReference: {fileID: 36100230}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.size
+      value: 7
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[0]
+      value: 
+      objectReference: {fileID: 1796194231}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[1]
+      value: 
+      objectReference: {fileID: 1414780811}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[2]
+      value: 
+      objectReference: {fileID: 1215516380}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[3]
+      value: 
+      objectReference: {fileID: 1574699272}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[4]
+      value: 
+      objectReference: {fileID: 419631730}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[5]
+      value: 
+      objectReference: {fileID: 173003435}
+    - target: {fileID: 1816618027770806195, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: outputRawImages.Array.data[6]
+      value: 
+      objectReference: {fileID: 1816618028118130020}
     - target: {fileID: 1816618028247322129, guid: a749571a9e509ee42a24695575e9a483, type: 3}
       propertyPath: m_AnchorMax.y
       value: 0
@@ -509,5 +751,28 @@ PrefabInstance:
       propertyPath: m_AnchoredPosition.y
       value: 0
       objectReference: {fileID: 0}
+    - target: {fileID: 1816618028747326785, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_SizeDelta.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618028747326785, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_AnchoredPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1816618028781697711, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+      propertyPath: m_Name
+      value: CameraAutoLight
+      objectReference: {fileID: 0}
     m_RemovedComponents: []
   m_SourcePrefab: {fileID: 100100000, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+--- !u!114 &1816618028118130020 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 4268820032225842432, guid: a749571a9e509ee42a24695575e9a483, type: 3}
+  m_PrefabInstance: {fileID: 1816618028118130019}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 

+ 390 - 3
Assets/SmartBow/Resources/SmartBow/Prefabs/Views/Home/InfraredScreenPositioningView.prefab

@@ -1121,7 +1121,8 @@ RectTransform:
   m_LocalPosition: {x: 0, y: 0, z: 0}
   m_LocalScale: {x: 1, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
-  m_Children: []
+  m_Children:
+  - {fileID: 6013103678145184470}
   m_Father: {fileID: 518485857765021418}
   m_RootOrder: 0
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
@@ -1354,6 +1355,7 @@ MonoBehaviour:
   line: {fileID: 5320514685055518600}
   slider: {fileID: 1710848639}
   rawImage: {fileID: 4109810863229036433}
+  crosshair: {fileID: 6013103678145184470}
   textTip1: {fileID: 6474032259244959646}
   textTip2: {fileID: 4901933954594160457}
   btnAuto: {fileID: 2949056324684405733}
@@ -1593,7 +1595,7 @@ RectTransform:
   - {fileID: 7855798539375045847}
   - {fileID: 4365742840661124964}
   m_Father: {fileID: 518485857765021418}
-  m_RootOrder: 2
+  m_RootOrder: 3
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0.5, y: 0.5}
   m_AnchorMax: {x: 0.5, y: 0.5}
@@ -2040,6 +2042,86 @@ RectTransform:
   m_AnchoredPosition: {x: 0, y: 0}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &2830622171831208811
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8331608948875303033}
+  - component: {fileID: 6421951175371325834}
+  - component: {fileID: 2407834152080892515}
+  m_Layer: 5
+  m_Name: Text
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &8331608948875303033
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2830622171831208811}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 8072716410762509428}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 274.68, y: 137}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6421951175371325834
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2830622171831208811}
+  m_CullTransparentMesh: 0
+--- !u!114 &2407834152080892515
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2830622171831208811}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 0, g: 0, b: 0, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_FontData:
+    m_Font: {fileID: 12800000, guid: 6b6cc7ab59ef00947950b61fdca2d042, type: 3}
+    m_FontSize: 78
+    m_FontStyle: 0
+    m_BestFit: 0
+    m_MinSize: 2
+    m_MaxSize: 78
+    m_Alignment: 4
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 1
+    m_LineSpacing: 1
+  m_Text: "\u8BB0\u5F55\u7EA2\u5916\u70B9(1)"
 --- !u!1 &2967645782101442890
 GameObject:
   m_ObjectHideFlags: 0
@@ -2475,6 +2557,79 @@ MonoBehaviour:
   m_ChildScaleWidth: 0
   m_ChildScaleHeight: 0
   m_ReverseArrangement: 0
+--- !u!1 &3632382463857017291
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6013103678145184470}
+  - component: {fileID: 5289673386120978708}
+  - component: {fileID: 8109953669238480337}
+  m_Layer: 5
+  m_Name: crosshair
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!224 &6013103678145184470
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3632382463857017291}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 5352980848473047839}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 200, y: 200}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5289673386120978708
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3632382463857017291}
+  m_CullTransparentMesh: 1
+--- !u!114 &8109953669238480337
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3632382463857017291}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 1344c3c82d62a2a41a3576d8abb8e3ea, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Texture: {fileID: 2800000, guid: f409aa79f7c0c064382caf85796c19e0, type: 3}
+  m_UVRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
 --- !u!1 &3803501476442448021
 GameObject:
   m_ObjectHideFlags: 0
@@ -3067,6 +3222,7 @@ RectTransform:
   m_ConstrainProportionsScale: 0
   m_Children:
   - {fileID: 5352980848473047839}
+  - {fileID: 6970137624786410075}
   - {fileID: 7371505907208373617}
   - {fileID: 1721352479526113342}
   m_Father: {fileID: 1063506209527944585}
@@ -3649,6 +3805,82 @@ MonoBehaviour:
     m_VerticalOverflow: 1
     m_LineSpacing: 1
   m_Text: 4
+--- !u!1 &6970137624786410074
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6970137624786410075}
+  - component: {fileID: 6970137624786410078}
+  - component: {fileID: 6970137624786410073}
+  m_Layer: 5
+  m_Name: Mask
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &6970137624786410075
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6970137624786410074}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 2.9551451, y: 2.1635885, z: 1.3192612}
+  m_ConstrainProportionsScale: 1
+  m_Children: []
+  m_Father: {fileID: 518485857765021418}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 1920, y: 1080}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6970137624786410078
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6970137624786410074}
+  m_CullTransparentMesh: 1
+--- !u!114 &6970137624786410073
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6970137624786410074}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 0
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 21300000, guid: f3b60078f3450d1459d9642ca6404c7d, type: 3}
+  m_Type: 0
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
 --- !u!1 &7041946675274892591
 GameObject:
   m_ObjectHideFlags: 0
@@ -3689,6 +3921,160 @@ RectTransform:
   m_AnchoredPosition: {x: 0, y: 0}
   m_SizeDelta: {x: 0, y: 0}
   m_Pivot: {x: 0.5, y: 0.5}
+--- !u!1 &7048938144688233294
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8072716410762509428}
+  - component: {fileID: 292131567826074232}
+  - component: {fileID: 5164905498187922994}
+  - component: {fileID: 7454652193996060800}
+  - component: {fileID: 4671111450719678205}
+  m_Layer: 5
+  m_Name: BtnRecordInfrared
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &8072716410762509428
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7048938144688233294}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children:
+  - {fileID: 8331608948875303033}
+  m_Father: {fileID: 7371505907208373617}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 1}
+  m_AnchorMax: {x: 0, y: 1}
+  m_AnchoredPosition: {x: 2194, y: -1254.2501}
+  m_SizeDelta: {x: 505.9, y: 137}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &292131567826074232
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7048938144688233294}
+  m_CullTransparentMesh: 1
+--- !u!114 &5164905498187922994
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7048938144688233294}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 1
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 21300000, guid: 3d8675433a508ec47b8f895201eacf20, type: 3}
+  m_Type: 1
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!114 &7454652193996060800
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7048938144688233294}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Navigation:
+    m_Mode: 3
+    m_WrapAround: 0
+    m_SelectOnUp: {fileID: 0}
+    m_SelectOnDown: {fileID: 0}
+    m_SelectOnLeft: {fileID: 0}
+    m_SelectOnRight: {fileID: 0}
+  m_Transition: 1
+  m_Colors:
+    m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+    m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+    m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+    m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+    m_ColorMultiplier: 1
+    m_FadeDuration: 0.1
+  m_SpriteState:
+    m_HighlightedSprite: {fileID: 0}
+    m_PressedSprite: {fileID: 0}
+    m_SelectedSprite: {fileID: 0}
+    m_DisabledSprite: {fileID: 0}
+  m_AnimationTriggers:
+    m_NormalTrigger: Normal
+    m_HighlightedTrigger: Highlighted
+    m_PressedTrigger: Pressed
+    m_SelectedTrigger: Selected
+    m_DisabledTrigger: Disabled
+  m_Interactable: 1
+  m_TargetGraphic: {fileID: 5164905498187922994}
+  m_OnClick:
+    m_PersistentCalls:
+      m_Calls:
+      - m_Target: {fileID: 0}
+        m_TargetAssemblyTypeName: InfraredScreenPositioningView, Assembly-CSharp
+        m_MethodName: 
+        m_Mode: 1
+        m_Arguments:
+          m_ObjectArgument: {fileID: 0}
+          m_ObjectArgumentAssemblyTypeName: UnityEngine.Object, UnityEngine
+          m_IntArgument: 0
+          m_FloatArgument: 0
+          m_StringArgument: 
+          m_BoolArgument: 0
+        m_CallState: 2
+--- !u!114 &4671111450719678205
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7048938144688233294}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 72b3331a3d48a0c4a9dbfb8e41495fc7, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  screenPositioningView: {fileID: 1598515592738749058}
+  infraredSigns:
+  - {fileID: 3355336842237625109}
+  - {fileID: 7990110363075381605}
+  - {fileID: 326007627452055294}
+  - {fileID: 6336526226875887966}
+  btnText: {fileID: 2407834152080892515}
 --- !u!1 &7092090652484598119
 GameObject:
   m_ObjectHideFlags: 0
@@ -3999,8 +4385,9 @@ RectTransform:
   - {fileID: 8816989788969488603}
   - {fileID: 2480474177723386199}
   - {fileID: 2371513326884009388}
+  - {fileID: 8072716410762509428}
   m_Father: {fileID: 518485857765021418}
-  m_RootOrder: 1
+  m_RootOrder: 2
   m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
   m_AnchorMin: {x: 0, y: 0}
   m_AnchorMax: {x: 1, y: 1}

+ 8 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 54c88acf42b8bda45baae70d0ba0d6bc
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 43 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/BtnRecordInfrared.cs

@@ -0,0 +1,43 @@
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.UI;
+using ZIM.Unity;
+
+public class BtnRecordInfrared : MonoBehaviour
+{
+    [SerializeField]
+    InfraredScreenPositioningView screenPositioningView;
+    [SerializeField]
+    List<RectTransform> infraredSigns;
+    [SerializeField]
+    Text btnText;
+
+    string[] btnHint = new string[4] { "记录红外点(1)", "记录红外点(2)", "记录红外点(3)", "记录红外点(4)" };
+    int operateIndex = 0;
+
+    void Start()
+    {
+        GetComponent<Button>().onClick.AddListener(OnClick_RecordInfrared);
+    }
+
+    // 记录红外灯位置,作为屏幕四个点
+    public void OnClick_RecordInfrared()
+    {
+        var location = ScreenLocate.Main.infraredSpotBuffer.FirstOrDefault()?.CameraLocation;
+        if (location == null)
+        {
+            Debug.LogWarning("在识别区域内,没有找到红外亮点");
+        }
+        else
+        {
+            infraredSigns[operateIndex].localPosition = location.Value.pixelToLocalPosition_AnchorCenter(ScreenLocate.Main.CameraSize, screenPositioningView.Bg.rectTransform.rect);
+            screenPositioningView.SetLinePos();
+
+            operateIndex = (operateIndex + 1) % 4;
+            btnText.text = btnHint[operateIndex];
+        }
+    }
+
+}

+ 11 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/BtnRecordInfrared.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 72b3331a3d48a0c4a9dbfb8e41495fc7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 64 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.cs

@@ -0,0 +1,64 @@
+using System.Collections;
+using System.Collections.Generic;
+using Unity.VisualScripting;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class PixelCheaker : MonoBehaviour
+{
+    public Image ColliderImage;
+    public Collider2D Collider2D;
+
+    private Vector2 _imageSize;
+    public Vector2 ImageSize
+    {
+        get => _imageSize;
+        set
+        {
+            if (_imageSize != value)
+            {
+                _imageSize = value;
+                cacheData();
+            }
+        }
+    }
+
+    HashSet<Vector2> PixelsOutCollider2D;       // 存一个外部像素的hashset, 外部像素比较少
+
+    public bool OutCollider2D(Vector2 p) => PixelsOutCollider2D.Contains(p);
+    public bool InCollider2D(Vector2 p) => !PixelsOutCollider2D.Contains(p);
+
+    public void ShowImage() => ColliderImage.enabled = true;
+    public void HideImage() => ColliderImage.enabled = false;
+
+    private void cacheData()
+    {
+        var canvas = FindAnyObjectByType<Canvas>().GetComponent<RectTransform>();
+        PixelsOutCollider2D = new HashSet<Vector2>();
+        for (int i = 0; i < ImageSize.x; i++)
+        {
+            for (int j = 0; j < ImageSize.y; j++)
+            {
+                var p = new Vector2(i, j);
+                if (!OverlapPoint(p, canvas))
+                    PixelsOutCollider2D.Add(p);
+            }
+        }
+    }
+
+    /// <summary>
+    /// 像素点是否在Collider2D的区域内,输入像素点坐标
+    /// </summary>
+    private bool OverlapPoint(Vector2 p, RectTransform canvas)
+    {
+        var u = p.x / ImageSize.x;
+        var v = p.y / ImageSize.y;
+        Vector3[] corners = new Vector3[4];
+        canvas.GetWorldCorners(corners);
+        float width = Vector3.Distance(corners[0], corners[3]);
+        float height = Vector3.Distance(corners[0], corners[1]);
+        var point = new Vector2(u * width, v * height);
+        return Collider2D.OverlapPoint(point);
+    }
+
+}

+ 11 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.cs.meta

@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 85aeb100c8367e948b420ca29c1cfd66
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 110 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.prefab

@@ -0,0 +1,110 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2705055563425357306
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2705055563425357307}
+  - component: {fileID: 2705055563425357310}
+  - component: {fileID: 2705055563425357305}
+  - component: {fileID: 2705055563425357308}
+  - component: {fileID: 2705055563425357311}
+  m_Layer: 5
+  m_Name: PixelCheaker
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!224 &2705055563425357307
+RectTransform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2705055563425357306}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 2.24, y: 1.64, z: 1}
+  m_ConstrainProportionsScale: 0
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 1920, y: 1080}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &2705055563425357310
+CanvasRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2705055563425357306}
+  m_CullTransparentMesh: 1
+--- !u!114 &2705055563425357305
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2705055563425357306}
+  m_Enabled: 0
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_RaycastTarget: 0
+  m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
+  m_Maskable: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  m_Sprite: {fileID: 21300000, guid: f3b60078f3450d1459d9642ca6404c7d, type: 3}
+  m_Type: 0
+  m_PreserveAspect: 0
+  m_FillCenter: 1
+  m_FillMethod: 4
+  m_FillAmount: 1
+  m_FillClockwise: 1
+  m_FillOrigin: 0
+  m_UseSpriteMesh: 0
+  m_PixelsPerUnitMultiplier: 1
+--- !u!70 &2705055563425357308
+CapsuleCollider2D:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2705055563425357306}
+  m_Enabled: 1
+  m_Density: 1
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_UsedByEffector: 0
+  m_UsedByComposite: 0
+  m_Offset: {x: 0, y: 0}
+  m_Size: {x: 500, y: 500}
+  m_Direction: 1
+--- !u!114 &2705055563425357311
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2705055563425357306}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 85aeb100c8367e948b420ca29c1cfd66, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  ColliderImage: {fileID: 2705055563425357305}
+  Collider2D: {fileID: 2705055563425357308}

+ 7 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/PixelCheaker.prefab.meta

@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: ab30c51d9dbef7d4db660e2ae2075902
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

二進制
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/screen_area.png


+ 123 - 0
Assets/SmartBow/Resources/SmartBow/Prefabs/ZIM/screen_area.png.meta

@@ -0,0 +1,123 @@
+fileFormatVersion: 2
+guid: f3b60078f3450d1459d9642ca6404c7d
+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: 0
+  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: 

+ 24 - 3
Assets/SmartBow/Scripts/Views/InfraredViewParts/InfraredScreenPositioningView.cs

@@ -8,6 +8,8 @@ using ZIM;
 using o0;
 using Color = UnityEngine.Color;
 using InfraredManager;
+using Org.BouncyCastle.Asn1.Crmf;
+using ZIM.Unity;
 
 public class LinePosition
 {
@@ -46,6 +48,8 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
 
     [SerializeField]
     RawImage rawImage;
+    [SerializeField]
+    RectTransform crosshair;
 
     [SerializeField]
     GameObject textTip1;
@@ -175,20 +179,35 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
         //InfraredDemo._ins?.SetResolutionNew(DefaultResolutionIndex);
     }
 
+    public RawImage Bg => rawImage;
+
     void Update()
     {
-        if (InfraredDemo.running)
+        if (enterFromZimWebCamera && ScreenLocate.Main.DebugOnEditorWin) {
+            // ZimWebCamera场景测试
+            rawImage.texture = ScreenLocate.Main.outputRawImages[6].texture;
+            if (ScreenLocate.Main.infraredSpotBuffer[0].CameraLocation.HasValue)
+            {
+                // 检测到光点
+                var posInCanvas = ScreenLocate.Main.infraredSpotBuffer[0].CameraLocation.Value.pixelToLocalPosition_AnchorCenter(ScreenLocate.Main.CameraSize, rawImage.rectTransform.rect);
+                crosshair.gameObject.SetActive(true);
+                crosshair.anchoredPosition = posInCanvas;
+            }
+            else
+                crosshair.gameObject.SetActive(false);
+        }
+
+        else if (InfraredDemo.running)
         {
             //渲染相机画面
             Texture texture = InfraredDemo.infraredCameraHelper.GetCameraTexture();
-
             if (rawImage.texture == null || texture.GetNativeTexturePtr() != rawImage.texture.GetNativeTexturePtr())
                 rawImage.texture = texture;
+
             //rawImage.material = InfraredDemo.infraredCameraHelper.GetCameraMaterial();
         }
     }
 
-
     public void OnClick_Back()
     {
         AudioMgr.ins.PlayBtn();
@@ -269,6 +288,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     //点击拖拽的开始位置
     public void onBeginPos(int index, Vector3 pos)
     {
+        Debug.Log("pos begin: " + pos);
         beginPos = pos;
     }
     public void onDragPos(int index, Vector3 pos)
@@ -279,6 +299,7 @@ public class InfraredScreenPositioningView : JCUnityLib.ViewBase
     //点击拖拽的结束位置
     public void onEndPos(int index, Vector3 pos)
     {
+        Debug.Log("pos end: " + pos);
         endPos = pos;
         if (beginPos == endPos) return;
 

+ 9 - 9
Packages/packages-lock.json

@@ -11,14 +11,14 @@
       "depth": 0,
       "source": "registry",
       "dependencies": {},
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.ext.nunit": {
       "version": "1.0.6",
       "depth": 1,
       "source": "registry",
       "dependencies": {},
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.ide.rider": {
       "version": "3.0.18",
@@ -27,7 +27,7 @@
       "dependencies": {
         "com.unity.ext.nunit": "1.0.6"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.ide.visualstudio": {
       "version": "2.0.17",
@@ -36,14 +36,14 @@
       "dependencies": {
         "com.unity.test-framework": "1.1.9"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.ide.vscode": {
       "version": "1.2.5",
       "depth": 0,
       "source": "registry",
       "dependencies": {},
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.test-framework": {
       "version": "1.1.31",
@@ -54,7 +54,7 @@
         "com.unity.modules.imgui": "1.0.0",
         "com.unity.modules.jsonserialize": "1.0.0"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.textmeshpro": {
       "version": "3.0.6",
@@ -63,7 +63,7 @@
       "dependencies": {
         "com.unity.ugui": "1.0.0"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.timeline": {
       "version": "1.6.4",
@@ -75,7 +75,7 @@
         "com.unity.modules.audio": "1.0.0",
         "com.unity.modules.particlesystem": "1.0.0"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.ugui": {
       "version": "1.0.0",
@@ -94,7 +94,7 @@
         "com.unity.ugui": "1.0.0",
         "com.unity.modules.jsonserialize": "1.0.0"
       },
-      "url": "https://packages.unity.com"
+      "url": "https://packages.unity.cn"
     },
     "com.unity.modules.ai": {
       "version": "1.0.0",

+ 1 - 1
ProjectSettings/PackageManagerSettings.asset

@@ -21,7 +21,7 @@ MonoBehaviour:
   m_Registries:
   - m_Id: main
     m_Name: 
-    m_Url: https://packages.unity.com
+    m_Url: https://packages.unity.cn
     m_Scopes: []
     m_IsDefault: 1
     m_Capabilities: 7

+ 5 - 0
ProjectSettings/ProjectSettings.asset

@@ -87,6 +87,11 @@ PlayerSettings:
   hideHomeButton: 0
   submitAnalytics: 1
   usePlayerLog: 1
+  autoStreaming: 0
+  useAnimationStreaming: 0
+  useFontStreaming: 0
+  autoStreamingId: 
+  instantGameAppId: 
   bakeCollisionMeshes: 0
   forceSingleInstance: 0
   useFlipModelSwapchain: 1

+ 2 - 2
ProjectSettings/ProjectVersion.txt

@@ -1,2 +1,2 @@
-m_EditorVersion: 2021.3.42f1
-m_EditorVersionWithRevision: 2021.3.42f1 (f1197811e8ce)
+m_EditorVersion: 2021.3.42f1c1
+m_EditorVersionWithRevision: 2021.3.42f1c1 (3d7b7240bc24)