rv1126物体检测 rkmedia、opencv……

news2024/9/21 2:37:22

整体码流流向:

因此代码也分为这几部分:

VI:采集视频 配置视频采集信息

模型推理线程:获取VI码流、载入模型、进行模型推理、保存推理结果

画框线程:获取VI码流、获取推理结果、显示推理结果、输出码流到VENC

VENC:编码

RTSP:获取VENC编码码流、传输

代码分析:

main函数:

从总体来看 main函数还是很简单的,首先创建了一个rknn队列,然后配置摄像头节点、初始化rtsp(默认端口554)(rtsp在主函数进行创建 rtsp 实例、创建 rtsp 接口、设置 rtsp 传输属性、同步 rtsp 时间戳然后在真正推流时候即线程中更新视频流设置并驱动事件即可)

后面就是对VI以及VENC的一些配置,然后创建了三个线程,这三个线程的创建是很重要的

int main(int argc, char *argv[])
{

  rknn_queue = new RKNN_QUEUE();

  RK_MPI_SYS_Init();
  RK_U32 u32Width = 1920;
  RK_U32 u32Height = 1080;
  RK_CHAR *pDeviceName_01 = "rkispp_scale0";
  RK_CHAR *pDeviceName_02 = "rkispp_scale1";
  RK_CHAR *pDeviceName_03 = "rkispp_m_bypass";
  RK_CHAR *pOutPath = "test_output.h264";
  RK_CHAR *pIqfilesPath = NULL;
  CODEC_TYPE_E enCodecType = RK_CODEC_TYPE_H264;
  RK_CHAR *pCodecName = "H264";
  RK_S32 s32CamId = 0;
  RK_U32 u32BufCnt = 3;
  int ret;

  printf("#Device: %s\n", pDeviceName_01);
  printf("#CodecName:%s\n", pCodecName);
  printf("#Resolution: %dx%d\n", u32Width, u32Height);
  printf("#Frame Count to save: %d\n", g_s32FrameCnt);
  printf("#Output Path: %s\n", pOutPath);
  printf("#CameraIdx: %d\n\n", s32CamId);


  g_rtsplive = create_rtsp_demo(554);
  g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/main_stream");

  rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
  rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());

#if 1
  VI_CHN_ATTR_S vi_chn_attr;
  vi_chn_attr.pcVideoNode = pDeviceName_01;
  vi_chn_attr.u32BufCnt = u32BufCnt;
  vi_chn_attr.u32Width = u32Width;
  vi_chn_attr.u32Height = u32Height;
  vi_chn_attr.enPixFmt = IMAGE_TYPE_NV12;
  vi_chn_attr.enBufType = VI_CHN_BUF_TYPE_MMAP;
  vi_chn_attr.enWorkMode = VI_WORK_MODE_NORMAL;
  ret = RK_MPI_VI_SetChnAttr(s32CamId, 0, &vi_chn_attr);
  ret |= RK_MPI_VI_EnableChn(s32CamId, 0);
  if (ret)
  {
    printf("ERROR: create rkisp0 VI[0] error! ret=%d\n", ret);
    return 0;
  }
