using Basler.Pylon; using System.ComponentModel; using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using TeamAAS.Camera.Images; using TeamAAS.Camera.Models; using TeamAAS.Camera.Attributes; using TeamAAS.Camera.Enums; namespace TeamAAS.Camera.Cameras { /// /// 巴斯勒 Pylon 相机 /// [Camera("Basler Pylon", "巴斯勒 Pylon")] public class BaslerCamera : CameraBase { #region 字段 private Basler.Pylon.Camera m_MyCamera; UInt32 m_nRowStep = 0; private PixelDataConverter converter = new PixelDataConverter(); [Browsable(false)] public ICameraInfo CameraInfo { get; private set; } #endregion [Browsable(false)] public override string CameraBrand => "巴斯勒 Pylon"; public BaslerCamera(Guid id, int index, string name, string serialNumber, string cameraIp, bool isFindByIp) : base(id, index, name, serialNumber, cameraIp, isFindByIp) { List allCameraInfos = Basler.Pylon.CameraFinder.Enumerate(); foreach (ICameraInfo cameraInfo in allCameraInfos) { if (cameraInfo[CameraInfoKey.DeviceType].ToLower().Contains("gige")) { if (IsFindByIp) { if (cameraInfo[CameraInfoKey.DeviceIpAddress] == CameraIp) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.GIGE; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } else { if (cameraInfo[CameraInfoKey.SerialNumber] == SerialNumber) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.GIGE; CameraIp = cameraInfo[CameraInfoKey.DeviceIpAddress]; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } } else { if (cameraInfo[CameraInfoKey.SerialNumber] == SerialNumber) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.USB; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } } IsConnected = false; } #region 设备发现 public static CameraInfo[] GetDevices() { List cameras = new List(); try { if (!CheckSoftwareInstalled().isInstalled) { return cameras.ToArray(); } List allCameraInfos = Basler.Pylon.CameraFinder.Enumerate(); int i = 0; foreach (ICameraInfo cameraInfo in allCameraInfos) { if (cameraInfo[CameraInfoKey.DeviceType].ToLower().Contains("gige")) { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = cameraInfo[CameraInfoKey.FullName], CameraBrand = "巴斯勒 Pylon", CameraType = CameraType.GIGE, ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo], Model = cameraInfo[CameraInfoKey.ModelName], SerialNumber = cameraInfo[CameraInfoKey.SerialNumber], CameraIp = cameraInfo[CameraInfoKey.DeviceIpAddress], IsFindByIp = true, }); } else if (cameraInfo[CameraInfoKey.DeviceType].ToLower().Contains("usb")) { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = cameraInfo[CameraInfoKey.FullName], CameraBrand = "巴斯勒 Pylon", CameraType = CameraType.USB, ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo], Model = cameraInfo[CameraInfoKey.ModelName], SerialNumber = cameraInfo[CameraInfoKey.SerialNumber], CameraIp = "", }); } else if (cameraInfo[CameraInfoKey.DeviceType].ToLower().Contains("link")) { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = cameraInfo[CameraInfoKey.FullName], CameraBrand = "巴斯勒 Pylon", CameraType = CameraType.Link, ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo], Model = cameraInfo[CameraInfoKey.ModelName], SerialNumber = cameraInfo[CameraInfoKey.SerialNumber], CameraIp = "", }); } else { cameras.Add(new CameraInfo() { CameraNo = i + 1, CameraName = cameraInfo[CameraInfoKey.FullName], CameraBrand = "巴斯勒 Pylon", CameraType = CameraType.Unknown, ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo], Model = cameraInfo[CameraInfoKey.ModelName], SerialNumber = cameraInfo[CameraInfoKey.SerialNumber], CameraIp = "", }); } i++; } } catch (Exception) { } return cameras.ToArray(); } public static (bool isInstalled, string version) CheckSoftwareInstalled() { string softwareName = "pylon 7 Camera Software"; string softwareVersion = "7.5.0.15658"; string[] registryPaths = new[] { @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" }; foreach (var path in registryPaths) { using (RegistryKey key = Registry.LocalMachine.OpenSubKey(path)) { if (key == null) continue; foreach (string subKeyName in key.GetSubKeyNames()) { using (RegistryKey subKey = key.OpenSubKey(subKeyName)) { string displayName = subKey?.GetValue("DisplayName")?.ToString(); if (displayName != null && displayName.Contains(softwareName)) { string version = subKey.GetValue("DisplayVersion")?.ToString(); Version v1 = new Version(version); Version v2 = new Version(softwareVersion); if (v1 >= v2) { return (true, version); } else { return (false, version); } } } } } } return (false, ""); } #endregion #region SDK 实现 public override bool OpenDevice() { using (WriteLockScope()) { if (m_MyCamera == null) { List allCameraInfos = Basler.Pylon.CameraFinder.Enumerate(); foreach (ICameraInfo cameraInfo in allCameraInfos) { if (cameraInfo[CameraInfoKey.DeviceType].ToLower().Contains("gige")) { if (IsFindByIp) { if (cameraInfo[CameraInfoKey.DeviceIpAddress] == CameraIp) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.GIGE; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } else { if (cameraInfo[CameraInfoKey.SerialNumber] == SerialNumber) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.GIGE; CameraIp = cameraInfo[CameraInfoKey.DeviceIpAddress]; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } } else { if (cameraInfo[CameraInfoKey.SerialNumber] == SerialNumber) { CameraInfo = cameraInfo; m_MyCamera = new Basler.Pylon.Camera(cameraInfo); ManufacturerName = cameraInfo[CameraInfoKey.ManufacturerInfo]; ModelName = cameraInfo[CameraInfoKey.ModelName]; SerialNumber = cameraInfo[CameraInfoKey.SerialNumber]; CameraType = CameraType.USB; m_MyCamera.Parameters[PLCameraInstance.GrabCameraEvents].SetValue(true); m_MyCamera.CameraOpened += Configuration.AcquireContinuous; m_MyCamera.CameraOpened += M_MyCamera_CameraOpened; m_MyCamera.CameraClosed += M_MyCamera_CameraClosed; break; } } } if (m_MyCamera == null) { throw new Exception("没有识别到相机"); } } if (!m_MyCamera.IsOpen) { m_MyCamera.Open(); // 按当前触发模式下发(默认软触发,与历史硬编码行为一致) ApplyTriggerToHardware(TriggerMode); } IsConnected = m_MyCamera.IsConnected; return IsConnected; } } public override void CloseDevice() { using (WriteLockScope()) { if (m_MyCamera != null) { if (m_MyCamera.IsOpen) { m_MyCamera.Close(); m_MyCamera.CameraOpened -= Configuration.AcquireContinuous; m_MyCamera.CameraOpened -= M_MyCamera_CameraOpened; m_MyCamera.CameraClosed -= M_MyCamera_CameraClosed; IsConnected = m_MyCamera.IsConnected; m_MyCamera.Dispose(); } } } } public override GenImage Grab() { using (WriteLockScope()) { var sw = Stopwatch.StartNew(); if (!m_MyCamera.IsOpen) { m_MyCamera.Open(); ApplyTriggerToHardware(TriggerMode); } if (!m_MyCamera.StreamGrabber.IsGrabbing) { m_MyCamera.StreamGrabber.Start(); } bool Succeed = false; IGrabResult grabResult = null; for (int i = 0; i < 5; i++) { try { // 仅软触发需主动发触发命令;连续采集自由出图,硬触发等外部信号 if (IsSoftwareTrigger) m_MyCamera.ExecuteSoftwareTrigger(); grabResult = m_MyCamera.StreamGrabber.RetrieveResult(1000, TimeoutHandling.ThrowException); if (grabResult != null && grabResult.GrabSucceeded) { ErrorMessage = ""; Succeed = true; break; } else { ErrorMessage = grabResult.ErrorDescription; } } catch (Exception ex) { ErrorMessage = ex.Message; m_MyCamera.Close(); m_MyCamera.CameraOpened -= M_MyCamera_CameraOpened; m_MyCamera.CameraClosed -= M_MyCamera_CameraClosed; m_MyCamera.Open(); ApplyTriggerToHardware(TriggerMode); IsConnected = m_MyCamera.IsConnected; } } if (!Succeed) { if (IsConnected) { IsConnected = false; OnConnectChanged(false); } return null; } Image = GrabResult2GenImage(grabResult); ImageWidth = (uint)Image.Width; ImageHeight = (uint)Image.Height; ApplyGammaToImage(Image); GC.Collect(); grabResult.Dispose(); TotalTime = sw.Elapsed; return Image; }} #region 触发模式 /// Basler 支持连续/软触发/硬触发(Line0)全部模式 public override IReadOnlyList SupportedTriggerModes => FullTriggerModes; /// 历史行为:OpenDevice 硬编码 TriggerMode=On + TriggerSource=Software,故默认软触发 protected override CameraTriggerMode DefaultTriggerMode => CameraTriggerMode.软触发; protected override bool ApplyTriggerMode(CameraTriggerMode mode) { using (WriteLockScope()) return ApplyTriggerToHardware(mode); } /// /// 把触发模式下发到 Pylon。AcquisitionMode 恒为 Continuous,仅触发项随模式变化。 /// 调用方需已持有写锁(OpenDevice/Grab 内部已在锁内;锁可重入)。 /// private bool ApplyTriggerToHardware(CameraTriggerMode mode) { if (m_MyCamera == null || !m_MyCamera.IsOpen) return false; m_MyCamera.Parameters[PLCamera.AcquisitionMode].SetValue(PLCamera.AcquisitionMode.Continuous); switch (mode) { case CameraTriggerMode.连续采集: m_MyCamera.Parameters[PLCamera.TriggerMode].SetValue(PLCamera.TriggerMode.Off); break; case CameraTriggerMode.软触发: m_MyCamera.Parameters[PLCamera.TriggerSource].SetValue(PLCamera.TriggerSource.Software); m_MyCamera.Parameters[PLCamera.TriggerMode].SetValue(PLCamera.TriggerMode.On); break; case CameraTriggerMode.硬触发上升沿: case CameraTriggerMode.硬触发下降沿: // 本版 Pylon 的 TriggerSource 枚举无 Line0,Basler 机型光耦输入惯例为 Line1; // 边沿名为 RisingEdge/FallingEdge(非 GenICam 通用的 Rising/Falling)。 m_MyCamera.Parameters[PLCamera.TriggerSource].SetValue(PLCamera.TriggerSource.Line1); m_MyCamera.Parameters[PLCamera.TriggerActivation].SetValue( mode == CameraTriggerMode.硬触发上升沿 ? PLCamera.TriggerActivation.RisingEdge : PLCamera.TriggerActivation.FallingEdge); m_MyCamera.Parameters[PLCamera.TriggerMode].SetValue(PLCamera.TriggerMode.On); break; default: // 跟随相机:不下发,沿用相机自身配置 break; } return true; } #endregion public override bool SetExposureTime(float exposureTime) { using (WriteLockScope()) { if (m_MyCamera.Parameters.Contains(PLCamera.ExposureTimeAbs)) { m_MyCamera.Parameters[PLCamera.ExposureTimeAbs].SetValue(exposureTime); } else m_MyCamera.Parameters[PLCamera.ExposureTime].SetValue(exposureTime); return true; }} public override float GetExposureTime() { using (ReadLockScope()) { if (m_MyCamera.Parameters.Contains(PLCamera.ExposureTimeAbs)) { return (float)m_MyCamera.Parameters[PLCamera.ExposureTimeAbs].GetValue(); } else return (float)m_MyCamera.Parameters[PLCamera.ExposureTime].GetValue(); }} public override bool SetGain(float gain) { using (WriteLockScope()) { if (m_MyCamera.Parameters.Contains(PLCamera.Gain)) { m_MyCamera.Parameters[PLCamera.Gain].SetValue(gain); } else if (m_MyCamera.Parameters.Contains(PLCamera.GainRaw)) { m_MyCamera.Parameters[PLCamera.GainRaw].SetValue((int)gain); } else if (m_MyCamera.Parameters.Contains(PLCamera.GainAbs)) { m_MyCamera.Parameters[PLCamera.GainAbs].SetValue(gain); } return true; }} public override float GetGain() { using (ReadLockScope()) { if (m_MyCamera == null) return 0; if (m_MyCamera.Parameters.Contains(PLCamera.Gain)) return (float)m_MyCamera.Parameters[PLCamera.Gain].GetValue(); if (m_MyCamera.Parameters.Contains(PLCamera.GainAbs)) return (float)m_MyCamera.Parameters[PLCamera.GainAbs].GetValue(); return (float)m_MyCamera.Parameters[PLCamera.GainRaw].GetValue(); } } public override bool SetGamma(float gamma) { using (WriteLockScope()) { try { if (m_MyCamera != null && m_MyCamera.Parameters.Contains(PLCamera.Gamma)) { m_MyCamera.Parameters[PLCamera.Gamma].SetValue(gamma); return true; } } catch { } Gamma = gamma; return true; } } public override float GetGamma() { using (ReadLockScope()) { try { if (m_MyCamera != null && m_MyCamera.Parameters.Contains(PLCamera.Gamma)) return (float)m_MyCamera.Parameters[PLCamera.Gamma].GetValue(); } catch { } return Gamma; } } #endregion #region 辅助方法 private void M_MyCamera_CameraClosed(object sender, EventArgs e) { OnConnectChanged(false); } private void M_MyCamera_CameraOpened(object sender, EventArgs e) { OnConnectChanged(true); } private bool IsColorPixelFormat(Basler.Pylon.PixelType enType) { switch (enType) { case Basler.Pylon.PixelType.RGB8packed: case Basler.Pylon.PixelType.BGR8packed: case Basler.Pylon.PixelType.RGBA8packed: case Basler.Pylon.PixelType.BGRA8packed: case Basler.Pylon.PixelType.YUV422packed: case Basler.Pylon.PixelType.YUV422_YUYV_Packed: case Basler.Pylon.PixelType.BayerGR8: case Basler.Pylon.PixelType.BayerRG8: case Basler.Pylon.PixelType.BayerGB8: case Basler.Pylon.PixelType.BayerBG8: case Basler.Pylon.PixelType.BayerGB10: case Basler.Pylon.PixelType.BayerGB10pp: case Basler.Pylon.PixelType.BayerBG10: case Basler.Pylon.PixelType.BayerBG10pp: case Basler.Pylon.PixelType.BayerRG10: case Basler.Pylon.PixelType.BayerRG10pp: case Basler.Pylon.PixelType.BayerGR10: case Basler.Pylon.PixelType.BayerGR10pp: case Basler.Pylon.PixelType.BayerGB12: case Basler.Pylon.PixelType.BayerGB12p: case Basler.Pylon.PixelType.BayerBG12: case Basler.Pylon.PixelType.BayerBG12Packed: case Basler.Pylon.PixelType.BayerRG12: case Basler.Pylon.PixelType.BayerRG12Packed: case Basler.Pylon.PixelType.BayerGR12: case Basler.Pylon.PixelType.BayerGR12Packed: return true; default: return false; } } private bool IsMonoPixelFormat(Basler.Pylon.PixelType enType) { switch (enType) { case Basler.Pylon.PixelType.Mono8: case Basler.Pylon.PixelType.Mono10: case Basler.Pylon.PixelType.Mono10packed: case Basler.Pylon.PixelType.Mono12: case Basler.Pylon.PixelType.Mono12packed: return true; default: return false; } } private GenImage GrabResult2GenImage(IGrabResult grabResult) { if (grabResult == null) return null; int width = grabResult.Width; int height = grabResult.Height; byte[] pixelData = grabResult.PixelData as byte[]; if (pixelData == null || pixelData.Length == 0) return null; // 单通道灰度 if (IsMonoPixelFormat(grabResult.PixelTypeValue)) { if (grabResult.PixelTypeValue == Basler.Pylon.PixelType.Mono8) { return new GenImage(width, height, Images.PixelType.Grey8, pixelData, width); } // 非 Mono8 → 转 Mono8 int bufSize = width * height; var buf = new byte[bufSize]; GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); try { var conv = new Basler.Pylon.PixelDataConverter(); conv.OutputPixelFormat = Basler.Pylon.PixelType.Mono8; conv.Convert(handle.AddrOfPinnedObject(), bufSize, grabResult); } finally { handle.Free(); } return new GenImage(width, height, Images.PixelType.Grey8, buf, width); } // 彩色 → BGRA32 if (IsColorPixelFormat(grabResult.PixelTypeValue)) { int stride = width * 4; var buf = new byte[stride * height]; GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned); try { converter.OutputPixelFormat = Basler.Pylon.PixelType.BGRA8packed; converter.Convert(handle.AddrOfPinnedObject(), stride * height, grabResult); } finally { handle.Free(); } return new GenImage(width, height, Images.PixelType.BGRA32, buf, stride); } return null; } #endregion } }