当前位置: 首页 > news >正文

C# OpencvSharp获取Astra Pro奥比中光深度相机深度图

在某鱼花了90RMB,买了个Astra Pro奥比中光深度相机,性价比还是比较高的。通过封装官方的SDK,导出为dll给C#调用,效果如下

///////////AstraCamera.cs////////////

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Drawing;
using OpenCvSharp;
using OpenCvSharp.Extensions;

public class AstraCamera : IDisposable
{
// DLL导入
[DllImport("ColorReaderEventCPP.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void StartCamera();

    [DllImport("ColorReaderEventCPP.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void StopCamera();

    [DllImport("ColorReaderEventCPP.dll", CallingConvention = CallingConvention.StdCall)]
private static extern bool GetLatestFrames(
IntPtr rawDepthData, int rawDepthSize,
IntPtr depthData, int depthSize,
IntPtr colorData, int colorSize);

    [DllImport("ColorReaderEventCPP.dll", CallingConvention = CallingConvention.StdCall)]
private static extern void GetFrameInfo(
out int rawDepthWidth, out int rawDepthHeight, out int rawDepthType,
out int depthWidth, out int depthHeight, out int depthType,
out int colorWidth, out int colorHeight, out int colorType);

    // 图像属性
public int RawDepthWidth;
public int RawDepthHeight;
public MatType RawDepthType { get; private set; }

    public int DepthWidth;
public int DepthHeight;
public MatType DepthType { get; private set; }

    public int ColorWidth;
public int ColorHeight;
public MatType ColorType { get; private set; }

    // 非托管内存缓冲区
private IntPtr _rawDepthBuffer;
private IntPtr _depthBuffer;
private IntPtr _colorBuffer;
private int _rawDepthBufferSize;
private int _depthBufferSize;
private int _colorBufferSize;

    // 当前帧
public Mat RawDepthFrame { get; private set; }  // 原始深度图 (16位)
public Mat DepthFrame { get; private set; }     // 伪彩色深度图
public Mat ColorFrame { get; private set; }     // 彩色图

    // 更新事件
public event Action FramesUpdated;

    // 后台线程
private Thread _updateThread;
private bool _isRunning;

    public AstraCamera()
{
// 获取帧信息
GetFrameInfo(out RawDepthWidth, out RawDepthHeight, out int rawDepthType,
out DepthWidth, out DepthHeight, out int depthType,
out ColorWidth, out ColorHeight, out int colorType);

        // 转换为MatType
RawDepthType = (MatType)rawDepthType;
DepthType = (MatType)depthType;
ColorType = (MatType)colorType;

        // 计算缓冲区大小
_rawDepthBufferSize = RawDepthWidth * RawDepthHeight * sizeof(ushort); // 16位深度图
_depthBufferSize = DepthWidth * DepthHeight * 3; // 伪彩色图 (3通道)
_colorBufferSize = ColorWidth * ColorHeight * 3; // 彩色图 (3通道)

        // 分配非托管内存
_rawDepthBuffer = Marshal.AllocHGlobal(_rawDepthBufferSize);
_depthBuffer = Marshal.AllocHGlobal(_depthBufferSize);
_colorBuffer = Marshal.AllocHGlobal(_colorBufferSize);

        // 创建Mat对象
RawDepthFrame = new Mat(RawDepthHeight, RawDepthWidth, RawDepthType);
DepthFrame = new Mat(DepthHeight, DepthWidth, DepthType);
ColorFrame = new Mat(ColorHeight, ColorWidth, ColorType);

        // 启动相机
StartCamera();
_isRunning = true;

        // 启动更新线程
_updateThread = new Thread(UpdateFrames);
_updateThread.IsBackground = true;
_updateThread.Start();
}

    private void UpdateFrames()
{
while (_isRunning)
{
// 获取最新帧
if (GetLatestFrames(
_rawDepthBuffer, _rawDepthBufferSize,
_depthBuffer, _depthBufferSize,
_colorBuffer, _colorBufferSize))
{
// 将数据复制到Mat

RawDepthFrame = new Mat(RawDepthHeight, RawDepthWidth, RawDepthType, _rawDepthBuffer);
Cv2.Flip(RawDepthFrame,RawDepthFrame,FlipMode.Y);
DepthFrame = new Mat(DepthHeight, DepthWidth, DepthType, _depthBuffer);
Cv2.Flip(DepthFrame, DepthFrame, FlipMode.Y);
ColorFrame = new Mat(ColorHeight, ColorWidth, ColorType, _colorBuffer);
Cv2.Flip(ColorFrame, ColorFrame, FlipMode.Y);

                // 触发更新事件
FramesUpdated?.Invoke();
}

            Thread.Sleep(30); // 约30fps
}
}

    // 转换为Bitmap用于PictureBox显示
public Bitmap GetRawDepthBitmap()
{
// 将16位深度图转换为8位显示
using (Mat normalized = new Mat())
{
double min, max;
RawDepthFrame.MinMaxLoc(out min, out max);
RawDepthFrame.ConvertTo(normalized, MatType.CV_8UC1, 255.0 / (max - min), -min * 255.0 / (max - min));
return normalized.ToBitmap();
}
}

    public Bitmap GetDepthBitmap()
{
return DepthFrame.ToBitmap();
}

    public Bitmap GetColorBitmap()
{
return ColorFrame.ToBitmap();
}

    public void Dispose()
{
_isRunning = false;
_updateThread?.Join(1000);

        StopCamera();

        Marshal.FreeHGlobal(_rawDepthBuffer);
Marshal.FreeHGlobal(_depthBuffer);
Marshal.FreeHGlobal(_colorBuffer);

        RawDepthFrame?.Dispose();
DepthFrame?.Dispose();
ColorFrame?.Dispose();
}
}

/////////////AstraProCamera.cs////////////////

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Contexts;
using System.Windows.Forms;
using OpenCvSharp;

namespace WindowsFormsApp1
{
public partial class AstraProCamera : Form
{
public AstraProCamera()
{
InitializeComponent();

}
private AstraCamera _astraCamera;
private Mat depthMat;
private void InitializeCamera()
{
try
{
_astraCamera = new AstraCamera();
_astraCamera.FramesUpdated += UpdatePictureBoxes;
}
catch (Exception ex)
{
MessageBox.Show($"Camera initialization failed: {ex.Message}");
Close();
}
}

        private void UpdatePictureBoxes()
{
// 使用BeginInvoke确保UI线程更新
if (_depthPictureBox.InvokeRequired || _colorPictureBox.InvokeRequired)
{
BeginInvoke(new Action(UpdatePictureBoxes));
return;
}

try
{
// 更新原始深度图
depthMat = _astraCamera.RawDepthFrame.Clone();
using (var rawDepthBitmap = _astraCamera.GetRawDepthBitmap())
{
_rawDepthPictureBox.Image?.Dispose();
_rawDepthPictureBox.Image = new Bitmap(rawDepthBitmap);
}

                // 更新深度图
using (var depthBitmap = _astraCamera.GetDepthBitmap())
{
_depthPictureBox.Image?.Dispose();
_depthPictureBox.Image = new Bitmap(depthBitmap);
}

                // 更新彩色图
using (var colorBitmap = _astraCamera.GetColorBitmap())
{
_colorPictureBox.Image?.Dispose();
_colorPictureBox.Image = new Bitmap(colorBitmap);
//Bitmap flipped = FlipX(colorBitmap);
//_colorPictureBox.Image = flipped;
}
}
catch (Exception ex)
{
Console.WriteLine($"Update error: {ex.Message}");
}
}

        private void button3_Click(object sender, EventArgs e)
{
InitializeCamera();
}

        private void button4_Click(object sender, EventArgs e)
{
_astraCamera.Dispose();
}

        // 计算图片在PictureBox中的显示区域
private RectangleF GetImageDisplayRectangle(PictureBox picBox)
{
if (picBox.Image == null) return RectangleF.Empty;

            System.Drawing.Size imgSize = picBox.Image.Size;
System.Drawing.Size boxSize = picBox.ClientSize;

            switch (picBox.SizeMode)
{
case PictureBoxSizeMode.Normal:
case PictureBoxSizeMode.AutoSize:
return new RectangleF(0, 0, imgSize.Width, imgSize.Height);

                case PictureBoxSizeMode.StretchImage:
return new RectangleF(0, 0, boxSize.Width, boxSize.Height);

                case PictureBoxSizeMode.Zoom:
float ratio = Math.Min(
(float)boxSize.Width / imgSize.Width,
(float)boxSize.Height / imgSize.Height);

                    float width = imgSize.Width * ratio;
float height = imgSize.Height * ratio;
float x = (boxSize.Width - width) / 2;
float y = (boxSize.Height - height) / 2;

                    return new RectangleF(x, y, width, height);

                case PictureBoxSizeMode.CenterImage:
return new RectangleF(
(boxSize.Width - imgSize.Width) / 2,
(boxSize.Height - imgSize.Height) / 2,
imgSize.Width,
imgSize.Height);

                default:
return RectangleF.Empty;
}
}
private static int Clamp(int value, int min, int max)
{
return value < min ? min : (value > max ? max : value);
}

        private void _colorPictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (_depthPictureBox.Image == null) return;

            // 获取图片原始尺寸和PictureBox显示区域
int imgWidth = _depthPictureBox.Image.Width;
int imgHeight = _depthPictureBox.Image.Height;
RectangleF displayRect = GetImageDisplayRectangle(_depthPictureBox);

            // 检查点击是否在图片区域内
if (!displayRect.Contains(e.Location)) return;

            // 计算实际图片坐标
float scaleX = imgWidth / displayRect.Width;
float scaleY = imgHeight / displayRect.Height;

            int actualX = (int)((e.X - displayRect.X) * scaleX);
int actualY = (int)((e.Y - displayRect.Y) * scaleY);

            // 确保坐标在图片范围内
actualX = Clamp(actualX, 0, imgWidth - 1);
actualY = Clamp(actualY, 0, imgHeight - 1);
ushort depth = depthMat.At<ushort>(actualY, actualX);
listBox1.Items.Insert(0, $"{DateTime.Now.ToString()} 深度:{depth} mm");
label4.Text = $"({actualX}, {actualY}) 深度:{depth} mm";
}
}
}

http://www.dtcms.com/a/341983.html

相关文章:

  • AXI GPIO S——ZYNQ学习笔记10
  • 基于OpenCV的物体识别与计数
  • 基于SpringBoot的流浪动物领养管理系统【2026最新】
  • 【Android】悬浮窗清理
  • 政务分建用户体系下基于OAuth2.0概念单点登录实现方案
  • CT02-20.有效的括号(Java)
  • 【Linux | 网络】多路转接IO之select
  • Linux软件编程--网络操作
  • FreeRTOS,事件标注组创建,xEventGroupCreate、xEventGroupCreateStatic
  • 【自记】Power BI 中 CALCULATETABLE 第二个及后续参数支持的两种类型说明
  • 基于混合注意力网络和深度信念网络的鲁棒视频水印技术基础理论深度解析
  • 【世纪龙科技】汽车专业虚拟仿真实训基地建设-理实虚一体化实践
  • 【lucene】lucene常用查询一览
  • 一键去除 Windows 图标角标(小箭头、盾牌与双箭头)
  • JeeSite V5.13.0 发布,升级 Spring Boot 3.5,Cloud 2025,AI 1.0,Vite 7
  • ORACLE中如何批量重置序列
  • 如何保证数据库和缓存的一致性?
  • 强化学习中的重要性采样:跨分布复用样本的核心技术
  • 大模型0基础开发入门与实践:第8章 “大力出奇迹”的哲学:大语言模型的核心技术揭秘
  • 【世纪龙科技】汽车专业虚拟仿真实训基地建设方案
  • 嵌入式软件典型架构:层次化模式 vs 递归模式
  • Java Main无法初始化主类的原因与解决方法(VsCode工具)
  • 【Java后端】Spring Boot 实现请求设备来源统计与UA解析全攻略
  • 智慧工厂的 “隐形大脑”:边缘计算网关凭什么重构设备连接新逻辑?
  • 编程刷题-资料分发1 图论/DFS
  • Kotlin-基础语法练习二
  • Android面试指南(四)
  • [新启航]机械深孔加工质控:新启航方案用激光频率梳破解 130mm 深度遮挡瓶颈
  • 闲聊汽车芯片的信息安全需求和功能
  • C# NX二次开发:反向控件和组控件详解