#endif

  VENC_CHN_ATTR_S venc_chn_attr;
  memset(&venc_chn_attr, 0, sizeof(VENC_CHN_ATTR_S));
  venc_chn_attr.stVencAttr.u32PicWidth = 1920;
  venc_chn_attr.stVencAttr.u32PicHeight = 1080;
  venc_chn_attr.stVencAttr.u32VirWidth = 1920;
  venc_chn_attr.stVencAttr.u32VirHeight = 1080;
  venc_chn_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
  venc_chn_attr.stVencAttr.enType = RK_CODEC_TYPE_H264;
  venc_chn_attr.stVencAttr.u32Profile = 66;
  venc_chn_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
  venc_chn_attr.stRcAttr.stH264Cbr.u32Gop = 30;
  venc_chn_attr.stRcAttr.stH264Cbr.u32BitRate = 1920 * 1080 * 3;
  venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
  venc_chn_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 25;
  venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateDen = 1;
  venc_chn_attr.stRcAttr.stH264Cbr.u32SrcFrameRateNum = 25;
  ret = RK_MPI_VENC_CreateChn(0, &venc_chn_attr);
  if (ret)
  {
     printf("ERROR: Create venc failed!\n");
     exit(0);
  }


  ret = RK_MPI_VI_StartStream(s32CamId, 0);
  if (ret)
  {
    printf("Start VI[0] failed! ret=%d\n", ret);
    return -1;
  }


  //pthread_t vi_pid;
  pthread_t rknn_pid;
  pthread_t process_pid;
  pthread_t rtsp_pid;

  pthread_create(&rknn_pid, NULL, rknn_vi_thread, NULL);
  pthread_create(&process_pid, NULL, rknn_vi_rknn_thread, NULL);
  pthread_create(&rtsp_pid, NULL, rknn_venc_rtsp_thread, NULL);

  printf("%s initial finish\n", __func__);
  signal(SIGINT, sigterm_handler);

  while (!quit)
  {
    usleep(500000);
  }

  if (g_output_file)
    fclose(g_output_file);

  printf("%s exit!\n", __func__);

  // destroy venc before vi
  ret = RK_MPI_VENC_DestroyChn(0);
  if (ret)
  {
    printf("ERROR: Destroy VENC[0] error! ret=%d\n", ret);
    return 0;
  }
  // destroy vi

  ret = RK_MPI_VI_DisableChn(s32CamId, 0);
  if (ret)
  {
    printf("ERROR: Destroy VI[0] error! ret=%d\n", ret);
    return 0;
  }

  ret = RK_MPI_RGA_DestroyChn(0);
  if (ret)
  {
     printf("ERROR: Destroy RGA[0] error! ret=%d\n", ret);
     return 0;
  }

  if (pIqfilesPath)
  {
#if 0
    SAMPLE_COMM_ISP_Stop(s32CamId);
#endif
  }
}

线程1:

        首先pthread_detach使得子线程与主线程分离且结束时候自动回收资源

        然后计算出比率比如vi采集的宽为1000,而模型推理输入的图片宽为100那么比例就是1000/10=10 那么model10的位置就是vi100的位置了

        下面进入循环,首先从VI通道0阻塞的方式获取码流,然后把该码流拷贝到&Media_Buffer[0]所指向的位置即该数组的首地址

        下面使用主函数创建的rknn队列(rknn_queue查看识别结果),并使用opencv创建了一个方框其高为1080宽为1920 CV_8UC1: 8位无符号单通道图像(灰度图像),显示在VI图像上

        从获取的结果中根据识别到的物体数量循环,如果置信度小于0.7则退出,如果大于0.7,分别转换得到1920*1080图片的坐标

        在原图mb上进行画框,并在框框对应位置载入文字(识别结果)

        最后将处理后的数据传输给VENC并销毁资源

        总结一下画框和文字显示,画框其实是直接在图片上画框了,但是文字显示其实是先在图像上创建了一个和图片一样大的框框(矩阵),然后对这个框框的对应位置进行了文字显示,由于框框在图片上因此文字就显示在了图片上

