Unity调用C++ OpenCV DLL处理视频流后Color32数组为空求助
问题排查与修复方案
核心错误点
- 交互签名不匹配:C#侧
ref Color32[]传递的是托管数组首元素的指针,对应C++侧应为一级指针Color32* rawImage_Inbound,原代码用二级指针导致内存地址读取错误,写入位置和C#数组不对应 - cv::Mat构造参数错误:
cv::Mat构造顺序为Mat(行高, 列宽, 类型, 数据指针),原代码写反了宽高,且绑定的CV_8UC4四通道类型和后续处理用的三通道Vec3b不匹配 - 通道顺序不兼容:OpenCV默认从摄像头读取的帧为BGR三通道格式,Unity的
Color32为RGBA四通道顺序,直接赋值会出现颜色错误、内存越界 - 像素读写类型不匹配:四通道
Mat读写像素应使用Vec4b而非Vec3b,否则会出现每个像素的内存偏移错误,导致颜色一致的问题
修复后代码
C# 侧代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; [RequireComponent(typeof(SpriteRenderer))] public class SpriteMaker : MonoBehaviour { SpriteRenderer rend; private Color32[] rawImage; private Texture2D tex; internal static class OpenCVInterop { [DllImport("ImageProcessor_dll")] internal static extern void Init(); // 传入数组首指针,同时传宽高避免硬编码错误 [DllImport("ImageProcessor_dll")] internal static extern void ImageProcessing([In, Out] Color32[] rawImage, int width, int height); } void Start() { OpenCVInterop.Init(); int width = 640; int height = 240; rawImage = new Color32[width * height]; tex = new Texture2D(width, height, TextureFormat.RGBA32, false); rend = GetComponent<SpriteRenderer>(); } void Update() { // 逐帧更新图像 OpenCVInterop.ImageProcessing(rawImage, tex.width, tex.height); tex.SetPixels32(rawImage); tex.Apply(); if (rend.sprite == null) { Sprite newSprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), Vector2.one * 0.5f); rend.sprite = newSprite; } } }
C++ 侧代码
#include "pch.h" #include <iostream> #include "opencv2/opencv.hpp" #include "opencv2/highgui.hpp" using namespace cv; using namespace std; VideoCapture _capture; Mat frameBuffer; struct Color32 { uchar red; uchar green; uchar blue; uchar alpha; }; extern "C" void __declspec(dllexport) __stdcall Init() { _capture.open(0); if (_capture.isOpened()) { std::cout << "VideoCapture is opened" << std::endl; } } extern "C" void __declspec(dllexport) __stdcall ImageProcessing(Color32* rawImage_Inbound, int width, int height) { // 先读取摄像头帧到三通道缓冲区 _capture >> frameBuffer; // 缩放到目标尺寸 resize(frameBuffer, frameBuffer, Size(width, height)); // 绑定C#传入的内存为四通道Mat Mat rawImage(height, width, CV_8UC4, rawImage_Inbound); // 转换BGR到RGBA,同时写入到绑定的C#数组内存 cvtColor(frameBuffer, rawImage, COLOR_BGR2RGBA); // 图像处理逻辑,现在用Vec4b读写四通道像素 for (int y = 0; y < rawImage.rows; y++) { for (int x = 0; x < rawImage.cols; x++) { Vec4b& color = rawImage.at<Vec4b>(Point(x, y)); float colorB = frameBuffer.at<Vec3b>(Point(x, y))[0]; float colorR = frameBuffer.at<Vec3b>(Point(x, y))[2]; float ID = (colorR - colorB) / (colorR + colorB + 0.001f); // 加小值避免除0 if (ID >= -1 && ID < 0) { color[0] = 128; // R color[1] = 128; // G color[2] = 128; // B color[3] = 255; // A } else { color[0] = 255; // R color[1] = 0; // G color[2] = 0; // B color[3] = 255; // A } } } imshow("frame", rawImage); waitKey(1); // 必须加waitKey才能正常显示OpenCV窗口 }
补充注意点
- C#侧给DLL传数组时加上
[In, Out]属性,明确告知运行时该数组需要双向数据同步 - OpenCV操作后必须调用
waitKey(1)才能刷新窗口,同时处理UI事件 - 摄像头默认输出尺寸和你设定的640*240不一致时,必须先做resize再处理,避免内存越界
内容的提问来源于stack exchange,提问作者Lennart
相关产品推荐
相关产品推荐

