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
}
}