//这个线程用来绘制框框
void *rknn_vi_thread(void *args)
{
  pthread_detach(pthread_self());
  int frame_id = 0;
  MEDIA_BUFFER mb = NULL;
//我个人认为这里MODEL_INPUT_SIZE是模型训练时用的输入尺寸
  float x_rate = (float)1920.0 / MODEL_INPUT_SIZE;
  float y_rate = (float)1080.0 / MODEL_INPUT_SIZE;
  printf("x_rate is %f, y_rate is %f\n", x_rate, y_rate);

  while (!quit)
  {
    mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);
    if (!mb)
    {
      printf("RK_MPI_SYS_GetMediaBuffer get null buffer!\n");
      break;
    }

    memcpy(&Media_Buffer[0], (uint8_t *)RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb));
    Media_Buffer_size = RK_MPI_MB_GetSize(mb);

    detect_result_group_t detect_result_group = rknn_queue->getDetectResult();
    Mat orig_img = Mat(1080, 1920, CV_8UC1, RK_MPI_MB_GetPtr(mb));

    for (int j = 0; j < detect_result_group.count; j++)
    {
      /*if (strcmp(detect_result_group.results[j].name, "person"))
        continue;*/
      if (detect_result_group.results[j].prop < 0.7)
        continue;
      int x = detect_result_group.results[j].box.left * x_rate;
      int y = detect_result_group.results[j].box.top * y_rate;
      int w = (detect_result_group.results[j].box.right -
               detect_result_group.results[j].box.left) *
              x_rate;
      int h = (detect_result_group.results[j].box.bottom -
               detect_result_group.results[j].box.top) *
              y_rate;

      if (x < 0)
        x = 0;
      if (y < 0)
        y = 0;
      while ((uint32_t)(x + w) >= 1920)
      {
        w -= 16;
      }
      while ((uint32_t)(y + h) >= 1080)
      {
        h -= 16;
      }
      printf("border=(%d %d %d %d)\n", x, y, w, h);
      nv12_border((char *)RK_MPI_MB_GetPtr(mb), 1920, 1080, x, y, w, h, 0, 0, 255);
      putText(orig_img, detect_result_group.results[j].name, Point(x + 32, y + 64), 2, 3, Scalar(255, 0, 0, 255));
    }

    RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, mb);
    RK_MPI_MB_ReleaseBuffer(mb);
  }
}

线程2:

        首先创建rknn模型连接符 ctx(类似于文件的fd) 然后使用load_model载入模型,这个函数内部也就是一个fopen函数 model_len是该函数执行后得到的模型长度

        rknn载入模型,把ctx与该模型连接起来

        下面这一段其实是判断了输入数与输出数其实可以不要 rknn_query 可以得到 输入数与输出数也可以得到其具体属性等等,具体可以参考瑞芯微 rknn手册

        一直到下面计算x,y比例(和上一个线程一样),然后创建了一个数据结构体

        进入while()循环,首先根据上述结构体创建了图像数据缓冲区mb,然后把上一线程中Media_Buffer(即vi获得的图像)拷贝到mb中

        设置原始图像字节数1920*1080*3通道与模型图片字节300*300*3,先将采集的NV12图像转化为rgb888,然后再将输入图像转化为300*300*3

        下面对模型输入输出进行配置:输入:图像数据 输出:名称与置信度

        运行模型rknn_run 进行一次推理

        最后对ssd预测做了一个后处理并把处理结果存在了detect_result_group,并把detect_result_group存入队列(这里是put,线程1 是get获取)

        释放资源

