MvCamera.cs 43 KB

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