MvCamera.cs 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. using Cognex.VisionPro;
  2. using LampInspectionMachine.Interfaces;
  3. using LampInspectionMachine.Log4xml;
  4. using LampInspectionMachine.Model;
  5. using Microsoft.Win32;
  6. using MvCamCtrl.NET;
  7. using MvCameraControl;
  8. using SqlSugar.DistributedSystem.Snowflake;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Collections.ObjectModel;
  12. using System.ComponentModel;
  13. using System.Data.SqlTypes;
  14. using System.Diagnostics;
  15. using System.Drawing.Imaging;
  16. using System.Drawing;
  17. using System.Linq;
  18. using System.Runtime.CompilerServices;
  19. using System.Runtime.InteropServices;
  20. using System.Text;
  21. using System.Threading;
  22. using System.Threading.Tasks;
  23. using System.Windows.Media.Media3D;
  24. namespace LampInspectionMachine.Cameralibs.HKCamera
  25. {
  26. public class MvCamera : ICamera, INotifyPropertyChanged
  27. {
  28. #region 字段
  29. private IDevice m_MyCamera = null;
  30. private Thread m_hReceiveThread;
  31. #endregion
  32. #region 属性
  33. public CameraBrand CameraBrand { get => CameraBrand.HikRobot_MVS; }
  34. public string Name { get; private set; }
  35. public Guid ID { get; private set; }
  36. public string ManufacturerName { get; private set; }
  37. public string ModelName { get; private set; }
  38. public string SerialNumber { get; private set; }
  39. public CameraType CameraType { get; private set; }
  40. public IDeviceInfo CameraInfo { get; private set; }
  41. public UInt32 ImageWidth { get; private set; }
  42. public UInt32 ImageHeight { get; private set; }
  43. public MvGvspPixelType PixelType { get; private set; }
  44. private bool _IsGrabbing;
  45. /// <summary>
  46. /// 正在采集
  47. /// </summary>
  48. public bool IsGrabbing
  49. {
  50. get { return _IsGrabbing; }
  51. private set { SetProperty(ref _IsGrabbing, value); }
  52. }
  53. private ICogImage _Image;
  54. public ICogImage Image
  55. {
  56. get { return _Image; }
  57. private set { SetProperty(ref _Image, value); }
  58. }
  59. public bool IsConnected { get; private set; }
  60. private bool IsHaveCamera = false;
  61. /// <summary>
  62. /// 采集用时
  63. /// </summary>
  64. public TimeSpan TotalTime { get; private set; }
  65. /// <summary>
  66. /// 错误信息
  67. /// </summary>
  68. public string ErrorMessage { get; private set; }
  69. #endregion
  70. #region 事件
  71. /// <summary>
  72. /// 手动采集图像回调事件
  73. /// </summary>
  74. public event Action<ICogImage, TimeSpan, string> ImageCallbackEvent;
  75. /// <summary>
  76. /// 触发取图回调事件
  77. /// </summary>
  78. public event Action<ICogImage> GrabImageCallbackEvent;
  79. public event Action<Guid, bool> CameraConnectChangedEvent;
  80. #endregion
  81. public MvCamera(Guid _ID, string _Name, string _SerialNumber)
  82. {
  83. ID = _ID;
  84. Name = _Name;
  85. SerialNumber = _SerialNumber;
  86. // ch: 初始化 SDK | en: Initialize SDK
  87. SDKSystem.Initialize();
  88. List<IDeviceInfo> devInfoList = new List<IDeviceInfo>();
  89. // ch:枚举设备 | en:Enum device
  90. int nRet = DeviceEnumerator.EnumDevices(DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvVirGigEDevice | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvVirUsbDevice, out devInfoList);
  91. if (nRet != MvError.MV_OK)
  92. {
  93. throw new Exception($"Enumerate devices fail: {nRet:x8}");
  94. }
  95. foreach (var devInfo in devInfoList)
  96. {
  97. if (devInfo.TLayerType == DeviceTLayerType.MvGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvVirGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvGenTLGigEDevice)
  98. {
  99. IGigEDeviceInfo gigeDevInfo = devInfo as IGigEDeviceInfo;
  100. uint nIp1 = ((gigeDevInfo.CurrentIp & 0xff000000) >> 24);
  101. uint nIp2 = ((gigeDevInfo.CurrentIp & 0x00ff0000) >> 16);
  102. uint nIp3 = ((gigeDevInfo.CurrentIp & 0x0000ff00) >> 8);
  103. uint nIp4 = (gigeDevInfo.CurrentIp & 0x000000ff);
  104. Console.WriteLine("DevIP: {0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
  105. if (string.Equals(devInfo.SerialNumber, _SerialNumber))
  106. {
  107. ManufacturerName = devInfo.ManufacturerName;
  108. ModelName = devInfo.ModelName;
  109. SerialNumber = devInfo.SerialNumber;
  110. CameraType = CameraType.GIGE;
  111. CameraInfo = devInfo;
  112. IsHaveCamera = true;
  113. break;
  114. }
  115. }
  116. else if (devInfo.TLayerType == DeviceTLayerType.MvUsbDevice || devInfo.TLayerType == DeviceTLayerType.MvVirUsbDevice)
  117. {
  118. if (string.Equals(devInfo.SerialNumber, _SerialNumber))
  119. {
  120. ManufacturerName = devInfo.ManufacturerName;
  121. ModelName = devInfo.ModelName;
  122. SerialNumber = devInfo.SerialNumber;
  123. CameraType = CameraType.USB;
  124. CameraInfo = devInfo;
  125. IsHaveCamera = true;
  126. break;
  127. }
  128. }
  129. }
  130. IsConnected = false;
  131. }
  132. #region 方法
  133. /// <summary>
  134. /// 获取设备
  135. /// </summary>
  136. /// <returns></returns>
  137. public static CameraInfo[] GetDevices()
  138. {
  139. List<CameraInfo> cameras = new List<CameraInfo>();
  140. try
  141. {
  142. if (!CheckSoftwareInstalled().isInstalled)
  143. {
  144. //LogHelper.WriteLogInfo("未安装MVS");
  145. return cameras.ToArray();
  146. }
  147. // ch: 初始化 SDK | en: Initialize SDK
  148. SDKSystem.Initialize();
  149. List<IDeviceInfo> devInfoList = new List<IDeviceInfo>();
  150. // ch:枚举设备 | en:Enum device
  151. int nRet = DeviceEnumerator.EnumDevices(DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvVirGigEDevice | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvVirUsbDevice, out devInfoList);
  152. if (nRet != MvError.MV_OK)
  153. {
  154. throw new Exception($"Enumerate devices fail: {nRet:x8}");
  155. }
  156. foreach (var devInfo in devInfoList)
  157. {
  158. if (devInfo.TLayerType == DeviceTLayerType.MvGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvVirGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvGenTLGigEDevice)
  159. {
  160. IGigEDeviceInfo gigeDevInfo = devInfo as IGigEDeviceInfo;
  161. uint nIp1 = ((gigeDevInfo.CurrentIp & 0xff000000) >> 24);
  162. uint nIp2 = ((gigeDevInfo.CurrentIp & 0x00ff0000) >> 16);
  163. uint nIp3 = ((gigeDevInfo.CurrentIp & 0x0000ff00) >> 8);
  164. uint nIp4 = (gigeDevInfo.CurrentIp & 0x000000ff);
  165. Console.WriteLine("DevIP: {0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
  166. cameras.Add(new CameraInfo()
  167. {
  168. CameraName = "",
  169. CameraBrand = CameraBrand.HikRobot_MVS,
  170. CameraType = CameraType.GIGE,
  171. Id = Guid.NewGuid(),
  172. ManufacturerName = devInfo.ManufacturerName,
  173. Model = devInfo.ModelName,
  174. SerialNumber = devInfo.SerialNumber,
  175. CameraIp = $"{nIp1}.{nIp2}.{nIp3}.{nIp4}",
  176. });
  177. }
  178. else if (devInfo.TLayerType == DeviceTLayerType.MvUsbDevice || devInfo.TLayerType == DeviceTLayerType.MvVirUsbDevice)
  179. {
  180. cameras.Add(new CameraInfo()
  181. {
  182. CameraName = "",
  183. CameraBrand = CameraBrand.HikRobot_MVS,
  184. CameraType = CameraType.USB,
  185. Id = Guid.NewGuid(),
  186. ManufacturerName = devInfo.ManufacturerName,
  187. Model = devInfo.ModelName,
  188. SerialNumber = devInfo.SerialNumber,
  189. CameraIp = "",
  190. });
  191. }
  192. }
  193. }
  194. catch (Exception ex)
  195. {
  196. // LogHelper.WriteLogError("搜索海康相机列表时出错!", ex);
  197. }
  198. return cameras.ToArray();
  199. }
  200. /// <summary>
  201. /// 检查相机软件是否安装
  202. /// </summary>
  203. /// <returns></returns>
  204. public static (bool isInstalled, string version) CheckSoftwareInstalled()
  205. {
  206. string softwareName = "MVS";
  207. string softwareVersion = "4.5.0";
  208. string[] registryPaths = new[]
  209. {
  210. @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
  211. @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
  212. };
  213. foreach (var path in registryPaths)
  214. {
  215. using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
  216. {
  217. if (key == null) continue;
  218. foreach (string subKeyName in key.GetSubKeyNames())
  219. {
  220. using (RegistryKey subKey = key.OpenSubKey(subKeyName))
  221. {
  222. string displayName = subKey.GetValue("DisplayName")?.ToString();
  223. if (displayName != null && displayName.Contains(softwareName))
  224. {
  225. string version = subKey.GetValue("DisplayVersion")?.ToString();
  226. Version v1 = new Version(version);
  227. Version v2 = new Version(softwareVersion);
  228. if (v1 >= v2)
  229. {
  230. return (true, version);
  231. }
  232. else
  233. {
  234. return (false, version);
  235. }
  236. }
  237. }
  238. }
  239. }
  240. }
  241. return (false, "");
  242. }
  243. /// <summary>
  244. /// 打开相机
  245. /// </summary>
  246. /// <returns></returns>
  247. /// <exception cref="Exception"></exception>
  248. public bool OpenDevice()
  249. {
  250. if (!IsHaveCamera)
  251. {
  252. List<IDeviceInfo> devInfoList = new List<IDeviceInfo>();
  253. // ch:枚举设备 | en:Enum device
  254. int nRet = DeviceEnumerator.EnumDevices(DeviceTLayerType.MvGigEDevice | DeviceTLayerType.MvVirGigEDevice | DeviceTLayerType.MvGenTLGigEDevice | DeviceTLayerType.MvUsbDevice | DeviceTLayerType.MvVirUsbDevice, out devInfoList);
  255. if (nRet != MvError.MV_OK)
  256. {
  257. throw new Exception($"Enumerate devices fail: {nRet:x8}");
  258. }
  259. foreach (var devInfo in devInfoList)
  260. {
  261. if (devInfo.TLayerType == DeviceTLayerType.MvGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvVirGigEDevice || devInfo.TLayerType == DeviceTLayerType.MvGenTLGigEDevice)
  262. {
  263. IGigEDeviceInfo gigeDevInfo = devInfo as IGigEDeviceInfo;
  264. uint nIp1 = ((gigeDevInfo.CurrentIp & 0xff000000) >> 24);
  265. uint nIp2 = ((gigeDevInfo.CurrentIp & 0x00ff0000) >> 16);
  266. uint nIp3 = ((gigeDevInfo.CurrentIp & 0x0000ff00) >> 8);
  267. uint nIp4 = (gigeDevInfo.CurrentIp & 0x000000ff);
  268. Console.WriteLine("DevIP: {0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
  269. if (string.Equals(devInfo.SerialNumber, SerialNumber))
  270. {
  271. ManufacturerName = devInfo.ManufacturerName;
  272. ModelName = devInfo.ModelName;
  273. SerialNumber = devInfo.SerialNumber;
  274. CameraType = CameraType.GIGE;
  275. CameraInfo = devInfo;
  276. IsHaveCamera = true;
  277. break;
  278. }
  279. }
  280. else if (devInfo.TLayerType == DeviceTLayerType.MvUsbDevice || devInfo.TLayerType == DeviceTLayerType.MvVirUsbDevice)
  281. {
  282. if (string.Equals(devInfo.SerialNumber, SerialNumber))
  283. {
  284. ManufacturerName = devInfo.ManufacturerName;
  285. ModelName = devInfo.ModelName;
  286. SerialNumber = devInfo.SerialNumber;
  287. CameraType = CameraType.USB;
  288. CameraInfo = devInfo;
  289. IsHaveCamera = true;
  290. break;
  291. }
  292. }
  293. }
  294. if (!IsHaveCamera)
  295. {
  296. throw new Exception("没有发现相机");
  297. }
  298. }
  299. if (m_MyCamera == null)
  300. {
  301. // ch:创建设备 | en:Create device
  302. try
  303. {
  304. m_MyCamera = DeviceFactory.CreateDevice(CameraInfo);
  305. }
  306. catch (Exception)
  307. {
  308. return false;
  309. }
  310. //m_MyCamera.RegisterExceptionCallBack(pCallBackFunc, IntPtr.Zero);
  311. }
  312. int nRet1 = m_MyCamera.Open(DeviceAccessMode.AccessControl, 0);
  313. if (nRet1 != MvError.MV_OK)
  314. {
  315. m_MyCamera.Dispose();
  316. return false;
  317. }
  318. CameraConnectChangedEvent?.Invoke(ID, true);
  319. // ch:探测网络最佳包大小(只对GigE相机有效) | en:Detection network optimal package size(It only works for the GigE camera)
  320. if (CameraType == CameraType.GIGE)
  321. {
  322. int packetSize;
  323. int ret = (m_MyCamera as IGigEDevice).GetOptimalPacketSize(out packetSize);
  324. if (packetSize > 0)
  325. {
  326. ret = m_MyCamera.Parameters.SetIntValue("GevSCPSPacketSize", packetSize);
  327. if (ret != MvError.MV_OK)
  328. {
  329. Console.WriteLine("Warning: Set Packet Size failed {0:x8}", ret);
  330. }
  331. else
  332. {
  333. Console.WriteLine("Set PacketSize to {0}", packetSize);
  334. }
  335. }
  336. else
  337. {
  338. Console.WriteLine("Warning: Get Packet Size failed {0:x8}", ret);
  339. }
  340. }
  341. //连续采集
  342. m_MyCamera.Parameters.SetEnumValueByString("TriggerMode", "Off");
  343. // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
  344. // 1 - Line1;
  345. // 2 - Line2;
  346. // 3 - Line3;
  347. // 4 - Counter;
  348. // 7 - Software;
  349. // m_MyCamera.Parameters.SetEnumValueByString("TriggerSource", "Software");
  350. m_MyCamera.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");
  351. //m_MyCamera.Parameters.SetCommandValue("TriggerSoftware");
  352. // ch:注册回调函数 | en:Register image callback
  353. // m_MyCamera.StreamGrabber.FrameGrabedEvent += FrameGrabedEventHandler;
  354. IsConnected = m_MyCamera.IsConnected;
  355. return IsConnected;
  356. }
  357. /// <summary>
  358. /// 关闭相机
  359. /// </summary>
  360. public void CloseDevice()
  361. {
  362. m_MyCamera.StreamGrabber.FrameGrabedEvent -= FrameGrabedEventHandler;
  363. // ch:关闭设备 | en:Close Device
  364. m_MyCamera.Close();
  365. m_MyCamera.Dispose();
  366. m_MyCamera = null;
  367. if (IsConnected)
  368. {
  369. IsConnected = false;
  370. CameraConnectChangedEvent?.Invoke(ID, IsConnected);
  371. }
  372. }
  373. /// <summary>
  374. /// 取图前的必要操作步骤
  375. /// </summary>
  376. /// <returns></returns>
  377. private Int32 NecessaryOperBeforeGrab()
  378. {
  379. // ch:取图像宽 | en:Get Iamge Width
  380. IIntValue pcWidth = null;
  381. int nRet = m_MyCamera.Parameters.GetIntValue("Width", out pcWidth);
  382. if (nRet != MvError.MV_OK)
  383. {
  384. return nRet;
  385. }
  386. ImageWidth = (UInt32)pcWidth.CurValue;
  387. // ch:取图像高 | en:Get Iamge Height
  388. IIntValue pcHeight = null;
  389. nRet = m_MyCamera.Parameters.GetIntValue("Height", out pcHeight);
  390. if (nRet != MvError.MV_OK)
  391. {
  392. return nRet;
  393. }
  394. ImageHeight = (UInt32)pcHeight.CurValue;
  395. // ch:取像素格式 | en:Get Pixel Format
  396. IEnumValue pcPixelFormat = null;
  397. nRet = m_MyCamera.Parameters.GetEnumValue("PixelFormat", out pcPixelFormat);
  398. if (nRet != MvError.MV_OK)
  399. {
  400. return nRet;
  401. }
  402. PixelType = (MvCameraControl.MvGvspPixelType)pcPixelFormat.CurEnumEntry.Value;
  403. return MvError.MV_OK;
  404. }
  405. /// <summary>
  406. /// 采集图像
  407. /// </summary>
  408. /// <returns></returns>
  409. public ICogImage Grab()
  410. {
  411. m_MyCamera.StreamGrabber.StartGrabbing();
  412. m_MyCamera.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");
  413. m_MyCamera.Parameters.SetEnumValueByString("TriggerMode", "Off");
  414. m_MyCamera.Parameters.SetFloatValue("ExposureTime", 5000); // 单位μs
  415. m_MyCamera.Parameters.SetIntValue("GevSCPSPacketSize", 8164);
  416. m_MyCamera.Parameters.SetIntValue("GevSCPD", 12000); // 缓冲区大小
  417. m_MyCamera.Parameters.SetEnumValueByString("PixelFormat", "RGB8");
  418. IFrameOut frameOut = null;
  419. bool Succeed = false;
  420. int nRet = -1;
  421. Stopwatch sw = Stopwatch.StartNew();
  422. for (int i = 0; i < 5; i++)
  423. {
  424. try
  425. {
  426. if (m_MyCamera == null || !m_MyCamera.IsConnected)
  427. {
  428. OpenDevice();
  429. }
  430. ErrorMessage = "";
  431. // ch:前置配置 | en:pre-operation
  432. nRet = NecessaryOperBeforeGrab();
  433. if (nRet != MvError.MV_OK)
  434. {
  435. CloseDevice();
  436. OpenDevice();
  437. ErrorMessage = $"{nRet: x8}";
  438. continue;
  439. }
  440. // ch:开始采集 | en:Start Grabbing
  441. nRet = m_MyCamera.StreamGrabber.StartGrabbing();
  442. if (nRet != MvError.MV_OK)
  443. {
  444. CloseDevice();
  445. OpenDevice();
  446. ErrorMessage = $"{nRet: x8}";
  447. continue;
  448. }
  449. nRet = m_MyCamera.StreamGrabber.GetImageBuffer(20000, out frameOut);
  450. m_MyCamera.StreamGrabber.StopGrabbing();
  451. if (nRet != MvError.MV_OK)
  452. {
  453. CloseDevice();
  454. OpenDevice();
  455. ErrorMessage = $"{nRet: x8}";
  456. continue;
  457. }
  458. m_MyCamera.StreamGrabber.FreeImageBuffer(frameOut);
  459. Succeed = true;
  460. break;
  461. }
  462. catch (Exception ex)
  463. {
  464. CloseDevice();
  465. OpenDevice();
  466. ErrorMessage = ex.Message;
  467. }
  468. }
  469. if (!Succeed)
  470. {
  471. ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
  472. return null;
  473. }
  474. if( frameOut !=null)
  475. Image = AnalyticImage(frameOut);
  476. TotalTime = sw.Elapsed;
  477. ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
  478. return Image;
  479. }
  480. /// <summary>
  481. /// 开始采集图像
  482. /// </summary>
  483. public void StartGrabbing()
  484. {
  485. if (IsGrabbing)
  486. return;
  487. IsGrabbing = true;
  488. m_hReceiveThread = new Thread(GetStreamThreadProc) { IsBackground = true };
  489. m_hReceiveThread.Start();
  490. }
  491. /// <summary>
  492. /// 停止采集图像
  493. /// </summary>
  494. public void StopGrabbing()
  495. {
  496. try
  497. {
  498. if ( IsGrabbing )
  499. {
  500. IsGrabbing = false;
  501. Thread.Sleep(1000);
  502. }
  503. if (m_hReceiveThread != null)
  504. {
  505. m_hReceiveThread.Abort();
  506. m_hReceiveThread = null;
  507. }
  508. }
  509. catch (Exception)
  510. {
  511. }
  512. }
  513. private void FrameGrabedEventHandler(object sender, FrameGrabbedEventArgs e)
  514. {
  515. GrabImageCallbackEvent?.Invoke(AnalyticImage(e.FrameOut));
  516. //Console.WriteLine("Get one frame: Width[{0}] , Height[{1}] , ImageSize[{2}], FrameNum[{3}]", e.FrameOut.Image.Width, e.FrameOut.Image.Height, e.FrameOut.Image.ImageSize, e.FrameOut.FrameNum);
  517. }
  518. /// <summary>
  519. /// Converts the image data from the specified <see cref="IFrameOut"/> object into an <see cref="ICogImage"/>
  520. /// format.
  521. /// </summary>
  522. /// <remarks>This method processes both color and monochrome images, converting them to a
  523. /// compatible format for further analysis. Unsupported pixel formats are not processed, and the method will
  524. /// return <see langword="null"/> in such cases. The caller is responsible for ensuring that the <paramref
  525. /// name="frameOut"/> parameter is valid and properly initialized.</remarks>
  526. /// <param name="frameOut">The frame output containing the image data to be analyzed and converted.</param>
  527. /// <returns>An <see cref="ICogImage"/> object representing the converted image. Returns <see langword="null"/> if the
  528. /// image format is unsupported or if an error occurs during conversion.</returns>
  529. private ICogImage AnalyticImage(IFrameOut frameOut)
  530. {
  531. ICogImage image = null;
  532. int nRet = -1;
  533. //IntPtr pTemp = IntPtr.Zero;
  534. IImage pImage = frameOut.Image;
  535. MvCameraControl.MvGvspPixelType pixelType = frameOut.Image.PixelType;
  536. if (IsColorPixelFormat(frameOut.Image.PixelType)) // 彩色图像处理
  537. {
  538. if (frameOut.Image.PixelType == MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed)
  539. {
  540. //pTemp = frameOut.Image.PixelDataPtr;
  541. }
  542. else
  543. {
  544. nRet = m_MyCamera.PixelTypeConverter.ConvertPixelType(frameOut.Image, out pImage, MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed);
  545. if (nRet != MvError.MV_OK)
  546. {
  547. frameOut.Dispose();
  548. return null;
  549. }
  550. pixelType = MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed;
  551. }
  552. }
  553. else if (IsMonoPixelFormat(frameOut.Image.PixelType)) // Mono图像处理
  554. {
  555. if (frameOut.Image.PixelType == MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8)
  556. {
  557. //pTemp = frameOut.Image.PixelDataPtr;
  558. }
  559. else
  560. {
  561. // 其他格式Mono转为Mono8
  562. nRet = m_MyCamera.PixelTypeConverter.ConvertPixelType(frameOut.Image, out pImage, MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8);
  563. if (nRet != MvError.MV_OK)
  564. {
  565. frameOut.Dispose();
  566. return null;
  567. }
  568. }
  569. pixelType = MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8;
  570. }
  571. else
  572. {
  573. return null; // 不支持的图像格式
  574. }
  575. // 直接转换为CogImage,避免不必要的内存拷贝
  576. image = ConvertToICogImage(pImage.Width, pImage.Height,
  577. pImage.PixelDataPtr, pixelType);
  578. ////2.申请 byte[]
  579. //byte[] m_BufForDriver1 = new byte[pImage.ImageSize];
  580. ////3.海康相机取流 指针转 byte[]
  581. //Marshal.Copy(pImage.PixelDataPtr, m_BufForDriver1, 0, ((int)pImage.ImageSize));
  582. ////4.转换成 CogImage
  583. //image = ConvertToICogImage(pImage.Width, pImage.Height, pImage.PixelDataPtr, pixelType);
  584. pImage.Dispose();
  585. frameOut.Dispose();
  586. return image;
  587. }
  588. /// <summary>
  589. /// 设置曝光时间
  590. /// </summary>
  591. /// <param name="ExposureTime"></param>
  592. /// <returns></returns>
  593. public bool SetExposureTime(float ExposureTime)
  594. {
  595. int nRet = m_MyCamera.Parameters.SetFloatValue("ExposureTime", ExposureTime);
  596. if (nRet != MvError.MV_OK)
  597. {
  598. return false;
  599. }
  600. else
  601. {
  602. return true;
  603. }
  604. }
  605. /// <summary>
  606. /// 获取曝光时间
  607. /// </summary>
  608. /// <returns></returns>
  609. public float GetExposureTime()
  610. {
  611. IFloatValue pcFloatValue = null;
  612. int nRet = m_MyCamera.Parameters.GetFloatValue("ExposureTime", out pcFloatValue);
  613. if (nRet == MvError.MV_OK)
  614. {
  615. return pcFloatValue.CurValue;
  616. }
  617. else
  618. {
  619. return 0;
  620. }
  621. }
  622. /// <summary>
  623. /// 设置增益
  624. /// </summary>
  625. /// <param name="Gain"></param>
  626. /// <returns></returns>
  627. public bool SetGain(float Gain)
  628. {
  629. int nRet = m_MyCamera.Parameters.SetFloatValue("Gain", Gain);
  630. if (nRet != MvError.MV_OK)
  631. {
  632. return false;
  633. }
  634. else
  635. {
  636. return true;
  637. }
  638. }
  639. /// <summary>
  640. /// 获取增益
  641. /// </summary>
  642. /// <returns></returns>
  643. public float GetGain()
  644. {
  645. IFloatValue pcFloatValue = null;
  646. int nRet = m_MyCamera.Parameters.GetFloatValue("Gain", out pcFloatValue);
  647. if (nRet == MvError.MV_OK)
  648. {
  649. return pcFloatValue.CurValue;
  650. }
  651. else
  652. {
  653. return 0;
  654. }
  655. }
  656. /// <summary>
  657. /// ch:获取触发模式 | en:Get Trigger Mode
  658. /// </summary>
  659. /// <returns>On/Off</returns>
  660. public bool GetTriggerMode()
  661. {
  662. IEnumValue enumValue;
  663. int result = m_MyCamera.Parameters.GetEnumValue("TriggerMode", out enumValue);
  664. if (result == MvError.MV_OK)
  665. {
  666. if (enumValue.CurEnumEntry.Symbolic == "On")
  667. {
  668. return true;
  669. }
  670. return false;
  671. }
  672. return false;
  673. }
  674. /// <summary>
  675. /// 设置触发模式
  676. /// </summary>
  677. /// <param name="mode">触发模式</param>
  678. /// <param name="triggerSource">触发源0 - Line0;1 - Line1;2 - Line2;3 - Line3;4 - Counter;7 - Software;</param>
  679. /// <returns></returns>
  680. public bool SetTriggerMode(bool mode, int triggerSource)
  681. {
  682. string strmode = mode ? "On" : "Off";
  683. int nRet = m_MyCamera.Parameters.SetEnumValueByString("TriggerMode", strmode);
  684. if (nRet != MvError.MV_OK)
  685. {
  686. return false;
  687. }
  688. if (mode)
  689. {
  690. return SetTriggerSource(triggerSource);
  691. }
  692. else
  693. {
  694. nRet = m_MyCamera.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");
  695. if (nRet != MvError.MV_OK)
  696. {
  697. return false;
  698. }
  699. }
  700. return true;
  701. }
  702. /// <summary>
  703. /// Sets the trigger source for the camera.
  704. /// </summary>
  705. /// <param name="source">0 - Line0;1 - Line1;2 - Line2;3 - Line3;4 - Counter;7 - Software;</param>
  706. /// <returns></returns>
  707. /// <exception cref="ArgumentOutOfRangeException"></exception>
  708. public bool SetTriggerSource(int source)
  709. {
  710. // ch:触发源选择:0 - Line0; | en:Trigger source select:0 - Line0;
  711. // 1 - Line1;
  712. // 2 - Line2;
  713. // 3 - Line3;
  714. // 4 - Counter;
  715. // 7 - Software;
  716. string sourceStr;
  717. switch (source)
  718. {
  719. case 0: sourceStr = "Line0"; break;
  720. case 1: sourceStr = "Line1"; break;
  721. case 2: sourceStr = "Line2"; break;
  722. case 3: sourceStr = "Line3"; break;
  723. case 4: sourceStr = "Counter"; break;
  724. case 7: sourceStr = "Software"; break;
  725. default: throw new ArgumentOutOfRangeException(nameof(source), "Invalid trigger source value");
  726. }
  727. int nRet = m_MyCamera.Parameters.SetEnumValueByString("TriggerSource", sourceStr);
  728. if (nRet != MvError.MV_OK)
  729. {
  730. return false;
  731. }
  732. return true;
  733. }
  734. /// <summary>
  735. /// Retrieves the current trigger source setting of the camera.
  736. /// </summary>
  737. /// <remarks>This method queries the camera's parameters to determine the current trigger source.
  738. /// If the retrieval is unsuccessful or the trigger source is not recognized, the method returns -1.</remarks>
  739. /// <returns>An integer representing the trigger source: <list type="bullet"> <item><description>0 for
  740. /// "Line0".</description></item> <item><description>1 for "Line1".</description></item> <item><description>2
  741. /// for "Line2".</description></item> <item><description>3 for "Line3".</description></item>
  742. /// <item><description>4 for "Counter".</description></item> <item><description>7 for
  743. /// "Software".</description></item> <item><description>-1 if the trigger source is unknown or if the retrieval
  744. /// fails.</description></item> </list></returns>
  745. public int GetTriggerSource()
  746. {
  747. IEnumValue enumValue;
  748. int result = m_MyCamera.Parameters.GetEnumValue("TriggerSource", out enumValue);
  749. if (result == MvError.MV_OK)
  750. {
  751. switch (enumValue.CurEnumEntry.Symbolic)
  752. {
  753. case "Line0": return 0;
  754. case "Line1": return 1;
  755. case "Line2": return 2;
  756. case "Line3": return 3;
  757. case "Counter": return 4;
  758. case "Software": return 7;
  759. default: return -1; // 未知触发源
  760. }
  761. }
  762. return -1; // 获取失败
  763. }
  764. /// <summary>
  765. /// Sends a software trigger command to the camera.
  766. /// </summary>
  767. /// <remarks>This method triggers the camera to capture an image or perform an action based on
  768. /// its current configuration. Ensure the camera is properly initialized and configured to respond to software
  769. /// triggers before calling this method.</remarks>
  770. public void TriggerSoftware()
  771. {
  772. // ch:触发软件 | en:Trigger Software
  773. m_MyCamera.Parameters.SetCommandValue("TriggerSoftware");
  774. }
  775. private void GetStreamThreadProc()
  776. {
  777. m_MyCamera.StreamGrabber.StartGrabbing();
  778. m_MyCamera.Parameters.SetEnumValueByString("AcquisitionMode", "Continuous");
  779. m_MyCamera.Parameters.SetEnumValueByString("TriggerMode", "Off");
  780. m_MyCamera.Parameters.SetFloatValue("ExposureTime", 5000); // 单位μs
  781. m_MyCamera.Parameters.SetIntValue("GevSCPSPacketSize", 8164);
  782. m_MyCamera.Parameters.SetIntValue("GevSCPD", 12000); // 缓冲区大小
  783. m_MyCamera.Parameters.SetEnumValueByString("PixelFormat", "RGB8");
  784. while (IsGrabbing)
  785. {
  786. Stopwatch sw = Stopwatch.StartNew();
  787. try
  788. {
  789. IFrameOut frameOut = null;
  790. int nRet = m_MyCamera.StreamGrabber.GetImageBuffer(5000, out frameOut);
  791. double time1 = sw.Elapsed.TotalMilliseconds;
  792. Console.WriteLine($"获取图像:{time1}ms");
  793. if ( nRet == MvError.MV_OK )
  794. {
  795. Image = AnalyticImage(frameOut);
  796. TotalTime = sw.Elapsed;
  797. ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
  798. m_MyCamera.StreamGrabber.FreeImageBuffer(frameOut);
  799. }
  800. else
  801. {
  802. TotalTime = sw.Elapsed;
  803. Thread.Sleep(5);
  804. }
  805. }
  806. catch (Exception ex)
  807. {
  808. TotalTime = sw.Elapsed;
  809. ErrorMessage = ex.Message;
  810. ImageCallbackEvent?.Invoke(Image, TotalTime, ErrorMessage);
  811. continue;
  812. }
  813. }
  814. IsGrabbing = false;
  815. }
  816. /// <summary>
  817. /// Converts raw image data into an <see cref="ICogImage"/> object, supporting both monochrome and color pixel
  818. /// formats.
  819. /// </summary>
  820. /// <remarks>This method supports both monochrome (PixelType_Gvsp_Mono8) and color pixel formats.
  821. /// For color images, the method processes the image data as a planar color format.</remarks>
  822. /// <param name="nHeight">The height of the image in pixels.</param>
  823. /// <param name="nWidth">The width of the image in pixels.</param>
  824. /// <param name="pImageBuf">A pointer to the buffer containing the raw image data.</param>
  825. /// <param name="enPixelType">The pixel format of the image, specified as a <see cref="MvCameraControl.MvGvspPixelType"/> value.</param>
  826. /// <returns>An <see cref="ICogImage"/> object representing the converted image. Returns <see langword="null"/> if the
  827. /// conversion fails.</returns>
  828. private ICogImage ConvertToICogImage(UInt32 nHeight, UInt32 nWidth, IntPtr pImageBuf, MvCameraControl.MvGvspPixelType enPixelType)
  829. {
  830. ICogImage cogImage = null;
  831. // ch:获取步长 || en: Get nRowStep
  832. uint m_nRowStep = nWidth * nHeight;
  833. // ch: 显示 || display
  834. try
  835. {
  836. if (enPixelType == MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8)
  837. {
  838. CogImage8Root cogImage8Root = new CogImage8Root();
  839. cogImage8Root.Initialize((Int32)nWidth, (Int32)nHeight, pImageBuf, (Int32)nWidth, null);
  840. CogImage8Grey cogImage8Grey = new CogImage8Grey();
  841. cogImage8Grey.SetRoot(cogImage8Root);
  842. cogImage = cogImage8Grey.ScaleImage((int)nWidth, (int)nHeight);
  843. System.GC.Collect();
  844. }
  845. else
  846. {
  847. CogImage8Root image0 = new CogImage8Root();
  848. IntPtr ptr0 = new IntPtr(pImageBuf.ToInt64());
  849. image0.Initialize((int)nWidth, (int)nHeight, ptr0, (int)nWidth, null);
  850. CogImage8Root image1 = new CogImage8Root();
  851. IntPtr ptr1 = new IntPtr(pImageBuf.ToInt64() + m_nRowStep);
  852. image1.Initialize((int)nWidth, (int)nHeight, ptr1, (int)nWidth, null);
  853. CogImage8Root image2 = new CogImage8Root();
  854. IntPtr ptr2 = new IntPtr(pImageBuf.ToInt64() + m_nRowStep * 2);
  855. image2.Initialize((int)nWidth, (int)nHeight, ptr2, (int)nWidth, null);
  856. CogImage24PlanarColor colorImage = new CogImage24PlanarColor();
  857. colorImage.SetRoots(image0, image1, image2);
  858. cogImage = colorImage.ScaleImage((int)nWidth, (int)nHeight);
  859. System.GC.Collect();
  860. }
  861. }
  862. catch (System.Exception ex)
  863. {
  864. ErrorMessage = $"转换ICogImage出错: {ex.Message}";
  865. return null;
  866. }
  867. return cogImage;
  868. }
  869. /// <summary>
  870. /// 图像是否为Mono格式
  871. /// </summary>
  872. /// <param name="enType"></param>
  873. /// <returns></returns>
  874. private bool IsMonoPixelFormat(MvCameraControl.MvGvspPixelType enType)
  875. {
  876. switch (enType)
  877. {
  878. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono8:
  879. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono10:
  880. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
  881. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono12:
  882. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
  883. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_Mono16:
  884. return true;
  885. default:
  886. return false;
  887. }
  888. }
  889. /// <summary>
  890. /// 图像是否为彩色
  891. /// </summary>
  892. /// <param name="enType"></param>
  893. /// <returns></returns>
  894. private bool IsColorPixelFormat(MvCameraControl.MvGvspPixelType enType)
  895. {
  896. switch (enType)
  897. {
  898. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
  899. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
  900. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_RGBA8_Packed:
  901. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BGRA8_Packed:
  902. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_YUV422_Packed:
  903. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_YUV422_YUYV_Packed:
  904. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
  905. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
  906. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
  907. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
  908. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRBGG8:
  909. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB10:
  910. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB10_Packed:
  911. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG10:
  912. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG10_Packed:
  913. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG10:
  914. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG10_Packed:
  915. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR10:
  916. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR10_Packed:
  917. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB12:
  918. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB12_Packed:
  919. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG12:
  920. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG12_Packed:
  921. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG12:
  922. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG12_Packed:
  923. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR12:
  924. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR12_Packed:
  925. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGR16:
  926. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerRG16:
  927. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerGB16:
  928. case MvCameraControl.MvGvspPixelType.PixelType_Gvsp_BayerBG16:
  929. return true;
  930. default:
  931. return false;
  932. }
  933. }
  934. #endregion
  935. #region 属性通知
  936. /// <summary>
  937. /// Occurs when a property value changes.
  938. /// </summary>
  939. public event PropertyChangedEventHandler PropertyChanged;
  940. /// <summary>
  941. /// Checks if a property already matches a desired value. Sets the property and
  942. /// notifies listeners only when necessary.
  943. /// </summary>
  944. /// <typeparam name="T">Type of the property.</typeparam>
  945. /// <param name="storage">Reference to a property with both getter and setter.</param>
  946. /// <param name="value">Desired value for the property.</param>
  947. /// <param name="propertyName">Name of the property used to notify listeners. This
  948. /// value is optional and can be provided automatically when invoked from compilers that
  949. /// support CallerMemberName.</param>
  950. /// <returns>True if the value was changed, false if the existing value matched the
  951. /// desired value.</returns>
  952. protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
  953. {
  954. if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
  955. storage = value;
  956. RaisePropertyChanged(propertyName);
  957. return true;
  958. }
  959. /// <summary>
  960. /// Checks if a property already matches a desired value. Sets the property and
  961. /// notifies listeners only when necessary.
  962. /// </summary>
  963. /// <typeparam name="T">Type of the property.</typeparam>
  964. /// <param name="storage">Reference to a property with both getter and setter.</param>
  965. /// <param name="value">Desired value for the property.</param>
  966. /// <param name="propertyName">Name of the property used to notify listeners. This
  967. /// value is optional and can be provided automatically when invoked from compilers that
  968. /// support CallerMemberName.</param>
  969. /// <param name="onChanged">Action that is called after the property value has been changed.</param>
  970. /// <returns>True if the value was changed, false if the existing value matched the
  971. /// desired value.</returns>
  972. protected virtual bool SetProperty<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
  973. {
  974. if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
  975. storage = value;
  976. onChanged?.Invoke();
  977. RaisePropertyChanged(propertyName);
  978. return true;
  979. }
  980. /// <summary>
  981. /// Raises this object's PropertyChanged event.
  982. /// </summary>
  983. /// <param name="propertyName">Name of the property used to notify listeners. This
  984. /// value is optional and can be provided automatically when invoked from compilers
  985. /// that support <see cref="CallerMemberNameAttribute"/>.</param>
  986. protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
  987. {
  988. OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
  989. }
  990. /// <summary>
  991. /// Raises this object's PropertyChanged event.
  992. /// </summary>
  993. /// <param name="args">The PropertyChangedEventArgs</param>
  994. protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
  995. {
  996. PropertyChanged?.Invoke(this, args);
  997. }
  998. #endregion
  999. }
  1000. }