void *rknn_vi_rknn_thread(void *args)
{
  pthread_detach(pthread_self());
  //rknn_context感觉类似于fd文件描述符一样,有点像模型描述符
  rknn_context ctx;
  int ret;
  int model_len = 0;
  unsigned char *model;

  printf("Loading model ...\n");
  model = load_model(g_ssd_path, &model_len);
  ret = rknn_init(&ctx, model, model_len, 0);
  if (ret < 0)
  {
    printf("rknn_init fail! ret=%d\n", ret);
    return NULL;
  }

  // Get Model Input Output Info
  rknn_input_output_num io_num;
  ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
  if (ret != RKNN_SUCC)
  {
    printf("rknn_query fail! ret=%d\n", ret);
    return NULL;
  }
  printf("model input num: %d, output num: %d\n", io_num.n_input,
         io_num.n_output);

  printf("input tensors:\n");
  rknn_tensor_attr input_attrs[io_num.n_input];
  memset(input_attrs, 0, sizeof(input_attrs));
  for (unsigned int i = 0; i < io_num.n_input; i++)
  {
    input_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]),
                     sizeof(rknn_tensor_attr));
    if (ret != RKNN_SUCC)
    {
      printf("rknn_query fail! ret=%d\n", ret);
      return NULL;
    }
    printRKNNTensor(&(input_attrs[i]));
  }

  printf("output tensors:\n");
  rknn_tensor_attr output_attrs[io_num.n_output];
  memset(output_attrs, 0, sizeof(output_attrs));
  for (unsigned int i = 0; i < io_num.n_output; i++)
  {
    output_attrs[i].index = i;
    ret = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]),
                     sizeof(rknn_tensor_attr));
    if (ret != RKNN_SUCC)
    {
      printf("rknn_query fail! ret=%d\n", ret);
      return NULL;
    }
    printRKNNTensor(&(output_attrs[i]));
  }

  rga_buffer_t src;

  float x_rate = (float)1920 / MODEL_INPUT_SIZE;
  float y_rate = (float)1080 / MODEL_INPUT_SIZE;

  MB_IMAGE_INFO_S stImageInfo = {1920, 1080, 1920, 1080, IMAGE_TYPE_NV12};

  while (!quit)
  {

    MEDIA_BUFFER mb = RK_MPI_MB_CreateImageBuffer(&stImageInfo, RK_TRUE, MB_FLAG_NOCACHED);
    if (!mb)
    {
      printf("ERROR: no space left!\n");
      break;
    }

//Media_Buffer在上一线程是获取的vi视频流
    memcpy(RK_MPI_MB_GetPtr(mb), Media_Buffer, Media_Buffer_size);
    RK_MPI_MB_SetSize(mb, Media_Buffer_size);

    int rga_buffer_size = 1920 * 1080 * 3;
    int rga_buffer_model_input_size = MODEL_INPUT_SIZE * MODEL_INPUT_SIZE * 3;

    unsigned char *rga_buffer = (unsigned char *)malloc(rga_buffer_size);
    unsigned char *rga_buffer_model_input =
        (unsigned char *)malloc(rga_buffer_model_input_size);

    nv12_to_rgb24((unsigned char *)RK_MPI_MB_GetPtr(mb), rga_buffer, 1920,
                  1080);
//这几步 把vi码流数据转换为模型推理格式rgb 888 300x300
    rgb24_resize(rga_buffer, rga_buffer_model_input, 1920, 1080, MODEL_INPUT_SIZE, MODEL_INPUT_SIZE);

    rknn_input inputs[1];
    memset(inputs, 0, sizeof(inputs));
    inputs[0].buf = rga_buffer_model_input;
    inputs[0].index = 0;
    inputs[0].type = RKNN_TENSOR_UINT8;
    inputs[0].size = rga_buffer_model_input_size;
    inputs[0].fmt = RKNN_TENSOR_NHWC;
    //设置模型推理输入
    ret = rknn_inputs_set(ctx, io_num.n_input, inputs);
    if (ret < 0)
    {
      printf("rknn_input_set fail! ret=%d\n", ret);
      return NULL;
    }

    // Run
    printf("rknn_run\n");
    //执行一次模型推理。
    ret = rknn_run(ctx, NULL);
    if (ret < 0)
    {
      printf("rknn_run fail! ret=%d\n", ret);
      return NULL;
    }

    rknn_output outputs[2];
    memset(outputs, 0, sizeof(outputs));
    outputs[0].want_float = 1;
    outputs[1].want_float = 1;
    ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);
    if (ret < 0)
    {
      printf("rknn_outputs_get fail! ret=%d\n", ret);
      return NULL;
    }
//推理结果detect_result_group/

    detect_result_group_t detect_result_group;
    postProcessSSD((float *)(outputs[0].buf), (float *)(outputs[1].buf), MODEL_INPUT_SIZE,
                   MODEL_INPUT_SIZE, &detect_result_group);

//存放推理结果
    rknn_queue->putDetectResult(detect_result_group);

    RK_MPI_MB_ReleaseBuffer(mb);
    mb = NULL;

    if (rga_buffer != NULL)
    {
      free(rga_buffer);
      rga_buffer = NULL;
    }

    if (rga_buffer_model_input != NULL)
    {
      free(rga_buffer_model_input);
      rga_buffer_model_input = NULL;
    }
  }

  return NULL;
}

线程3:

        分离线程

        循环中不断进行推流即:rtsp_tx_video

        释放资源

        删除rtsp实例:rtsp_do_event

void *rknn_venc_rtsp_thread(void *args)
{
  pthread_detach(pthread_self());
  MEDIA_BUFFER mb = NULL;

  while (!quit)
  {
    mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0, -1);
    if (!mb)
    {
      printf("RK_MPI_SYS_GetMediaBuffer venc get null buffer!\n");
      break;
    }

    rtsp_tx_video(g_rtsp_session, (unsigned char *)RK_MPI_MB_GetPtr(mb), RK_MPI_MB_GetSize(mb), RK_MPI_MB_GetTimestamp(mb));
    RK_MPI_MB_ReleaseBuffer(mb);
    rtsp_do_event(g_rtsplive);
  }
  return NULL;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

