上次博文有个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;
}