上次博文有个unity和opencv传递图片的问题,终于找个时间解决了。过程是一把心酸一把泪,给unity打包过c++dll的同志肯定能明白其中的痛苦,网上看了很多文章,都只是笼统的一说,没有什么特别具体的解决方案,甚至很多解决方案很不负责任,动不动就内存溢出,泄露,越界。当然也可能人家只传一张图片,没有这么强的实时性。还有unity和opencv的图片对照真是坑上加坑,具体有多坑,谁踩谁知道,手动滑稽 不bb,上代码
首先看unity声明
lanedet,函数是具体道路识别,数据处理的函数
setcolor,设置处理后图片的颜色,尽量不要用这个函数,我还没测过,实在没时间弄
flip,图片翻转函数,就是opencv上那个,用到这个函数的原因上个博文也说到了,把它重新封装是因为快速处理。
CreatImage,是到指定内存处理图片,并确定处理多大的内存,确定图片的通道数
[DllImport("LaneIdentificationImg")]
extern static int lanedet();
[DllImport("LaneIdentificationImg")]
extern static int SetColor(int[] col);
[DllImport("LaneIdentificationImg")]
extern static int Flip(int flip);
[DllImport("LaneIdentificationImg")]
extern static int CreatImage(IntPtr data, int Width, int Height, int Aisle);
unity的操作。
为了不影响主线程,只能放在协程中运行,我的项目还是比较大的,优化之后不带处理图片这部分跑场景,大概60多fps,将图片处理放在fiexdupdate,只能跑40fps左右,放在携程中却几乎没什么影响,场景运行速度还是60左右。同时ui上显示的图像也非常顺畅,没有特别明显的卡顿。
WritePNGToLocad 是否将图片生成到本地。不想用可以把if中的代码块删了,没啥用 还会拖慢进度,建议不要声明这个宏就行
IEnumerator OutputRt()
{
while (true)
{
rt = camera.targetTexture;
RenderTexture.active = rt;
png.ReadPixels(new Rect(0, 0, imgsize.x, imgsize.y), 0, 0);
yield return new WaitForSeconds(0.01f);//上面操作时取出相机看到的图片
#if WritePNGToLocad
byte[] dataBytes = png.EncodeToPNG();
#else
pixels = png.GetPixels32();//对图片进行像素点处理
pixelHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned);
pixelPointer = pixelHandle.AddrOfPinnedObject();//将内存中的图像数据开辟为一块GC不释放的空间,
#endif
yield return new WaitForSeconds(0.01f);
CreatImage(pixelPointer, (int)imgsize.x, (int)imgsize.y, 4);//上篇文章中用opencv制作的dll中的函数
yield return new WaitForSeconds(0.01f);
Flip(0);//dll中的函数
yield return new WaitForSeconds(0.01f);
float mark = lanedet();//dll中的函数
//---------没啥用可去除,我的项目中的东西--------
mark /= 100;
//LKAState = Mathf.Lerp(LKAState, mark, 0.05f);
LKAState = mark;
//---------------------------------------------
yield return new WaitForSeconds(0.01f);
Flip(0);//dll中函数翻转用的
cppixels = pixels;//没啥用可去除
yield return new WaitForSeconds(0.01f);
showpng.SetPixels32(cppixels);//将处理后的像素点重新设置回图片上
showpng.Apply();//此处大坑,切记设置完图片需要此函数确定
yield return new WaitForSeconds(0.01f);
pixelHandle.Free();//释放内存区域
#if WritePNGToLocad
string strSaveFile = "D:\\hehe" + ".png";
FileStream fs = File.Open(strSaveFile, FileMode.OpenOrCreate);
fs.Write(dataBytes, 0, dataBytes.Length);
fs.Flush();
fs.Close();
png = null;
#endif
RenderTexture.active = null;
}
}
opencv的声明
没啥好说的,和上面的声明对照
extern "C" __declspec(dllexport) int lanedet();
extern "C" __declspec(dllexport) int SetColor(int col[]);
extern "C" __declspec(dllexport) BYTE * Flip(int flip);
extern "C" __declspec(dllexport) int CreatImage(BYTE * data, int Width, int Height, int Aisle);
opencv处理函数
没啥说的首先将通道对照好了,还有别问我为啥只有4通道能用,为啥rgb24不行
上篇博文中的代码照着这个改改就ok了
static cv::Mat frame;//(900, 1440, CV_8UC4);
int CreatImage(BYTE * data,int Width,int Height ,int Aisle)
{
using namespace cv;
switch (Aisle)
{
case 1:
frame = Mat(Height, Width, CV_8UC1);
break;
case 3:
frame = Mat(Height, Width, CV_8UC3);
break;
case 4:
frame = Mat(Height, Width, CV_8UC4);
break;
default:
break;
}
BYTE * mark = data;//首先取首指针
//strcpy_s((char *)frame.data, Height*Width*Aisle+1, (char *)mark);//该方法已尝试不能用,会丢数据,想想字符串结尾,坑
frame.data = data;//将数据给到图片,其实就是直接将传递过来的内存块挂载到我们声明的图片的数据区
cv::cvtColor(frame, frame, cv::COLOR_RGBA2GRAY, Aisle);//图片灰度处理
cv::cvtColor(frame, frame, cv::COLOR_GRAY2RGBA, Aisle);//再彩色处理,我的项目需要传回去一张灰色的图。直接丢数据,这操作骚不骚
for (size_t i = 0; i < Height*Width*Aisle + 1; i++)
{
mark[i] = frame.data[i];
}
//for循环是用来防止数据丢失
//strcpy_s((char *)mark, Height*Width*Aisle + 1, (char *)frame.data);
//*mark = *frame.data;
//delete frame.data;
frame.data = mark;
return 1;
}
BYTE * Flip(int flip){
cv::flip(frame, frame,flip);
return frame.data;
}
int SetColor(int col[]){
for (size_t i = 0; i < 6; i++)
{
color[i] = col[6];
}
return 1;
}