w30-python02-pytest入门

代码如下&#xff1a; import pytest class Test_Obj:"""测试类"""#用例级别前后置def setup(self):print(用例级别------的前置处理)def teardown(self):print("用例级别--------的后置处理")# 用例def test_case1(self):print(&quo…

Photoshop(PS) 抠图简单教程

目录 快速选择 魔棒 钢笔 橡皮擦 蒙版 通道 小结 可以发现&#xff0c;ps逐渐成为必备基础的办公软件。本文让ps新手轻松学会抠图。 快速选择 在抠图之前&#xff0c;先了解下选区的概念。ps中大多数的抠图操作都是基于选区的&#xff0c;先选区再Ctrl J提取选区。而快…

android13 Settings动态显示隐藏某一项

总纲 android13 rom 开发总纲说明 目录 1.前言 2.确定目标设置项 3.修改参考 3.1 方法1 3.2 方法2 4.编译测试 5.彩蛋 1.前言 在Android 13系统中,动态显示或隐藏Settings应用中的某一项通常涉及到对Settings应用的内部逻辑进行修改。由于Settings应用是一个系统应用…

Fine-BI学习笔记

官方学习文档&#xff1a;快速入门指南- FineBI帮助文档 FineBI帮助文档 (fanruan.com) 1.零基础入门 1.1 功能简介 完成四个流程&#xff1a;新建分析主题、添加数据、分析数据、分享协作。 示例数据获取&#xff1a;5分钟上手FineBI - FineBI帮助文档 (fanruan.com) 1.2 …

Vue--解决error:0308010C:digital envelope routines::unsupported

原文网址&#xff1a;Vue--解决error:0308010C:digital envelope routines::unsupported_IT利刃出鞘的博客-CSDN博客 简介 本文介绍如何解决node.js在运行Vue项目时的报错&#xff1a;error:0308010C:digital envelope routines::unsupported。 问题描述 使用node.js运行Vu…

Zabbix监控TiDB数据库教程

作者 乐维社区&#xff08;forum.lwops.cn&#xff09; 许远 1概述 TiDB数据库是一个常见的开源分布式关系型数据库&#xff0c;通过使用分布式事务、分布式 SQL 引擎和分布式存储引擎来实现高可用性和横向扩展性。而 Docker 则是一个开源的容器化平台&#xff0c;它可以帮助开…

服务端渲染中的数据获取:结合 useRequestHeaders 与 useFetch

title: 服务端渲染中的数据获取&#xff1a;结合 useRequestHeaders 与 useFetch date: 2024/7/24 updated: 2024/7/24 author: cmdragon excerpt: 摘要&#xff1a;本文介绍Vue服务端渲染中使用useRequestHeaders获取请求头部信息&#xff0c;如cookie和authorization&…

入门 PyQt6 看过来(案例)07~ 文件选择

本文实现一个图片和文本文件选择器的案例&#xff0c;效果如下&#xff1a; ​ 文件选择查看功能很简单&#xff0c;只需要设计好图片文件和文本文件的选择函数就可以了。 1 图片文件选择 #图片文件选择槽函数&#xff0c;支持png ico jpg格式文件def imgFile(self):fname, _tm…

paraFoam 运行 报错 usr/lib/x86_64-linux-gnu/libQt5Core.so 已解决

在日常项目开发中。使用ubuntu 视图开发的时候。报错 缺少 libQt5Core 核心组件&#xff01; whereis libQt5Core.so.5sudo strip --remove-section.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 完美解决&#xff0c;并且能正常打开&#xff0c;前提是&#xff0c…

嵌入式MCU固件的几种Flash划分方式详解

通过OTA远程等方式下载的程序,其实还需要提前下载bootloader程序,才能进一步下载APP程序。 今天就来说说通过OTA方式升级固件时,几种flash划分方式。 独立型 所谓独立型就是专门划出一部分闪存(Flash)空间用来存储引导程序(BootLoader)。 如下图: BootLoader:引导…

