Segformer模型的平台部署和项目应用
最近因为离职太忙了之前的很多内容没有更新,离开BYD进入新的环境中成长。
本文包含了Segformer的网络结构重构后如何部署到算法平台中方便标注训练推理的过程,以及如何应用到项目中(BYD最后一个项目:异物检测系统)
C++做Segformer的推理加速,Python做Segformer平台部署,C#调用算法接口实现项目开发。
前言
本章将前面的segfomer网络结构的学习和重构、segformer双显卡推理部署、算法平台开发、c#使用pythonnet调用python多进程项目、windows forms图形界面的开发,c++加cuda对模型推理加速的所有内容贯穿起来完成一个大型项目的整个开发起来。
一、算法平台的开发
1、在现有的算法平台添加该深度学习模块,避免后面的项目用到该模型重复造轮子,增加标注训练功能和推理工具
2、推理功能接口提供pytorch\onnx\tensort三种推理方式
3、项目逻辑功能:实现相机和电机和机械手的控制
4、图片算法逻辑功能实现,输出分割区域
二、c++和cuda 实现segformer推理加速
c++推理代码
int load_model(std::string modelPath, int cudaDevice,int width,int height,int batchSize, int classNum)
{
cudaSetDevice(cudaDevice);
/*******************************************************************************************************
一、读取本地模型
*******************************************************************************************************/
//引擎文件绝对地址
//std::string engine_name = R"(D:\Drivers\TensorRT-8.5.3.1\bin\models_YW240619.trt)"; //D:\Drivers\TensorRT - 8.5.3.1\bin\models_YW240619.trt
std::string engine_name = modelPath;//R"(D:\model\models.trt)"; //D:\Drivers\TensorRT - 8.5.3.1\bin\models_YW240619.trt F:\model\models_YW240619.trt
//该文件保存了模型的所有信息以及电脑的配置信息,因此该模型文件不支持在不同电脑上使用。
std::ifstream file(engine_name, std::ios::binary);
if (!file.good())
std::cerr << "文件无法打开,请确定文件是否可用!" << std::endl;
size_t size = 0;
file.seekg(0, file.end); // 将读指针从文件末尾开始移动0个字节
size = file.tellg(); // 返回读指针的位置,此时读指针的位置就是文件的字节数
file.seekg(0, file.beg); // 将读指针从文件开头开始移动0个字节
char* modelStream = new char[size];
//将模型文件信息读取到内存中
file.read(modelStream, size);
file.close();
/*******************************************************************************************************
二、构建引擎
*******************************************************************************************************/
//创建反序列化引擎并使用日志记录接口---使用全局 TensorRT API 方法用于创建 iRuntime 类型的对象
this->runtime = nvinfer1::createInferRuntime(glogger);
//调用反序列化引擎来创建推理引擎,即反序列化获取模型(modelStream)中的引擎---通过调用运行时方法 deserializeCudaEngine() 来创建引擎
this->engine = runtime->deserializeCudaEngine(modelStream, size, nullptr);
//判断推理引擎是否为空
if (engine == NULL) {
printf("modelStream 为空,模型加载失败");
delete[] modelStream;
return 0;
}
else
{
//创建推理上下文---为后面进行模型推理的类
this->context = this->engine->createExecutionContext();
delete[] modelStream;
//创建GPU缓存区,使用这些索引,创建buffers指向 GPU 上输入和输出缓冲区
std::string inputBindingname = this->engine->getBindingName(inputBindingindex);
std::string outputBindingname = this->engine->getBindingName(outputBindingindex);
this->inputIndex = this->engine->getBindingIndex(inputBindingname.c_str());
this->outputIndex = this->engine->getBindingIndex(outputBindingname.c_str());
inputDims.nbDims = 4;
inputDims.d[0] = 1;
inputDims.d[1] = 3;
inputDims.d[2] = 512;
inputDims.d[3] = 512;
this->context->setBindingDimensions(this->inputIndex, inputDims);
//创建GPU显存输入缓存区---输入推理数据(GPU)
cudaMalloc(&buffers[this->inputIndex], (3 * 512 * 512) * sizeof(float));
//创建GPU显存输出缓存区--输出推理结果数据(GPU)
cudaMalloc(&buffers[this->outputIndex], (classNum * 512 * 512) * sizeof(float));
cudaMalloc((void**)&srcDevData, sizeof(uchar) * (width * height * 3 * batchSize));
cudaMalloc((void**)&srcResizeData, sizeof(uchar) * (512 * 512 * 3 * batchSize));
//使用CUDA流数据加载,将结果数据(CPU内存)输出到float变量中
cudaMalloc((void**)&post_device_data, sizeof(uchar) * (512 * 512 * batchSize));
cudaMalloc((void**)&post_resize_data, sizeof(uchar) * (width * height * batchSize));
scr = new uchar[batchSize * 3 * width * height];
post_host_data = new uchar[width * height * batchSize];
return 1;
}
}
cuda核函数代码
__global__ void process(const uchar* srcData, float* tgtData, const int h, const int w)
{
int ix = threadIdx.x + blockIdx.x * blockDim.x;
int iy = threadIdx.y + blockIdx.y * blockDim.y;
int idx = ix + iy * w;
int idx3 = idx * 3;
if (ix < w && iy < h)
{
tgtData[idx] = ((float)srcData[idx3 + 2] - 123.675f) / 58.359; // R pixel
tgtData[idx + h * w] = ((float)srcData[idx3 + 1] -116.28)/ 57.12; // G pixel
tgtData[idx + h * w * 2] = ((float)srcData[idx3] - 103.53) / 57.375; // B pixel
}
//if (idx < 1)
//{
// printf("idx:%d float_a: %f float_b :%f float_c :%f \n", idx, tgtData[idx], tgtData[idx + h * w], tgtData[idx + h * w * 2]);
//}
}
__global__ void process(const uchar* srcData, float* tgtData, const int h, const int w)
{
int ix = threadIdx.x + blockIdx.x * blockDim.x;
int iy = threadIdx.y + blockIdx.y * blockDim.y;
int idx = ix + iy * w;
int idx3 = idx * 3;
if (ix < w && iy < h)
{
tgtData[idx] = ((float)srcData[idx3 + 2] - 123.675f) / 58.359; // R pixel
tgtData[idx + h * w] = ((float)srcData[idx3 + 1] -116.28)/ 57.12; // G pixel
tgtData[idx + h * w * 2] = ((float)srcData[idx3] - 103.53) / 57.375; // B pixel
}
//if (idx < 1)
//{
// printf("idx:%d float_a: %f float_b :%f float_c :%f \n", idx, tgtData[idx], tgtData[idx + h * w], tgtData[idx + h * w * 2]);
//}
}
__global__ void resize(const uchar* srcData, const int srcH, const int srcW, uchar* tgtData, const int tgtH, const int tgtW)
{
//灰色图片做
int ix = threadIdx.x + blockIdx.x * blockDim.x;
int iy = threadIdx.y + blockIdx.y * blockDim.y;
int idx = ix + iy * tgtW;
int idx3 = idx;
float scaleY = (float)tgtH / (float)srcH;
float scaleX = (float)tgtW / (float)srcW;
// (ix,iy)为目标图像坐标
// (before_x,before_y)原图坐标
float beforeX = float(ix + 0.5) / scaleX - 0.5;
float beforeY = float(iy + 0.5) / scaleY - 0.5;
// 原图像坐标四个相邻点
// 获得变换前最近的四个顶点,取整
int topY = static_cast<int>(beforeY);
int bottomY = topY + 1;
int leftX = static_cast<int>(beforeX);
int rightX = leftX + 1;
//计算变换前坐标的小数部分
float u = beforeX - leftX;
float v = beforeY - topY;
if (ix < tgtW && iy < tgtH)
{
// 如果计算的原始图像的像素大于真实原始图像尺寸
if (topY >= srcH && leftX >= srcW) //右下角
{
for (int k = 0; k < 1; k++)
{
//tgtData[idx3 + k] = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k];
tgtData[idx3 + k] = srcData[(leftX + topY * srcW) * 3 + k];
//tgtData[idx3 + k] = 255.0;
}
}
else if (topY >= srcH ) // 最后一行
{
for (int k = 0; k < 1; k++)
{
/* tgtData[idx3 + k]
= (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k]
+ (u) * (1. - v) * srcData[(rightX + topY * srcW) * 3 + k];*/
if ((1. - u) >= 0.5 )
tgtData[idx3 + k] = srcData[(leftX + topY * srcW) * 3 + k];
else if ((u) > 0.5)
tgtData[idx3 + k] = srcData[(rightX + topY * srcW) * 3 + k];
else
tgtData[idx3 + k] = srcData[(rightX + topY * srcW) * 3 + k];
}
}
else if (leftX >= srcW) // 最后一列
{
for (int k = 0; k < 1; k++)
{
/*tgtData[idx3 + k]
= (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) * 3 + k]
+ (1. - u) * (v)*srcData[(leftX + bottomY * srcW) * 3 + k];*/
if ((1. - v) >= 0.5)
tgtData[idx3 + k] = srcData[(leftX + topY * srcW) * 3 + k];
else if ( (v) > 0.5)
tgtData[idx3 + k] = srcData[(leftX + bottomY * srcW) * 3 + k];
else
tgtData[idx3 + k] = srcData[(leftX + bottomY * srcW) * 3 + k];
}
}
else // 非最后一行或最后一列情况
{
for (int k = 0; k < 1; k++)
{
if ((1. - u) >=0.5 && (1. - v) >=0.5)
tgtData[idx3 + k] = srcData[(leftX + topY * srcW) + k];
else if ((u)>0.5 && (1. - v) > 0.5)
tgtData[idx3 + k] = srcData[(rightX + topY * srcW) + k];
else if ((1. - u)>0.5 && (v) > 0.5)
tgtData[idx3 + k] = srcData[(leftX + bottomY * srcW) + k];
else if (u >0.5&& v > 0.5)
tgtData[idx3 + k] =srcData[(rightX + bottomY * srcW) + k];
else
tgtData[idx3 + k] = srcData[(rightX + bottomY * srcW) + k];
/* tgtData[idx3 + k] = (1. - u) * (1. - v) * srcData[(leftX + topY * srcW) + k]
+ (u) * (1. - v) * srcData[(rightX + topY * srcW) + k]
+ (1. - u) * (v)*srcData[(leftX + bottomY * srcW) + k]
+ u * v * srcData[(rightX + bottomY * srcW) + k];*/
}
}
}
}
三、C#利用pythonnet通过序列化文件调用python多进程算法平台开发好的算法
序列化文件利用pickle库储存有所有调用算法的组合链接方式和参数,python可直接调用运行、c++可以转json读取算法参数和链接方式直接运行和生成组合后的dll、那这里为啥这里要用C#来调用呢,主要是方便其他同事后续的修改(主要用C#),所里C#调用的方式相对复杂一点(涉及到python多进程的处理)
c#图形界面可以看前面的内容
调用代码:
/// <summary>
/// 初始化VisionTool的运行pyhton环境
/// </summary>
public static void InitVisionTool()
{
try
{
//初始化python引擎
var pythonPathBuilder = new StringBuilder();
detects.Add(new Detect("CCD1"));
detects.Add(new Detect("CCD2"));
pythonPathBuilder.Append(@"D:\AllLargeProject\Visontool\windows版本VT\vt_windows_software_1.3.7.0;");
pythonPathBuilder.Append(@"D:\AllLargeProject\Visontool\windows版本VT\vt_windows_software_1.3.7.0\visiontool\core\common\datatype;");
pythonPathBuilder.Append(@"C:\Users\xbb1995\anaconda3\envs\visiontool\Lib\encodings;");
pythonPathBuilder.Append(@"C:\Users\xbb1995\anaconda3\envs\visiontool\xbb\visiontool\DLLs;");
pythonPathBuilder.Append(@"C:\Users\xbb1995\anaconda3\envs\visiontool;");
pythonPathBuilder.Append(@"C:\Users\xbb1995\anaconda3\envs\visiontool\Lib;");
pythonPathBuilder.Append(@"C:\Users\xbb1995\anaconda3\envs\visiontool\Lib\site-packages;");
//pythonPathBuilder.Append(@"D:\xbb\VT\vt_windows_software_1.3.7.0\torch\lib;");
PythonEngine.PythonPath = pythonPathBuilder.ToString();
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
using (Py.GIL())
{
unsafe
{
//初始化相关库
pickle = Py.Import("pickle");//pickle库,导入模块,导出模块
cv2 = Py.Import("cv2");
numpy = Py.Import("numpy");
ctypes = Py.Import("ctypes");
ImageTypeConvert = Py.Import("ImageTypeConvert");//图片格式转换
builtins = Py.Import("builtins");
VisionTool_Project = pickle.load(builtins.open(@"D:\TEMP_File\project11.vtpro", "rb"));
//VisionTool_Project = pickle.load(builtins.open(@"D:\向斌斌\产线程序\装配\潍柴\QH2.0\异物检测\程序\程序\异物检测程序(重新最新版本)\VisionProgram\bin\x64\Debug\Vtpro\project.vtpro", "rb"));//模块:所有深度学习模型、机器学习模块的参数数组模型
//VisionTool_Project = pickle.load(builtins.open(@"D:\xbb(勿删)\vtpro\project_deep.vtpro", "rb"));//模块:所有深度学习模型、机器学习模块的参数数组模型
}
}
}
catch (Exception ex)
{
throw new Exception("python环境参数配置有误!VisionTool初始化失败!请检查参数配置!", ex);
}
}
/// <summary>
/// VisionTool运行(处理手动传入图片)
/// </summary>
public static VisionToolRunInfo RunVisionToolFlow1(int Index, WuKongData img_intptr)
{
try
{
unsafe
{
using (Py.GIL())
{
if (img_intptr.Images.Count == 1)//手动测试图片
{
//1、传入VisionTool图片
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image1 =
// IntPtr2GrayImage(img_intptr.Images["1"].datas, img_intptr.Images["1"].nWidth, img_intptr.Images["1"].nHeight);
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image2 =
// IntPtr2GrayImage(img_intptr.Images["1"].datas, img_intptr.Images["1"].nWidth, img_intptr.Images["1"].nHeight);
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image3 =
// IntPtr2GrayImage(img_intptr.Images["1"].datas, img_intptr.Images["1"].nWidth, img_intptr.Images["1"].nHeight);
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image4 =
// IntPtr2GrayImage(img_intptr.Images["1"].datas, img_intptr.Images["1"].nWidth, img_intptr.Images["1"].nHeight);
//2、运行VisonTool算法流程
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").run();
//3、获得VisionTool检测结果
//vt_result.rectangleNum = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs.len;
//vt_result.YiWuNum = vt_result.rectangleNum;
//if (vt_result.rectangleNum > 10)
// vt_result.rectangleNum = 10;
//float area = 0;
//for (int i = 0; i < vt_result.rectangleNum; i++)
//{
// vt_result.rectanglenNdarry[0 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.x
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.x;
// vt_result.rectanglenNdarry[1 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.y
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.y;
// vt_result.rectanglenNdarry[2 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.width;
// vt_result.rectanglenNdarry[3 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.height;
// float cur_area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
// if (area < cur_area)
// area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
//}
//vt_result.YiWuArea = area;
return vt_result;
}
else if (img_intptr.Images.Count == 2)//手动测试图片
{
//1、传入VisionTool图片
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").input_image = IntPtr2ColorImage(img_intptr.Images["1"].datas, img_intptr.Images["1"].nWidth, img_intptr.Images["1"].nHeight);
2、运行VisonTool算法流程In CogImage8Grey((Bitmap)data.Images[j + "_" + i])
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").run();
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").input_image = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").result.image;
//VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").run();
3、获得VisionTool检测结果
//vt_result.rectangleNum = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs.len;
//vt_result.YiWuNum = vt_result.rectangleNum;
//if (vt_result.rectangleNum > 10)
// vt_result.rectangleNum = 10;
//float area = 0;
//for (int i = 0; i < vt_result.rectangleNum; i++)
//{
// vt_result.rectanglenNdarry[0 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.x
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.x;
// vt_result.rectanglenNdarry[1 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.y
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.y;
// vt_result.rectanglenNdarry[2 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.width;
// vt_result.rectanglenNdarry[3 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.height;
// float cur_area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
// if (area < cur_area)
// area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
//}
//vt_result.YiWuArea = area;
return vt_result;
}
else if (img_intptr.Images.Count == 12)//自动检测
{
//1、传入VisionTool图片
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image1 =
Bitmap2GrayImage(img_intptr.Images["1_0"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image2 =
Bitmap2GrayImage(img_intptr.Images["2_0"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image3 =
Bitmap2GrayImage(img_intptr.Images["3_0"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_1").input_image4 =
Bitmap2GrayImage(img_intptr.Images["4_0"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_2").input_image1 =
Bitmap2GrayImage(img_intptr.Images["1_1"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_2").input_image2 =
Bitmap2GrayImage(img_intptr.Images["2_1"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_2").input_image3 =
Bitmap2GrayImage(img_intptr.Images["3_1"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_2").input_image4 =
Bitmap2GrayImage(img_intptr.Images["4_1"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_3").input_image1 =
Bitmap2GrayImage(img_intptr.Images["1_2"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_3").input_image2 =
Bitmap2GrayImage(img_intptr.Images["2_2"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_3").input_image3 =
Bitmap2GrayImage(img_intptr.Images["3_2"]);
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImageSplicingTool_3").input_image4 =
Bitmap2GrayImage(img_intptr.Images["4_2"]);
//2、运行VisonTool算法流程In CogImage8Grey((Bitmap)data.Images[j + "_" + i])
VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").run();
//3、获得VisionTool检测结果
outputColorImage = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImgThreshMergeTool_1").result.output_merge_image;
//vt_result.OutImage = ColorImage2Bitmap(outputColorImage, img_intptr.Images["4_2"].Height*4, img_intptr.Images["4_2"].Width);
vt_result.OutImage.Save("D:\\1.bmp", ImageFormat.Bmp);
image = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__2").getTool("ImgThreshMergeTool_1").input_gchannel_image;
int a = image[2300][2400];
//vt_result.rectangleNum = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs.len;
//vt_result.YiWuNum = vt_result.rectangleNum;
if (vt_result.rectangleNum > 10)
vt_result.rectangleNum = 10;
//float area = 0;
//for (int i = 0; i < vt_result.rectangleNum; i++)
//{
// vt_result.rectanglenNdarry[0 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.x
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.x;
// vt_result.rectanglenNdarry[1 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.y
// + VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("SegformerPredictTool_1").region.y;
// vt_result.rectanglenNdarry[2 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.width;
// vt_result.rectanglenNdarry[3 + i * 4] = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].bounding_box.height;
// float cur_area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
// int leibei = image[vt_result.rectanglenNdarry[1 + i * 4] + vt_result.rectanglenNdarry[3 + i * 4] / 2][vt_result.rectanglenNdarry[0 + i * 4] + vt_result.rectanglenNdarry[2 + i * 4] / 2];
// if (area < cur_area)
// area = VisionTool_Project.getTask($"task{Index + 1}").getBlock("Vision__3").getTool("BlobTool_2").result.blobs[i].area;
//}
//vt_result.YiWuArea = area;
return vt_result;
}
}
return null;
}
}
catch (Exception ex)
{
return vt_result;
//throw new Exception($"运行VisionTool算法失败:{ex.Message}");
//return false;
}
}