MvCamera.cs 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. using Microsoft.Win32;
  2. using System.ComponentModel;
  3. using MvCamCtrl.NET;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Diagnostics;
  7. using System.Linq;
  8. using System.Reflection;
  9. using System.Runtime.InteropServices;
  10. using TeamAAS.Camera.Images;
  11. using TeamAAS.Camera.Models;
  12. using TeamAAS.Camera.Attributes;
  13. using TeamAAS.Camera.Enums;
  14. namespace TeamAAS.Camera.Cameras
  15. {
  16. /// <summary>
  17. /// 海康威视 MVS 相机(原生 MVS SDK,无需 VisionPro 依赖)
  18. /// </summary>
  19. [Camera("HikRobot MVS", "海康威视 MVS")]
  20. public class MvCamera : CameraBase
  21. {
  22. private MyCamera _mvCamera;
  23. private bool _isGrabbing;
  24. /// <summary>本实例是否已完成 CreateDevice + OpenDevice(自维护状态)。
  25. /// 不要依赖 MV_CC_IsDeviceConnected_NET 判断“是否已打开”——对新 new 的裸句柄它会返回不可靠的 true。</summary>
  26. private bool _deviceOpened;
  27. [Browsable(false)]
  28. public MyCamera.MV_CC_DEVICE_INFO CameraInfo { get; private set; }
  29. [Browsable(false)]
  30. public MyCamera.MvGvspPixelType PixelType { get; private set; }
  31. [Browsable(false)]
  32. public override string CameraBrand => "海康威视 MVS";
  33. public MvCamera(Guid id, int index, string name, string serialNumber, string cameraIp, bool isFindByIp)
  34. : base(id, index, name, serialNumber, cameraIp, isFindByIp)
  35. {
  36. FindDevice();
  37. }
  38. private void FindDevice()
  39. {
  40. var deviceList = new List<CCameraInfo>();
  41. CSystem.EnumDevices(CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE, ref deviceList);
  42. for (int i = 0; i < deviceList.Count; i++)
  43. {
  44. var device = deviceList[i];
  45. if (device.nTLayerType == CSystem.MV_GIGE_DEVICE)
  46. {
  47. var gigeInfo = (CGigECameraInfo)device;
  48. uint nIp1 = ((gigeInfo.nCurrentIp & 0xff000000) >> 24);
  49. uint nIp2 = ((gigeInfo.nCurrentIp & 0x00ff0000) >> 16);
  50. uint nIp3 = ((gigeInfo.nCurrentIp & 0x0000ff00) >> 8);
  51. uint nIp4 = (gigeInfo.nCurrentIp & 0x000000ff);
  52. string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
  53. bool match = IsFindByIp ? CameraIp == ip : string.Equals(gigeInfo.chSerialNumber, SerialNumber);
  54. if (match)
  55. {
  56. ManufacturerName = gigeInfo.chManufacturerName;
  57. ModelName = gigeInfo.chModelName;
  58. SerialNumber = gigeInfo.chSerialNumber;
  59. CameraType = CameraType.GIGE;
  60. var gigeInfoData = new MyCamera.MV_GIGE_DEVICE_INFO
  61. {
  62. chSerialNumber = gigeInfo.chSerialNumber,
  63. chUserDefinedName = gigeInfo.UserDefinedName,
  64. chManufacturerName = gigeInfo.chManufacturerName,
  65. chModelName = gigeInfo.chModelName,
  66. nCurrentIp = gigeInfo.nCurrentIp,
  67. nCurrentSubNetMask = gigeInfo.nCurrentSubNetMask,
  68. nDefultGateWay = gigeInfo.nDefultGateWay
  69. };
  70. // 关键:必须补零到 SPECIAL_INFO 联合体的声明长度,
  71. // 否则 P/Invoke 封送时抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
  72. byte[] gigeBytes = PadToUnionSize(StructToBytes(gigeInfoData),
  73. typeof(MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO),
  74. "stGigEInfo");
  75. CameraInfo = new MyCamera.MV_CC_DEVICE_INFO
  76. {
  77. nTLayerType = CSystem.MV_GIGE_DEVICE,
  78. SpecialInfo = new MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO
  79. {
  80. stGigEInfo = gigeBytes
  81. }
  82. };
  83. _isHaveCamera = true;
  84. break;
  85. }
  86. }
  87. else if (device.nTLayerType == CSystem.MV_USB_DEVICE)
  88. {
  89. var usbInfo = (CUSBCameraInfo)device;
  90. if (string.Equals(usbInfo.chSerialNumber, SerialNumber))
  91. {
  92. ManufacturerName = usbInfo.chManufacturerName;
  93. ModelName = usbInfo.chModelName;
  94. SerialNumber = usbInfo.chSerialNumber;
  95. CameraType = CameraType.USB;
  96. var usbInfoData = new MyCamera.MV_USB3_DEVICE_INFO
  97. {
  98. chSerialNumber = usbInfo.chSerialNumber,
  99. chUserDefinedName = usbInfo.UserDefinedName,
  100. chManufacturerName = usbInfo.chManufacturerName,
  101. chModelName = usbInfo.chModelName
  102. };
  103. byte[] usbBytes = PadToUnionSize(StructToBytes(usbInfoData),
  104. typeof(MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO),
  105. "stUsb3VInfo");
  106. CameraInfo = new MyCamera.MV_CC_DEVICE_INFO
  107. {
  108. nTLayerType = CSystem.MV_USB_DEVICE,
  109. SpecialInfo = new MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO
  110. {
  111. stUsb3VInfo = usbBytes
  112. }
  113. };
  114. _isHaveCamera = true;
  115. break;
  116. }
  117. }
  118. }
  119. }
  120. /// <summary>
  121. /// 把子设备结构转出的字节流补零到 SPECIAL_INFO 联合体中对应字段的声明长度。
  122. /// 海康封装中 SPECIAL_INFO 的 stGigEInfo/stUsb3VInfo 是 ByValArray 定长字段,
  123. /// 实际长度不足时 P/Invoke 封送会抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
  124. /// </summary>
  125. private static byte[] PadToUnionSize(byte[] payload, Type unionType, string fieldName)
  126. {
  127. if (payload == null)
  128. return null;
  129. try
  130. {
  131. var field = unionType.GetField(fieldName,
  132. BindingFlags.Public | BindingFlags.Instance);
  133. var attr = field?
  134. .GetCustomAttributes(typeof(MarshalAsAttribute), false)
  135. .OfType<MarshalAsAttribute>()
  136. .FirstOrDefault();
  137. if (attr != null &&
  138. attr.Value == UnmanagedType.ByValArray &&
  139. attr.SizeConst > payload.Length)
  140. {
  141. var padded = new byte[attr.SizeConst];
  142. Buffer.BlockCopy(payload, 0, padded, 0, payload.Length);
  143. return padded;
  144. }
  145. }
  146. catch { }
  147. return payload;
  148. }
  149. #region 设备发现
  150. public static CameraInfo[] GetDevices()
  151. {
  152. var cameras = new List<CameraInfo>();
  153. try
  154. {
  155. if (!CheckSoftwareInstalled().isInstalled) return cameras.ToArray();
  156. var deviceList = new List<CCameraInfo>();
  157. int nRet = CSystem.EnumDevices(CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE, ref deviceList);
  158. if (nRet != 0) return cameras.ToArray();
  159. for (int i = 0; i < deviceList.Count; i++)
  160. {
  161. if (deviceList[i].nTLayerType == CSystem.MV_GIGE_DEVICE)
  162. {
  163. var gigeInfo = (CGigECameraInfo)deviceList[i];
  164. uint nIp1 = ((gigeInfo.nCurrentIp & 0xff000000) >> 24);
  165. uint nIp2 = ((gigeInfo.nCurrentIp & 0x00ff0000) >> 16);
  166. uint nIp3 = ((gigeInfo.nCurrentIp & 0x0000ff00) >> 8);
  167. uint nIp4 = (gigeInfo.nCurrentIp & 0x000000ff);
  168. string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
  169. cameras.Add(new CameraInfo
  170. {
  171. CameraNo = i + 1,
  172. CameraName = gigeInfo.UserDefinedName,
  173. CameraBrand = "海康威视 MVS",
  174. CameraType = CameraType.GIGE,
  175. ManufacturerName = gigeInfo.chManufacturerName,
  176. Model = gigeInfo.chModelName,
  177. SerialNumber = gigeInfo.chSerialNumber,
  178. CameraIp = ip,
  179. IsFindByIp = true
  180. });
  181. }
  182. else if (deviceList[i].nTLayerType == CSystem.MV_USB_DEVICE)
  183. {
  184. var usbInfo = (CUSBCameraInfo)deviceList[i];
  185. cameras.Add(new CameraInfo
  186. {
  187. CameraNo = i + 1,
  188. CameraName = usbInfo.UserDefinedName,
  189. CameraBrand = "海康威视 MVS",
  190. CameraType = CameraType.USB,
  191. ManufacturerName = usbInfo.chManufacturerName,
  192. Model = usbInfo.chModelName,
  193. SerialNumber = usbInfo.chSerialNumber,
  194. CameraIp = ""
  195. });
  196. }
  197. else if (deviceList[i].nTLayerType == CSystem.MV_CAMERALINK_DEVICE)
  198. {
  199. var clInfo = (CCamLCameraInfo)deviceList[i];
  200. cameras.Add(new CameraInfo
  201. {
  202. CameraNo = i + 1,
  203. CameraName = clInfo.chModelName,
  204. CameraBrand = "海康威视 MVS",
  205. CameraType = CameraType.Link,
  206. ManufacturerName = clInfo.chManufacturerName,
  207. Model = clInfo.chModelName,
  208. SerialNumber = clInfo.chSerialNumber,
  209. CameraIp = ""
  210. });
  211. }
  212. }
  213. }
  214. catch { }
  215. return cameras.ToArray();
  216. }
  217. public static (bool isInstalled, string version) CheckSoftwareInstalled()
  218. {
  219. string softwareName = "MVS";
  220. string softwareVersion = "4.4.0";
  221. string[] registryPaths = {
  222. @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
  223. @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
  224. };
  225. foreach (var path in registryPaths)
  226. {
  227. using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path))
  228. {
  229. if (key == null) continue;
  230. foreach (string subKeyName in key.GetSubKeyNames())
  231. {
  232. using (RegistryKey subKey = key.OpenSubKey(subKeyName))
  233. {
  234. string displayName = subKey?.GetValue("DisplayName")?.ToString();
  235. if (displayName != null && displayName.ToUpper().Trim() == softwareName.ToUpper())
  236. {
  237. string version = subKey.GetValue("DisplayVersion")?.ToString();
  238. if (new Version(version) >= new Version(softwareVersion))
  239. return (true, version);
  240. return (false, version);
  241. }
  242. }
  243. }
  244. }
  245. }
  246. return (false, "");
  247. }
  248. #endregion
  249. #region SDK 实现
  250. public override bool OpenDevice()
  251. {
  252. using (WriteLockScope())
  253. {
  254. try
  255. {
  256. if (_mvCamera == null)
  257. _mvCamera = new MyCamera();
  258. // 上次意外掉线留下的旧句柄:先销毁再重建,保证链路干净。
  259. if (_deviceOpened && !_mvCamera.MV_CC_IsDeviceConnected_NET())
  260. {
  261. try { _mvCamera.MV_CC_CloseDevice_NET(); } catch { }
  262. try { _mvCamera.MV_CC_DestroyDevice_NET(); } catch { }
  263. _deviceOpened = false;
  264. _isGrabbing = false;
  265. _mvCamera = new MyCamera();
  266. }
  267. if (!_deviceOpened)
  268. {
  269. // ★ 官方推荐流程:现场枚举得到“由 runtime 按原生布局反序列化”的设备信息,
  270. // 其嵌入数组长度天然与声明一致;不要手工拼接 MV_CC_DEVICE_INFO——
  271. // 否则 P/Invoke 会抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
  272. var matched = EnumerateAndMatch();
  273. if (matched.nTLayerType == 0)
  274. {
  275. ErrorMessage = "未找到相机设备(请确认相机/海康虚拟相机是否在线)";
  276. return false;
  277. }
  278. int nRet = _mvCamera.MV_CC_CreateDevice_NET(ref matched);
  279. if (nRet != 0)
  280. {
  281. ErrorMessage = $"创建设备失败: 0x{nRet:X8}";
  282. return false;
  283. }
  284. nRet = _mvCamera.MV_CC_OpenDevice_NET();
  285. if (nRet != 0)
  286. {
  287. ErrorMessage = $"打开设备失败: 0x{nRet:X8}";
  288. return false;
  289. }
  290. // 按当前触发模式下发(默认连续采集 = TriggerMode Off,与历史硬编码一致)
  291. ApplyTriggerToHardware(TriggerMode);
  292. _deviceOpened = true;
  293. }
  294. if (!IsConnected) OnConnectChanged(true);
  295. IsConnected = true;
  296. return true;
  297. }
  298. catch (Exception ex)
  299. {
  300. ErrorMessage = ex.Message;
  301. return false;
  302. }
  303. }
  304. }
  305. /// <summary>
  306. /// 现场枚举(GigE + USB)并按 序列号/IP 匹配本实例配置的相机。
  307. /// 返回值直接来自 MV_CC_EnumDevices_NET 的原生结构经 PtrToStructure 反序列化,
  308. /// 嵌入数组长度与声明完全一致;未匹配到时返回 default(nTLayerType == 0)。
  309. /// </summary>
  310. private MyCamera.MV_CC_DEVICE_INFO EnumerateAndMatch()
  311. {
  312. uint typeFlags = CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE;
  313. var list = new MyCamera.MV_CC_DEVICE_INFO_LIST();
  314. int nRet = MyCamera.MV_CC_EnumDevices_NET(typeFlags, ref list);
  315. if (nRet != 0 || list.pDeviceInfo == null)
  316. return default(MyCamera.MV_CC_DEVICE_INFO);
  317. int count = Math.Min((int)list.nDeviceNum, list.pDeviceInfo.Length);
  318. for (int i = 0; i < count; i++)
  319. {
  320. var ptr = list.pDeviceInfo[i];
  321. if (ptr == IntPtr.Zero) continue;
  322. var dev = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(
  323. ptr, typeof(MyCamera.MV_CC_DEVICE_INFO));
  324. string sn = string.Empty;
  325. string ip = string.Empty;
  326. if (dev.nTLayerType == CSystem.MV_GIGE_DEVICE &&
  327. dev.SpecialInfo.stGigEInfo != null)
  328. {
  329. var gige = BytesToStruct<MyCamera.MV_GIGE_DEVICE_INFO>(dev.SpecialInfo.stGigEInfo);
  330. sn = gige.chSerialNumber ?? string.Empty;
  331. ip = string.Format("{0}.{1}.{2}.{3}",
  332. (gige.nCurrentIp & 0xff000000) >> 24,
  333. (gige.nCurrentIp & 0x00ff0000) >> 16,
  334. (gige.nCurrentIp & 0x0000ff00) >> 8,
  335. (gige.nCurrentIp & 0x000000ff));
  336. }
  337. else if (dev.nTLayerType == CSystem.MV_USB_DEVICE &&
  338. dev.SpecialInfo.stUsb3VInfo != null)
  339. {
  340. var usb = BytesToStruct<MyCamera.MV_USB3_DEVICE_INFO>(dev.SpecialInfo.stUsb3VInfo);
  341. sn = usb.chSerialNumber ?? string.Empty;
  342. }
  343. bool match = IsFindByIp ? CameraIp == ip
  344. : string.Equals(sn, SerialNumber, StringComparison.OrdinalIgnoreCase);
  345. if (!match) continue;
  346. // 同步展示信息(首次连接后 Manufacturer/Model 即为设备真实值)
  347. if (dev.nTLayerType == CSystem.MV_GIGE_DEVICE)
  348. {
  349. var gige = BytesToStruct<MyCamera.MV_GIGE_DEVICE_INFO>(dev.SpecialInfo.stGigEInfo);
  350. CameraType = CameraType.GIGE;
  351. if (!string.IsNullOrEmpty(gige.chManufacturerName)) ManufacturerName = gige.chManufacturerName;
  352. if (!string.IsNullOrEmpty(gige.chModelName)) ModelName = gige.chModelName;
  353. if (!string.IsNullOrEmpty(gige.chSerialNumber)) SerialNumber = gige.chSerialNumber;
  354. _isHaveCamera = true;
  355. }
  356. else if (dev.nTLayerType == CSystem.MV_USB_DEVICE)
  357. {
  358. var usb = BytesToStruct<MyCamera.MV_USB3_DEVICE_INFO>(dev.SpecialInfo.stUsb3VInfo);
  359. CameraType = CameraType.USB;
  360. if (!string.IsNullOrEmpty(usb.chManufacturerName)) ManufacturerName = usb.chManufacturerName;
  361. if (!string.IsNullOrEmpty(usb.chModelName)) ModelName = usb.chModelName;
  362. if (!string.IsNullOrEmpty(usb.chSerialNumber)) SerialNumber = usb.chSerialNumber;
  363. _isHaveCamera = true;
  364. }
  365. return dev;
  366. }
  367. return default(MyCamera.MV_CC_DEVICE_INFO);
  368. }
  369. /// <summary>把定长 byte[] 反序列化为结构体(SPECIAL_INFO 联合体成员 → 具体设备信息)。</summary>
  370. private static T BytesToStruct<T>(byte[] data) where T : struct
  371. {
  372. var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
  373. try
  374. {
  375. return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
  376. }
  377. finally
  378. {
  379. handle.Free();
  380. }
  381. }
  382. public override void CloseDevice()
  383. {
  384. using (WriteLockScope())
  385. {
  386. // 无论 SDK 当前上报什么状态,只要本实例打开过就执行清理(防句柄泄漏)。
  387. if (_deviceOpened && _mvCamera != null)
  388. {
  389. try { _mvCamera.MV_CC_CloseDevice_NET(); } catch { }
  390. try { _mvCamera.MV_CC_DestroyDevice_NET(); } catch { }
  391. }
  392. _deviceOpened = false;
  393. _isGrabbing = false;
  394. _mvCamera = null;
  395. IsConnected = false;
  396. OnConnectChanged(false);
  397. }
  398. }
  399. #region 触发模式
  400. /// <summary>海康 MVS 支持连续/软触发/硬触发(Line0)</summary>
  401. public override IReadOnlyList<CameraTriggerMode> SupportedTriggerModes => FullTriggerModes;
  402. /// <summary>历史行为:OpenDevice 硬编码 TriggerMode=Off,故默认连续采集</summary>
  403. protected override CameraTriggerMode DefaultTriggerMode => CameraTriggerMode.连续采集;
  404. protected override bool ApplyTriggerMode(CameraTriggerMode mode)
  405. {
  406. using (WriteLockScope())
  407. return ApplyTriggerToHardware(mode);
  408. }
  409. /// <summary>
  410. /// 把触发模式下发到海康 SDK(GenICam 字符串接口,返回 0 = MV_OK)。
  411. /// 调用方需已持有写锁(OpenDevice/Grab 内部已在锁内;锁可重入)。
  412. /// </summary>
  413. private bool ApplyTriggerToHardware(CameraTriggerMode mode)
  414. {
  415. if (_mvCamera == null || !_mvCamera.MV_CC_IsDeviceConnected_NET()) return false;
  416. _mvCamera.MV_CC_SetEnumValueByString_NET("AcquisitionMode", "Continuous");
  417. switch (mode)
  418. {
  419. case CameraTriggerMode.连续采集:
  420. return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "Off") == 0;
  421. case CameraTriggerMode.软触发:
  422. _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerSource", "Software");
  423. return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "On") == 0;
  424. case CameraTriggerMode.硬触发上升沿:
  425. case CameraTriggerMode.硬触发下降沿:
  426. _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerSource", "Line0");
  427. _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerActivation",
  428. mode == CameraTriggerMode.硬触发上升沿 ? "Rising" : "Falling");
  429. return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "On") == 0;
  430. default: // 跟随相机:不下发,沿用相机自身配置
  431. return true;
  432. }
  433. }
  434. #endregion
  435. public override GenImage Grab()
  436. {
  437. using (WriteLockScope())
  438. {
  439. var sw = Stopwatch.StartNew();
  440. try
  441. {
  442. if (_mvCamera == null || !_mvCamera.MV_CC_IsDeviceConnected_NET())
  443. {
  444. ErrorMessage = "相机未连接";
  445. return null;
  446. }
  447. if (!_isGrabbing)
  448. {
  449. _mvCamera.MV_CC_StartGrabbing_NET();
  450. _isGrabbing = true;
  451. }
  452. // 仅软触发模式需主动发触发命令;连续采集自由出图,硬触发等外部信号
  453. if (IsSoftwareTrigger)
  454. _mvCamera.MV_CC_TriggerSoftwareExecute_NET();
  455. var frame = new MyCamera.MV_FRAME_OUT();
  456. int ret2 = _mvCamera.MV_CC_GetImageBuffer_NET(ref frame, 1000);
  457. if (ret2 != 0)
  458. {
  459. ErrorMessage = $"取相失败: {ret2}";
  460. _isGrabbing = false;
  461. return null;
  462. }
  463. try
  464. {
  465. int w = frame.stFrameInfo.nWidth;
  466. int h = frame.stFrameInfo.nHeight;
  467. Images.PixelType pixelType = Images.PixelType.Grey8;
  468. PixelType = frame.stFrameInfo.enPixelType;
  469. GenImage genImg = CreateGenImageFromFrame(frame, w, h, ref pixelType);
  470. if (genImg != null)
  471. {
  472. ErrorMessage = "";
  473. Image = genImg;
  474. ImageWidth = (uint)w;
  475. ImageHeight = (uint)h;
  476. ApplyGammaToImage(Image);
  477. if (!IsConnected)
  478. {
  479. IsConnected = true;
  480. OnConnectChanged(true);
  481. }
  482. TotalTime = sw.Elapsed;
  483. return Image;
  484. }
  485. }
  486. finally
  487. {
  488. _mvCamera.MV_CC_FreeImageBuffer_NET(ref frame);
  489. }
  490. return null;
  491. }
  492. catch (Exception ex)
  493. {
  494. _isGrabbing = false;
  495. ErrorMessage = ex.Message;
  496. return null;
  497. }
  498. }
  499. }
  500. public override bool SetExposureTime(float exposureTime)
  501. {
  502. using (WriteLockScope())
  503. {
  504. try
  505. {
  506. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  507. {
  508. _mvCamera.MV_CC_SetFloatValue_NET("ExposureTime", exposureTime);
  509. }
  510. return true;
  511. }
  512. catch { return false; }
  513. }
  514. }
  515. public override float GetExposureTime()
  516. {
  517. using (ReadLockScope())
  518. {
  519. try
  520. {
  521. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  522. {
  523. return 0;
  524. }
  525. }
  526. catch { }
  527. return 0;
  528. }
  529. }
  530. public override bool SetGain(float gain)
  531. {
  532. using (WriteLockScope())
  533. {
  534. try
  535. {
  536. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  537. {
  538. _mvCamera.MV_CC_SetFloatValue_NET("Gain", gain);
  539. }
  540. return true;
  541. }
  542. catch { return false; }
  543. }
  544. }
  545. public override float GetGain()
  546. {
  547. using (ReadLockScope())
  548. {
  549. try
  550. {
  551. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  552. {
  553. return 0;
  554. }
  555. }
  556. catch { }
  557. return 0;
  558. }
  559. }
  560. public override bool SetGamma(float gamma)
  561. {
  562. using (WriteLockScope())
  563. {
  564. try
  565. {
  566. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  567. {
  568. _mvCamera.MV_CC_SetFloatValue_NET("Gamma", gamma);
  569. return true;
  570. }
  571. }
  572. catch { }
  573. UpdateGammaSnapshot(gamma);
  574. return true;
  575. }
  576. }
  577. public override float GetGamma()
  578. {
  579. using (ReadLockScope())
  580. {
  581. try
  582. {
  583. if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
  584. {
  585. return Gamma;
  586. }
  587. }
  588. catch { }
  589. return Gamma;
  590. }
  591. }
  592. #endregion
  593. #region 辅助方法
  594. private static byte[] StructToBytes<T>(T obj) where T : struct
  595. {
  596. int size = Marshal.SizeOf(obj);
  597. var bytes = new byte[size];
  598. IntPtr ptr = Marshal.AllocHGlobal(size);
  599. try
  600. {
  601. Marshal.StructureToPtr(obj, ptr, false);
  602. Marshal.Copy(ptr, bytes, 0, size);
  603. }
  604. finally
  605. {
  606. Marshal.FreeHGlobal(ptr);
  607. }
  608. return bytes;
  609. }
  610. private static GenImage CreateGenImageFromFrame(MyCamera.MV_FRAME_OUT frame, int w, int h, ref Images.PixelType pixelType)
  611. {
  612. int stride = (int)(frame.stFrameInfo.nFrameLen / (uint)h);
  613. IntPtr dataPtr = frame.pBufAddr;
  614. switch (frame.stFrameInfo.enPixelType)
  615. {
  616. case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
  617. {
  618. pixelType = Images.PixelType.Grey8;
  619. int dataSize = w * h;
  620. var data = new byte[dataSize];
  621. for (int y = 0; y < h; y++)
  622. Marshal.Copy(dataPtr + y * stride, data, y * w, w);
  623. return new GenImage(w, h, pixelType, data, w);
  624. }
  625. case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
  626. case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
  627. {
  628. pixelType = Images.PixelType.Grey8;
  629. int dataSize = w * h;
  630. var data = new byte[dataSize];
  631. for (int y = 0; y < h; y++)
  632. {
  633. int srcOff = y * stride;
  634. for (int x = 0; x < w; x++)
  635. {
  636. byte hi = Marshal.ReadByte(dataPtr, srcOff + x * 2 + 1);
  637. data[y * w + x] = hi;
  638. }
  639. }
  640. return new GenImage(w, h, pixelType, data, w);
  641. }
  642. case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
  643. {
  644. pixelType = Images.PixelType.RGB24;
  645. int dataSize = w * 3 * h;
  646. var data = new byte[dataSize];
  647. for (int y = 0; y < h; y++)
  648. Marshal.Copy(dataPtr + y * stride, data, y * w * 3, w * 3);
  649. return new GenImage(w, h, pixelType, data, w * 3);
  650. }
  651. case MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
  652. {
  653. pixelType = Images.PixelType.BGR24;
  654. int dataSize = w * 3 * h;
  655. var data = new byte[dataSize];
  656. for (int y = 0; y < h; y++)
  657. Marshal.Copy(dataPtr + y * stride, data, y * w * 3, w * 3);
  658. return new GenImage(w, h, pixelType, data, w * 3);
  659. }
  660. case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
  661. case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
  662. case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
  663. case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
  664. {
  665. pixelType = Images.PixelType.BGR24;
  666. return DemosaicBayer8(dataPtr, w, h, stride, frame.stFrameInfo.enPixelType);
  667. }
  668. default:
  669. {
  670. int dataSize = (int)frame.stFrameInfo.nFrameLen;
  671. var data = new byte[dataSize];
  672. Marshal.Copy(dataPtr, data, 0, dataSize);
  673. pixelType = Images.PixelType.Grey8;
  674. return new GenImage(w, h, pixelType, data, stride);
  675. }
  676. }
  677. }
  678. private static GenImage DemosaicBayer8(IntPtr src, int w, int h, int srcStride, MyCamera.MvGvspPixelType bayerType)
  679. {
  680. int dstStride = w * 3;
  681. var dst = new byte[dstStride * h];
  682. for (int y = 0; y < h; y++)
  683. {
  684. for (int x = 0; x < w; x++)
  685. {
  686. byte val = Marshal.ReadByte(src, y * srcStride + x);
  687. int di = y * dstStride + x * 3;
  688. dst[di] = val;
  689. dst[di + 1] = val;
  690. dst[di + 2] = val;
  691. }
  692. }
  693. return new GenImage(w, h, Images.PixelType.BGR24, dst, dstStride);
  694. }
  695. #endregion
  696. }
  697. }