如何正确选择叉车防撞系统?不可忽视的五大关键要素!

在繁忙的工业环境中&#xff0c;叉车防撞系统的选择是一项至关重要的任务。一个高效、可靠的防撞系统&#xff0c;不仅能够减少事故发生率&#xff0c;还能保障员工的安全和设备的完好。然而&#xff0c;市场上的叉车防撞系统种类繁多&#xff0c;如何正确选择&#xff0c;避免…

SQL注入万字详解,基于sqli-labs(手注+sqlmap)

目录 一、什么是SQL 1.什么是SQL 2.SQL的作用 3.MySQL基础知识 4.SQL增、删、改语句 *5.SQL查询语句 二、什么是SQL注入 1.SQL注入原理&#xff1a; 2.SQL注入&#xff1a; 3.SQL注入危害&#xff1a; 4.SQL注入技术分类&#xff1a; 5.防御方法&#xff1a;使用参…

图形编辑器基于Paper.js教程09:鼠标拖动画布,以鼠标点为缩放中心进行视图的缩放

如何使用Paper.js实现画布的缩放与拖动功能 在Web开发中&#xff0c;利用Paper.js库进行图形的绘制和交互操作是一种常见的实践。Paper.js是一个强大的矢量图形库&#xff0c;可以让开发者通过简洁的API完成复杂的图形操作。在本文中&#xff0c;我们将详细探讨如何使用Paper.…

electron安装及快速创建

electron安装及快速创建 electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 详细内容见官网&#xff1a;https://www.electronjs.org/zh/docs/latest/。 今天来记录下练习中的安装过程和hello world的创建。 创建项目文件夹&#xff0c;并执行npm 初始化命…

【MetaGPT系列】【MetaGPT完全实践宝典——如何定义单一行为多行为Agent】

目录 前言一、智能体1-1、Agent概述1-2、Agent与ChatGPT的区别 二、多智能体框架MetaGPT2-1、安装&配置2-2、使用已有的Agent&#xff08;ProductManager&#xff09;2-3、拥有单一行为的Agent&#xff08;SimpleCoder&#xff09;2-3-1、定义写代码行为2-3-2、角色定义2-3…

增材制造与智能制造关系

在撰写的增材制造技术与装备书籍中有着明确的描述&#xff0c;增材制造是智能制造的典型范例&#xff0c;是智能制造“类”的实例化过程。这种借助于计算机编程面向对象思想的解释可以更全面的理解增材制造和智能制造的关系。增材制造实例具备了智能制造类的属性&#xff0c;智…

第4章 .NET 8.0 ASP.NET Core图书管理系统 :项目布局

第1章 框架学习的基石与实战策略 第2章 大话ASP.NET Core 入门 第3章 创建最小&#xff08;Minimal APIs&#xff09;API应用程序 第4章 .NET 8.0 ASP.NET Core图书管理系统 &#xff1a;项目布局 在第3章中&#xff0c;我们利用ASP.NET Core的“空”模板创建了BookQueryS…

2、springboot3 vue3开发平台-后端-基础数据准备,MybatisPlus整合

文章目录 1. 基础数据准备2. 整合MybatisPlus3. MybatisPlus 配置3.1 数据源配置3.2 mybatis-plus 分页插件配置3.3 mybatis-plus 自动填充3.4 代码生成器 1. 基础数据准备 直接拿前辈做的表结构使用。 /*Navicat Premium Data TransferSource Server : localhost_my…

光伏气象站可以监测哪些数据?

推荐型号&#xff1a;TH-BGF11】光伏气象站是一种专门为评估太阳能光伏发电资源而设计的综合气象监测设备&#xff0c;能够实时监测和记录影响太阳能发电效率的关键气象参数。这些参数包括太阳辐射强度、温度&#xff08;环境温度和光伏组件表面温度&#xff09;、风速、风向、…

Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|(三)双向LSTM+CRF模型构建实现

Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|&#xff08;一&#xff09;序列标注与条件随机场的关系 Mindspore框架CRF条件随机场概率图模型实现文本序列命名实体标注|&#xff08;二&#xff09;CRF模型构建 Mindspore框架CRF条件随机场概率图模型实现文本…