using Microsoft.Win32;
using System.ComponentModel;
using MvCamCtrl.NET;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using TeamAAS.Camera.Images;
using TeamAAS.Camera.Models;
using TeamAAS.Camera.Attributes;
using TeamAAS.Camera.Enums;
namespace TeamAAS.Camera.Cameras
{
///
/// 海康威视 MVS 相机(原生 MVS SDK,无需 VisionPro 依赖)
///
[Camera("HikRobot MVS", "海康威视 MVS")]
public class MvCamera : CameraBase
{
private MyCamera _mvCamera;
private bool _isGrabbing;
/// 本实例是否已完成 CreateDevice + OpenDevice(自维护状态)。
/// 不要依赖 MV_CC_IsDeviceConnected_NET 判断“是否已打开”——对新 new 的裸句柄它会返回不可靠的 true。
private bool _deviceOpened;
[Browsable(false)]
public MyCamera.MV_CC_DEVICE_INFO CameraInfo { get; private set; }
[Browsable(false)]
public MyCamera.MvGvspPixelType PixelType { get; private set; }
[Browsable(false)]
public override string CameraBrand => "海康威视 MVS";
public MvCamera(Guid id, int index, string name, string serialNumber, string cameraIp, bool isFindByIp)
: base(id, index, name, serialNumber, cameraIp, isFindByIp)
{
FindDevice();
}
private void FindDevice()
{
var deviceList = new List();
CSystem.EnumDevices(CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE, ref deviceList);
for (int i = 0; i < deviceList.Count; i++)
{
var device = deviceList[i];
if (device.nTLayerType == CSystem.MV_GIGE_DEVICE)
{
var gigeInfo = (CGigECameraInfo)device;
uint nIp1 = ((gigeInfo.nCurrentIp & 0xff000000) >> 24);
uint nIp2 = ((gigeInfo.nCurrentIp & 0x00ff0000) >> 16);
uint nIp3 = ((gigeInfo.nCurrentIp & 0x0000ff00) >> 8);
uint nIp4 = (gigeInfo.nCurrentIp & 0x000000ff);
string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
bool match = IsFindByIp ? CameraIp == ip : string.Equals(gigeInfo.chSerialNumber, SerialNumber);
if (match)
{
ManufacturerName = gigeInfo.chManufacturerName;
ModelName = gigeInfo.chModelName;
SerialNumber = gigeInfo.chSerialNumber;
CameraType = CameraType.GIGE;
var gigeInfoData = new MyCamera.MV_GIGE_DEVICE_INFO
{
chSerialNumber = gigeInfo.chSerialNumber,
chUserDefinedName = gigeInfo.UserDefinedName,
chManufacturerName = gigeInfo.chManufacturerName,
chModelName = gigeInfo.chModelName,
nCurrentIp = gigeInfo.nCurrentIp,
nCurrentSubNetMask = gigeInfo.nCurrentSubNetMask,
nDefultGateWay = gigeInfo.nDefultGateWay
};
// 关键:必须补零到 SPECIAL_INFO 联合体的声明长度,
// 否则 P/Invoke 封送时抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
byte[] gigeBytes = PadToUnionSize(StructToBytes(gigeInfoData),
typeof(MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO),
"stGigEInfo");
CameraInfo = new MyCamera.MV_CC_DEVICE_INFO
{
nTLayerType = CSystem.MV_GIGE_DEVICE,
SpecialInfo = new MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO
{
stGigEInfo = gigeBytes
}
};
_isHaveCamera = true;
break;
}
}
else if (device.nTLayerType == CSystem.MV_USB_DEVICE)
{
var usbInfo = (CUSBCameraInfo)device;
if (string.Equals(usbInfo.chSerialNumber, SerialNumber))
{
ManufacturerName = usbInfo.chManufacturerName;
ModelName = usbInfo.chModelName;
SerialNumber = usbInfo.chSerialNumber;
CameraType = CameraType.USB;
var usbInfoData = new MyCamera.MV_USB3_DEVICE_INFO
{
chSerialNumber = usbInfo.chSerialNumber,
chUserDefinedName = usbInfo.UserDefinedName,
chManufacturerName = usbInfo.chManufacturerName,
chModelName = usbInfo.chModelName
};
byte[] usbBytes = PadToUnionSize(StructToBytes(usbInfoData),
typeof(MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO),
"stUsb3VInfo");
CameraInfo = new MyCamera.MV_CC_DEVICE_INFO
{
nTLayerType = CSystem.MV_USB_DEVICE,
SpecialInfo = new MyCamera.MV_CC_DEVICE_INFO.SPECIAL_INFO
{
stUsb3VInfo = usbBytes
}
};
_isHaveCamera = true;
break;
}
}
}
}
///
/// 把子设备结构转出的字节流补零到 SPECIAL_INFO 联合体中对应字段的声明长度。
/// 海康封装中 SPECIAL_INFO 的 stGigEInfo/stUsb3VInfo 是 ByValArray 定长字段,
/// 实际长度不足时 P/Invoke 封送会抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
///
private static byte[] PadToUnionSize(byte[] payload, Type unionType, string fieldName)
{
if (payload == null)
return null;
try
{
var field = unionType.GetField(fieldName,
BindingFlags.Public | BindingFlags.Instance);
var attr = field?
.GetCustomAttributes(typeof(MarshalAsAttribute), false)
.OfType()
.FirstOrDefault();
if (attr != null &&
attr.Value == UnmanagedType.ByValArray &&
attr.SizeConst > payload.Length)
{
var padded = new byte[attr.SizeConst];
Buffer.BlockCopy(payload, 0, padded, 0, payload.Length);
return padded;
}
}
catch { }
return payload;
}
#region 设备发现
public static CameraInfo[] GetDevices()
{
var cameras = new List();
try
{
if (!CheckSoftwareInstalled().isInstalled) return cameras.ToArray();
var deviceList = new List();
int nRet = CSystem.EnumDevices(CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE, ref deviceList);
if (nRet != 0) return cameras.ToArray();
for (int i = 0; i < deviceList.Count; i++)
{
if (deviceList[i].nTLayerType == CSystem.MV_GIGE_DEVICE)
{
var gigeInfo = (CGigECameraInfo)deviceList[i];
uint nIp1 = ((gigeInfo.nCurrentIp & 0xff000000) >> 24);
uint nIp2 = ((gigeInfo.nCurrentIp & 0x00ff0000) >> 16);
uint nIp3 = ((gigeInfo.nCurrentIp & 0x0000ff00) >> 8);
uint nIp4 = (gigeInfo.nCurrentIp & 0x000000ff);
string ip = string.Format("{0}.{1}.{2}.{3}", nIp1, nIp2, nIp3, nIp4);
cameras.Add(new CameraInfo
{
CameraNo = i + 1,
CameraName = gigeInfo.UserDefinedName,
CameraBrand = "海康威视 MVS",
CameraType = CameraType.GIGE,
ManufacturerName = gigeInfo.chManufacturerName,
Model = gigeInfo.chModelName,
SerialNumber = gigeInfo.chSerialNumber,
CameraIp = ip,
IsFindByIp = true
});
}
else if (deviceList[i].nTLayerType == CSystem.MV_USB_DEVICE)
{
var usbInfo = (CUSBCameraInfo)deviceList[i];
cameras.Add(new CameraInfo
{
CameraNo = i + 1,
CameraName = usbInfo.UserDefinedName,
CameraBrand = "海康威视 MVS",
CameraType = CameraType.USB,
ManufacturerName = usbInfo.chManufacturerName,
Model = usbInfo.chModelName,
SerialNumber = usbInfo.chSerialNumber,
CameraIp = ""
});
}
else if (deviceList[i].nTLayerType == CSystem.MV_CAMERALINK_DEVICE)
{
var clInfo = (CCamLCameraInfo)deviceList[i];
cameras.Add(new CameraInfo
{
CameraNo = i + 1,
CameraName = clInfo.chModelName,
CameraBrand = "海康威视 MVS",
CameraType = CameraType.Link,
ManufacturerName = clInfo.chManufacturerName,
Model = clInfo.chModelName,
SerialNumber = clInfo.chSerialNumber,
CameraIp = ""
});
}
}
}
catch { }
return cameras.ToArray();
}
public static (bool isInstalled, string version) CheckSoftwareInstalled()
{
string softwareName = "MVS";
string softwareVersion = "4.4.0";
string[] registryPaths = {
@"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.ToUpper().Trim() == softwareName.ToUpper())
{
string version = subKey.GetValue("DisplayVersion")?.ToString();
if (new Version(version) >= new Version(softwareVersion))
return (true, version);
return (false, version);
}
}
}
}
}
return (false, "");
}
#endregion
#region SDK 实现
public override bool OpenDevice()
{
using (WriteLockScope())
{
try
{
if (_mvCamera == null)
_mvCamera = new MyCamera();
// 上次意外掉线留下的旧句柄:先销毁再重建,保证链路干净。
if (_deviceOpened && !_mvCamera.MV_CC_IsDeviceConnected_NET())
{
try { _mvCamera.MV_CC_CloseDevice_NET(); } catch { }
try { _mvCamera.MV_CC_DestroyDevice_NET(); } catch { }
_deviceOpened = false;
_isGrabbing = false;
_mvCamera = new MyCamera();
}
if (!_deviceOpened)
{
// ★ 官方推荐流程:现场枚举得到“由 runtime 按原生布局反序列化”的设备信息,
// 其嵌入数组长度天然与声明一致;不要手工拼接 MV_CC_DEVICE_INFO——
// 否则 P/Invoke 会抛“嵌入数组实例的长度与布局中声明的长度不匹配”。
var matched = EnumerateAndMatch();
if (matched.nTLayerType == 0)
{
ErrorMessage = "未找到相机设备(请确认相机/海康虚拟相机是否在线)";
return false;
}
int nRet = _mvCamera.MV_CC_CreateDevice_NET(ref matched);
if (nRet != 0)
{
ErrorMessage = $"创建设备失败: 0x{nRet:X8}";
return false;
}
nRet = _mvCamera.MV_CC_OpenDevice_NET();
if (nRet != 0)
{
ErrorMessage = $"打开设备失败: 0x{nRet:X8}";
return false;
}
// 按当前触发模式下发(默认连续采集 = TriggerMode Off,与历史硬编码一致)
ApplyTriggerToHardware(TriggerMode);
_deviceOpened = true;
}
if (!IsConnected) OnConnectChanged(true);
IsConnected = true;
return true;
}
catch (Exception ex)
{
ErrorMessage = ex.Message;
return false;
}
}
}
///
/// 现场枚举(GigE + USB)并按 序列号/IP 匹配本实例配置的相机。
/// 返回值直接来自 MV_CC_EnumDevices_NET 的原生结构经 PtrToStructure 反序列化,
/// 嵌入数组长度与声明完全一致;未匹配到时返回 default(nTLayerType == 0)。
///
private MyCamera.MV_CC_DEVICE_INFO EnumerateAndMatch()
{
uint typeFlags = CSystem.MV_GIGE_DEVICE | CSystem.MV_USB_DEVICE;
var list = new MyCamera.MV_CC_DEVICE_INFO_LIST();
int nRet = MyCamera.MV_CC_EnumDevices_NET(typeFlags, ref list);
if (nRet != 0 || list.pDeviceInfo == null)
return default(MyCamera.MV_CC_DEVICE_INFO);
int count = Math.Min((int)list.nDeviceNum, list.pDeviceInfo.Length);
for (int i = 0; i < count; i++)
{
var ptr = list.pDeviceInfo[i];
if (ptr == IntPtr.Zero) continue;
var dev = (MyCamera.MV_CC_DEVICE_INFO)Marshal.PtrToStructure(
ptr, typeof(MyCamera.MV_CC_DEVICE_INFO));
string sn = string.Empty;
string ip = string.Empty;
if (dev.nTLayerType == CSystem.MV_GIGE_DEVICE &&
dev.SpecialInfo.stGigEInfo != null)
{
var gige = BytesToStruct(dev.SpecialInfo.stGigEInfo);
sn = gige.chSerialNumber ?? string.Empty;
ip = string.Format("{0}.{1}.{2}.{3}",
(gige.nCurrentIp & 0xff000000) >> 24,
(gige.nCurrentIp & 0x00ff0000) >> 16,
(gige.nCurrentIp & 0x0000ff00) >> 8,
(gige.nCurrentIp & 0x000000ff));
}
else if (dev.nTLayerType == CSystem.MV_USB_DEVICE &&
dev.SpecialInfo.stUsb3VInfo != null)
{
var usb = BytesToStruct(dev.SpecialInfo.stUsb3VInfo);
sn = usb.chSerialNumber ?? string.Empty;
}
bool match = IsFindByIp ? CameraIp == ip
: string.Equals(sn, SerialNumber, StringComparison.OrdinalIgnoreCase);
if (!match) continue;
// 同步展示信息(首次连接后 Manufacturer/Model 即为设备真实值)
if (dev.nTLayerType == CSystem.MV_GIGE_DEVICE)
{
var gige = BytesToStruct(dev.SpecialInfo.stGigEInfo);
CameraType = CameraType.GIGE;
if (!string.IsNullOrEmpty(gige.chManufacturerName)) ManufacturerName = gige.chManufacturerName;
if (!string.IsNullOrEmpty(gige.chModelName)) ModelName = gige.chModelName;
if (!string.IsNullOrEmpty(gige.chSerialNumber)) SerialNumber = gige.chSerialNumber;
_isHaveCamera = true;
}
else if (dev.nTLayerType == CSystem.MV_USB_DEVICE)
{
var usb = BytesToStruct(dev.SpecialInfo.stUsb3VInfo);
CameraType = CameraType.USB;
if (!string.IsNullOrEmpty(usb.chManufacturerName)) ManufacturerName = usb.chManufacturerName;
if (!string.IsNullOrEmpty(usb.chModelName)) ModelName = usb.chModelName;
if (!string.IsNullOrEmpty(usb.chSerialNumber)) SerialNumber = usb.chSerialNumber;
_isHaveCamera = true;
}
return dev;
}
return default(MyCamera.MV_CC_DEVICE_INFO);
}
/// 把定长 byte[] 反序列化为结构体(SPECIAL_INFO 联合体成员 → 具体设备信息)。
private static T BytesToStruct(byte[] data) where T : struct
{
var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
try
{
return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
}
finally
{
handle.Free();
}
}
public override void CloseDevice()
{
using (WriteLockScope())
{
// 无论 SDK 当前上报什么状态,只要本实例打开过就执行清理(防句柄泄漏)。
if (_deviceOpened && _mvCamera != null)
{
try { _mvCamera.MV_CC_CloseDevice_NET(); } catch { }
try { _mvCamera.MV_CC_DestroyDevice_NET(); } catch { }
}
_deviceOpened = false;
_isGrabbing = false;
_mvCamera = null;
IsConnected = false;
OnConnectChanged(false);
}
}
#region 触发模式
/// 海康 MVS 支持连续/软触发/硬触发(Line0)
public override IReadOnlyList SupportedTriggerModes => FullTriggerModes;
/// 历史行为:OpenDevice 硬编码 TriggerMode=Off,故默认连续采集
protected override CameraTriggerMode DefaultTriggerMode => CameraTriggerMode.连续采集;
protected override bool ApplyTriggerMode(CameraTriggerMode mode)
{
using (WriteLockScope())
return ApplyTriggerToHardware(mode);
}
///
/// 把触发模式下发到海康 SDK(GenICam 字符串接口,返回 0 = MV_OK)。
/// 调用方需已持有写锁(OpenDevice/Grab 内部已在锁内;锁可重入)。
///
private bool ApplyTriggerToHardware(CameraTriggerMode mode)
{
if (_mvCamera == null || !_mvCamera.MV_CC_IsDeviceConnected_NET()) return false;
_mvCamera.MV_CC_SetEnumValueByString_NET("AcquisitionMode", "Continuous");
switch (mode)
{
case CameraTriggerMode.连续采集:
return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "Off") == 0;
case CameraTriggerMode.软触发:
_mvCamera.MV_CC_SetEnumValueByString_NET("TriggerSource", "Software");
return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "On") == 0;
case CameraTriggerMode.硬触发上升沿:
case CameraTriggerMode.硬触发下降沿:
_mvCamera.MV_CC_SetEnumValueByString_NET("TriggerSource", "Line0");
_mvCamera.MV_CC_SetEnumValueByString_NET("TriggerActivation",
mode == CameraTriggerMode.硬触发上升沿 ? "Rising" : "Falling");
return _mvCamera.MV_CC_SetEnumValueByString_NET("TriggerMode", "On") == 0;
default: // 跟随相机:不下发,沿用相机自身配置
return true;
}
}
#endregion
public override GenImage Grab()
{
using (WriteLockScope())
{
var sw = Stopwatch.StartNew();
try
{
if (_mvCamera == null || !_mvCamera.MV_CC_IsDeviceConnected_NET())
{
ErrorMessage = "相机未连接";
return null;
}
if (!_isGrabbing)
{
_mvCamera.MV_CC_StartGrabbing_NET();
_isGrabbing = true;
}
// 仅软触发模式需主动发触发命令;连续采集自由出图,硬触发等外部信号
if (IsSoftwareTrigger)
_mvCamera.MV_CC_TriggerSoftwareExecute_NET();
var frame = new MyCamera.MV_FRAME_OUT();
int ret2 = _mvCamera.MV_CC_GetImageBuffer_NET(ref frame, 1000);
if (ret2 != 0)
{
ErrorMessage = $"取相失败: {ret2}";
_isGrabbing = false;
return null;
}
try
{
int w = frame.stFrameInfo.nWidth;
int h = frame.stFrameInfo.nHeight;
Images.PixelType pixelType = Images.PixelType.Grey8;
PixelType = frame.stFrameInfo.enPixelType;
GenImage genImg = CreateGenImageFromFrame(frame, w, h, ref pixelType);
if (genImg != null)
{
ErrorMessage = "";
Image = genImg;
ImageWidth = (uint)w;
ImageHeight = (uint)h;
ApplyGammaToImage(Image);
if (!IsConnected)
{
IsConnected = true;
OnConnectChanged(true);
}
TotalTime = sw.Elapsed;
return Image;
}
}
finally
{
_mvCamera.MV_CC_FreeImageBuffer_NET(ref frame);
}
return null;
}
catch (Exception ex)
{
_isGrabbing = false;
ErrorMessage = ex.Message;
return null;
}
}
}
public override bool SetExposureTime(float exposureTime)
{
using (WriteLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
_mvCamera.MV_CC_SetFloatValue_NET("ExposureTime", exposureTime);
}
return true;
}
catch { return false; }
}
}
public override float GetExposureTime()
{
using (ReadLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
return 0;
}
}
catch { }
return 0;
}
}
public override bool SetGain(float gain)
{
using (WriteLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
_mvCamera.MV_CC_SetFloatValue_NET("Gain", gain);
}
return true;
}
catch { return false; }
}
}
public override float GetGain()
{
using (ReadLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
return 0;
}
}
catch { }
return 0;
}
}
public override bool SetGamma(float gamma)
{
using (WriteLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
_mvCamera.MV_CC_SetFloatValue_NET("Gamma", gamma);
return true;
}
}
catch { }
UpdateGammaSnapshot(gamma);
return true;
}
}
public override float GetGamma()
{
using (ReadLockScope())
{
try
{
if (_mvCamera != null && _mvCamera.MV_CC_IsDeviceConnected_NET())
{
return Gamma;
}
}
catch { }
return Gamma;
}
}
#endregion
#region 辅助方法
private static byte[] StructToBytes(T obj) where T : struct
{
int size = Marshal.SizeOf(obj);
var bytes = new byte[size];
IntPtr ptr = Marshal.AllocHGlobal(size);
try
{
Marshal.StructureToPtr(obj, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return bytes;
}
private static GenImage CreateGenImageFromFrame(MyCamera.MV_FRAME_OUT frame, int w, int h, ref Images.PixelType pixelType)
{
int stride = (int)(frame.stFrameInfo.nFrameLen / (uint)h);
IntPtr dataPtr = frame.pBufAddr;
switch (frame.stFrameInfo.enPixelType)
{
case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono8:
{
pixelType = Images.PixelType.Grey8;
int dataSize = w * h;
var data = new byte[dataSize];
for (int y = 0; y < h; y++)
Marshal.Copy(dataPtr + y * stride, data, y * w, w);
return new GenImage(w, h, pixelType, data, w);
}
case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono10_Packed:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_Mono12_Packed:
{
pixelType = Images.PixelType.Grey8;
int dataSize = w * h;
var data = new byte[dataSize];
for (int y = 0; y < h; y++)
{
int srcOff = y * stride;
for (int x = 0; x < w; x++)
{
byte hi = Marshal.ReadByte(dataPtr, srcOff + x * 2 + 1);
data[y * w + x] = hi;
}
}
return new GenImage(w, h, pixelType, data, w);
}
case MyCamera.MvGvspPixelType.PixelType_Gvsp_RGB8_Packed:
{
pixelType = Images.PixelType.RGB24;
int dataSize = w * 3 * h;
var data = new byte[dataSize];
for (int y = 0; y < h; y++)
Marshal.Copy(dataPtr + y * stride, data, y * w * 3, w * 3);
return new GenImage(w, h, pixelType, data, w * 3);
}
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BGR8_Packed:
{
pixelType = Images.PixelType.BGR24;
int dataSize = w * 3 * h;
var data = new byte[dataSize];
for (int y = 0; y < h; y++)
Marshal.Copy(dataPtr + y * stride, data, y * w * 3, w * 3);
return new GenImage(w, h, pixelType, data, w * 3);
}
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGR8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerRG8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerGB8:
case MyCamera.MvGvspPixelType.PixelType_Gvsp_BayerBG8:
{
pixelType = Images.PixelType.BGR24;
return DemosaicBayer8(dataPtr, w, h, stride, frame.stFrameInfo.enPixelType);
}
default:
{
int dataSize = (int)frame.stFrameInfo.nFrameLen;
var data = new byte[dataSize];
Marshal.Copy(dataPtr, data, 0, dataSize);
pixelType = Images.PixelType.Grey8;
return new GenImage(w, h, pixelType, data, stride);
}
}
}
private static GenImage DemosaicBayer8(IntPtr src, int w, int h, int srcStride, MyCamera.MvGvspPixelType bayerType)
{
int dstStride = w * 3;
var dst = new byte[dstStride * h];
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
byte val = Marshal.ReadByte(src, y * srcStride + x);
int di = y * dstStride + x * 3;
dst[di] = val;
dst[di + 1] = val;
dst[di + 2] = val;
}
}
return new GenImage(w, h, Images.PixelType.BGR24, dst, dstStride);
}
#endregion
}
}