当前位置: 首页 > news >正文

WPF+Halcon 培训项目实战(11):HS组件封装

文章目录

  • 前言
  • 相关链接
  • 项目专栏
  • 运行环境
  • 匹配图片
  • 封装组件
    • 新增类库项目
    • 选择依赖顺序并添加Nuget
    • 修改原本矩形方法
    • 运行结果:
  • 对矩形进行抽象封装
    • 抽象基类
    • 矩形抽象改造
  • 圆形抽象封装
    • 代码
    • 运行结果

前言

为了更好地去学习WPF+Halcon,我决定去报个班学一下。原因无非是想换个工作。相关的教学视频来源于下方的Up主的提供的教程。这里只做笔记分享,想要源码或者教学视频可以和他联系一下。

相关链接

微软系列技术教程 WPF 年度公益课程

Halcon开发 CSDN博客专栏

个人学习的Gitee 项目地址仓库

项目专栏

WPF+Halcon实战项目

运行环境

  • .net core 8.0
  • visual studio 2022
  • halcon HDevelop 20.11
  • windows 11

匹配图片

在这里插入图片描述

封装组件

Halcon官方的代码很多都是基于String的类型判断,我们要对Halcon的代码进行一步简单的封装。

新增类库项目

在这里插入图片描述
在这里插入图片描述

选择依赖顺序并添加Nuget

让我们的WPF主程序依赖于HalconExtension类库

在这里插入图片描述

添加Nuget

在这里插入图片描述

修改原本矩形方法

原来的方法

             // 显示绘制图案HalconWindow.HalconWindow.AttachDrawingObjectToWindow(rect);// 去除绘制图案//HalconWindow.HalconWindow.DetachDrawingObjectFromWindow(rect);rect.OnDrag(new HDrawingObject.HDrawingObjectCallbackClass((id, window, type) =>{var hv_type = id.GetDrawingObjectParams("type");var row1 = id.GetDrawingObjectParams("row1");var row2 = id.GetDrawingObjectParams("row2");var column1 = id.GetDrawingObjectParams("column1");var column2 = id.GetDrawingObjectParams("column2");//打印坐标NLogHelper.Debug($"拖动事件: {hv_type}:{row1} {column1} {row2} {column2} ");}));

现在的方法:

using HalconDotNet;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HalconExtension.Models
{/// <summary>/// 矩形封装/// </summary>public class HRectangleObj{public HTuple HType { get; private set; }public HTuple Row1 { get; private set; }public HTuple Row2 { get; private set; }public HTuple Column1 { get; private set; }public HTuple Column2 { get; private set; }public HDrawingObject DrawingObject { get; set; }/// <summary>/// HS组件主题/// </summary>public HSmartWindowControlWPF WindowControlWPF { get; set; }public Action<HRectangleObj> RefleshCallBack { get; set; }/// <summary>/// 初始化构造函数/// </summary>/// <param name="windowControlWPF"></param>/// <param name="row1"></param>/// <param name="column1"></param>/// <param name="row2"></param>/// <param name="column2"></param>public HRectangleObj(HSmartWindowControlWPF windowControlWPF,HTuple row1,HTuple column1,HTuple row2,HTuple column2){WindowControlWPF = windowControlWPF;Row1 = row1;Column1 = column1;Row2 = row2;Column2 = column2;DrawingObject = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.RECTANGLE1,Row1,Column1,Row2,Column2);}/// <summary>/// 默认构造函数/// </summary>/// <param name="windowControl"></param>public HRectangleObj(HSmartWindowControlWPF windowControl){WindowControlWPF = windowControl;Row1 = 0;Column1 = 0;Row2 = 100;Column2 = 100;DrawingObject = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.RECTANGLE1, Row1, Column1, Row2, Column2);//添加更新事件DrawingObject.OnDrag(Reflash);DrawingObject.OnResize(Reflash);}/// <summary>/// 显示图像/// </summary>public void Show(){WindowControlWPF.HalconWindow.AttachDrawingObjectToWindow(DrawingObject);}/// <summary>/// 分离图像/// </summary>public void Close(){WindowControlWPF.HalconWindow.DetachDrawingObjectFromWindow(DrawingObject);}/// <summary>/// 序列化输出/// </summary>/// <returns></returns>public override string ToString(){return JsonConvert.SerializeObject(new{type = HType.ToString(),row1 = TwoDecimalPlaces(Row1),column1 = TwoDecimalPlaces(Column1),row2 = TwoDecimalPlaces(Row2),column2 = TwoDecimalPlaces(Column2)});}public void Reflash(HDrawingObject drawid, HWindow window, string type){HType = drawid.GetDrawingObjectParams("type");Row1 = drawid.GetDrawingObjectParams("row1");Row2 = drawid.GetDrawingObjectParams("row2");Column1 = drawid.GetDrawingObjectParams("column1");Column2 = drawid.GetDrawingObjectParams("column2");//将本身回调RefleshCallBack(this);}/// <summary>/// 保留两位小数/// </summary>/// <returns></returns>private string TwoDecimalPlaces(HTuple value){return decimal.Round(decimal.Parse(value.ToString()), 2).ToString();}}
}

运行代码

            InitRectangleBtn = new RelayCommand(() =>{MsgHelper.Info("生成矩形");//rect = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.RECTANGLE1, 100, 200, 300, 400); 显示绘制图案//HalconWindow.HalconWindow.AttachDrawingObjectToWindow(rect); 去除绘制图案HalconWindow.HalconWindow.DetachDrawingObjectFromWindow(rect);//rect.OnDrag(new HDrawingObject.HDrawingObjectCallbackClass((id, window, type) =>//{//    var hv_type = id.GetDrawingObjectParams("type");//    var row1 = id.GetDrawingObjectParams("row1");//    var row2 = id.GetDrawingObjectParams("row2");//    var column1 = id.GetDrawingObjectParams("column1");//    var column2 = id.GetDrawingObjectParams("column2");//    //打印坐标//    NLogHelper.Debug($"拖动事件: {hv_type}:{row1} {column1} {row2} {column2} ");//}));//初始化对象RectangleObj = new HRectangleObj(HalconWindow);//刷新回调里面进行打印RectangleObj.RefleshCallBack = (obj) =>{NLogHelper.Debug(obj.ToString());};//显示对象RectangleObj.Show();});

运行结果:

在这里插入图片描述

对矩形进行抽象封装

抽象就是abstract,简单来说就是认为所有的基类都有相同的函数,比如ToString,Show,Close等。抽象更利于我们的代码的规划。

其实很多语法是程序自己给自己加限制,就是强迫自己按照某个逻辑进行代码开发。典型的例子就是JS转TS。这里不展开说明

抽象基类

using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HalconExtension.Models
{public abstract class HObjBase{/// <summary>/// HS组件主题/// </summary>public HSmartWindowControlWPF WindowControlWPF { get; set; }public HDrawingObject DrawingObject { get; set; }public HTuple HType { get; private set; }public HObjBase(HSmartWindowControlWPF windowControlWPF){WindowControlWPF = windowControlWPF;}/// <summary>/// 显示图像/// </summary>public void Show(){WindowControlWPF.HalconWindow.AttachDrawingObjectToWindow(DrawingObject);}/// <summary>/// 分离图像/// </summary>public void Close(){WindowControlWPF.HalconWindow.DetachDrawingObjectFromWindow(DrawingObject);}/// <summary>/// 保留两位小数/// </summary>/// <returns></returns>public string TwoDecimalPlaces(HTuple value){return decimal.Round(decimal.Parse(value.ToString()), 2).ToString();}/// <summary>/// 序列化/// </summary>/// <returns></returns>public abstract override string ToString();/// <summary>/// 刷新事件/// </summary>public abstract void Reflash(HDrawingObject drawid, HWindow window, string type);}
}

矩形抽象改造

using HalconDotNet;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HalconExtension.Models
{/// <summary>/// 矩形封装/// </summary>public class HRectangleObj : HObjBase{public HTuple HType { get; private set; }public HTuple Row1 { get; private set; }public HTuple Row2 { get; private set; }public HTuple Column1 { get; private set; }public HTuple Column2 { get; private set; }/// <summary>/// 刷新回调/// </summary>public Action<HRectangleObj> RefleshCallBack { get; set; }/// <summary>/// 初始化构造函数/// </summary>/// <param name="windowControlWPF"></param>/// <param name="row1"></param>/// <param name="column1"></param>/// <param name="row2"></param>/// <param name="column2"></param>public HRectangleObj(HSmartWindowControlWPF windowControlWPF, HTuple row1, HTuple column1, HTuple row2, HTuple column2) : base(windowControlWPF){//WindowControlWPF = windowControlWPF;Row1 = row1;Column1 = column1;Row2 = row2;Column2 = column2;DrawingObject = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.RECTANGLE1, Row1, Column1, Row2, Column2);//添加更新事件DrawingObject.OnDrag(Reflash);DrawingObject.OnResize(Reflash);}/// <summary>/// 默认构造函数/// </summary>/// <param name="windowControl"></param>public HRectangleObj(HSmartWindowControlWPF windowControl) : base(windowControl){Row1 = 0;Column1 = 0;Row2 = 100;Column2 = 100;DrawingObject = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.RECTANGLE1, Row1, Column1, Row2, Column2);//添加更新事件DrawingObject.OnDrag(Reflash);DrawingObject.OnResize(Reflash);}/// <summary>/// 序列化输出/// </summary>/// <returns></returns>public override string ToString(){return JsonConvert.SerializeObject(new{type = HType.ToString(),row1 = TwoDecimalPlaces(Row1),column1 = TwoDecimalPlaces(Column1),row2 = TwoDecimalPlaces(Row2),column2 = TwoDecimalPlaces(Column2)});}public override void Reflash(HDrawingObject drawid, HWindow window, string type){HType = drawid.GetDrawingObjectParams("type");Row1 = drawid.GetDrawingObjectParams("row1");Row2 = drawid.GetDrawingObjectParams("row2");Column1 = drawid.GetDrawingObjectParams("column1");Column2 = drawid.GetDrawingObjectParams("column2");//将本身回调RefleshCallBack(this);}}
}

圆形抽象封装

如果集成抽象的对象只有一个,那么抽象就没有意义了。抽象一定是多个类继承才有使用的实际意义。

代码

using HalconDotNet;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace HalconExtension.Models
{public class HCircleObj : HObjBase{public HTuple Row { get; set; }public HTuple Column { get; set; }public HTuple Radius { get; set; }public HTuple HType { get; private set; }/// <summary>/// 刷新回调/// </summary>public Action<HCircleObj> RefleshCallBack { get; set; }public HCircleObj(HSmartWindowControlWPF windowControlWPF, HTuple row, HTuple col, HTuple radius):base(windowControlWPF){Row = row;Column = col;Radius = radius;DrawingObject = HDrawingObject.CreateDrawingObject(HDrawingObject.HDrawingObjectType.CIRCLE, Row, Column, Radius);//添加更新事件DrawingObject.OnDrag(Reflash);DrawingObject.OnResize(Reflash);}public HCircleObj(HSmartWindowControlWPF windowControlWPF) : this(windowControlWPF,64,64,64){}public override void Reflash(HDrawingObject drawid, HWindow window, string type){HType = drawid.GetDrawingObjectParams("type");Row = drawid.GetDrawingObjectParams("row");Column = drawid.GetDrawingObjectParams("column");Radius = drawid.GetDrawingObjectParams("radius");//将本身回调RefleshCallBack(this);}public override string ToString(){return JsonConvert.SerializeObject(new{type = HType.ToString(),row = TwoDecimalPlaces(Row),column = TwoDecimalPlaces(Column),radius = TwoDecimalPlaces(Radius),});}}
}

运行结果

HCircleObj(windowControlWPF, 64, 64, 64);

相关文章:

WPF+Halcon 培训项目实战(11):HS组件封装

文章目录 前言相关链接项目专栏运行环境匹配图片封装组件新增类库项目选择依赖顺序并添加Nuget修改原本矩形方法运行结果&#xff1a; 对矩形进行抽象封装抽象基类矩形抽象改造 圆形抽象封装代码运行结果 前言 为了更好地去学习WPFHalcon&#xff0c;我决定去报个班学一下。原…...

VUE——IDEA 启动前端工程VS文件启动前端工程

IDEA 启动前端 目录 前言一、打开控制台二、输入npm install三、依赖下载完之后&#xff0c;输入npm run dev&#xff0c;运行前端项目1、IDEA启动前端工程2、文件目录启动前端工程 四、点击http://localhost:8080后续敬请期待 前言 启动已有的vue前端项目 一、打开控制台 选…...

自动驾驶论文

文章目录 一、Convolutional Social Pooling for Vehicle Trajectory Prediction二、QCNet&#xff1a;Query-Centric Trajectory Prediction三、VectorNet: Encoding HD Maps and Agent Dynamics from Vectorized Representation 一、Convolutional Social Pooling for Vehicl…...

Java经典框架之SpringDataJPA

SpringDataJPA Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机&#xff0c;Java 仍是企业和开发人员的首选开发平台。 课程内容的介绍 1. Spring整合Hibernate 2…...

向爬虫而生---Redis 基石篇3 <拓展List>

前言: 继上一篇向爬虫而生---Redis 基石篇2 &#xff1c;拓展Hash&#xff1e;-CSDN博客​​​​​​.往下继续---挖一挖list 正文: 在Redis中&#xff0c;列表&#xff08;List&#xff09;是一个常用的数据结构&#xff0c;尤其在爬虫应用中。例如&#xff0c;可以用列表实现…...

CSS渲染性能优化

✨ 专栏介绍 HTML/CSS专栏合集是一个涵盖HTML和CSS两个方面的栏目。HTML是一种标记语言&#xff0c;用于创建网页的结构和内容&#xff0c;而CSS是一种样式表语言&#xff0c;用于控制网页的外观和布局。 在HTML/CSS专栏合集中&#xff0c;我们将深入探讨HTML和CSS的基础知识…...

【C++入门】类和对象(完)

前言 在谈论C时&#xff0c;常常会涉及到一些高级特性和概念&#xff0c;比如初始化列表、static成员、友元、内部类、匿名对象等。这些概念在C编程中起着非常重要的作用&#xff0c;对于想要深入了解C语言的开发者来说&#xff0c;掌握这些知识是至关重要的。本文&#xff0c;…...

webshell检测方式深度剖析 --- Pixy系列二(数据流分析)

开篇 书接上文&#xff0c;这次我们来聊聊数据流分析&#xff0c;数据流分析的内容非常广泛&#xff0c;我们力求深入浅出通俗易懂&#xff0c;在简短的篇幅内将这一概念描述清楚。 简单来说&#xff0c;数据流分析是一种用来获取相关数据沿着程序执行路径流动的信息分析技术…...

[DAU-FI Net开源 | Dual Attention UNet+特征融合+Sobel和Canny等算子解决语义分割痛点]

文章目录 概要I Introduction小结 概要 提出的架构&#xff0c;双注意力U-Net与特征融合&#xff08;DAU-FI Net&#xff09;&#xff0c;解决了语义分割中的挑战&#xff0c;特别是在多类不平衡数据集上&#xff0c;这些数据集具有有限的样本。DAU-FI Net 整合了多尺度空间-通…...

使用Triton部署ONNX模型

介绍 适用于各种 AI 工作负载的推理&#xff1a;借助 NVIDIA Triton™&#xff0c;在任何处理器&#xff08;GPU、CPU 或其他&#xff09;上&#xff0c;对使用基于任何框架的&#xff0c;经过训练的机器学习模型或深度学习模型&#xff0c;进行推理部署。Triton 是 NVIDIA AI…...

Python访问ElasticSearch

ElasticSearch是广受欢迎的NoSQL数据库&#xff0c;其分布式架构提供了极佳的数据空间的水平扩展能力&#xff0c;同时保障了数据的可靠性&#xff1b;反向索引技术使得数据检索和查询速度非常快。更多功能参见官网介绍 https://www.elastic.co/cn/elasticsearch/ 下面简单罗列…...

Flutter 混合开发 - 动态下发 libflutter.so libapp.so

背景 最近在做包体积优化&#xff0c;在完成代码混淆、压缩&#xff0c;裁剪ndk支持架构&#xff0c;以及资源压缩&#xff08;如图片转webp、mp3压缩等&#xff09;后发现安装包的中占比较大的仍是 so 动态库依赖。 具体查看发现 libflutter.so 和 libapp.so 的体积是最大的&…...

Peter算法小课堂—动态规划

Peter推荐算法书&#xff1a;《算法导论》 图示&#xff1a; 目录 钢条切割 打字怪人 钢条切割 算法导论&#xff08;第四版&#xff09;第十四章第一节&#xff1a;钢条切割 题目描述&#xff1a; 给定一根长度为 n 英寸的钢条和一个价格表 &#xff0c;其中 i1,2,…,n …...

2022–2023学年2021级计算机科学与技术专业数据库原理 (A)卷

一、单项选择题&#xff08;每小题1.5分&#xff0c;共30分&#xff09; 1、构成E—R模型的三个基本要素是&#xff08; B &#xff09;。 A&#xff0e;实体、属性值、关系 B&#xff0e;实体、属性、联系 C&#xff0e;实体、实体集、联系 D&#xff0e;实体、实体…...

Clojure 实战(4):编写 Hadoop MapReduce 脚本

Hadoop简介 众所周知&#xff0c;我们已经进入了大数据时代&#xff0c;每天都有PB级的数据需要处理、分析&#xff0c;从中提取出有用的信息。Hadoop就是这一时代背景下的产物。它是Apache基金会下的开源项目&#xff0c;受Google两篇论文的启发&#xff0c;采用分布式的文件…...

Django 分页(表单)

目录 一、手动分页二、分页器分页 一、手动分页 1、概念 页码&#xff1a;很容易理解&#xff0c;就是一本书的页码每页数量&#xff1a;就是一本书中某一页中的内容&#xff08;数据量&#xff0c;比如第二页有15行内容&#xff09;&#xff0c;这 15 就是该页的数据量 每一…...

socket实现视频通话-WebRTC

最近喜欢研究视频流&#xff0c;所以思考了双向通信socket&#xff0c;接下来我们就一起来看看本地如何实现双向视频通讯的功能吧~ 客户端获取视频流 首先思考如何获取视频流呢&#xff1f; 其实跟录音的功能差不多&#xff0c;都是查询电脑上是否有媒体设备&#xff0c;如果…...

simulink代码生成(九)—— 串口显示数据(纸飞机联合调试)

纸飞机里面的协议是固定的&#xff0c;必须按照协议配置&#xff1b; &#xff08;1&#xff09;使用EasyHEX协议&#xff0c;测试int16数据类型 测试串口发出的数据是否符合&#xff1f; 串口接收数据为&#xff1a; 打开纸飞机绘图侧&#xff1a; &#xff08;1&#xff09…...

Mysql数据库(中)——增删改查的学习(全面,详细)

上一篇主要对查询操作进行了详细的总结&#xff0c;本篇主要对增删改操作以及一些常用的函数进行总结&#xff0c;包括流程控制等&#xff1b;以下的代码可以直接复制到数据库可视化软件中&#xff0c;便于理解和练习&#xff1b; 常用的操作&#xff1a; #函数&#xff1a; S…...

test dbtest-03-对比 Liquibase、flyway、dbDeploy、dbsetup

详细对比 Liquibase、flyway、dbDeploy、dbsetup&#xff0c;给出对比表格 下面是一个简要的对比表格&#xff0c;涵盖了 Liquibase、Flyway、dbDeploy 和 DbSetup 这四个数据库变更管理工具的一些主要特点。 特点/工具LiquibaseFlywaydbDeployDbSetup开发语言Java&#xff0…...

移动端ECharts实战:如何隐藏原生滚动条实现内容区域左右滑动(附完整代码)

移动端ECharts进阶&#xff1a;原生滚动条隐藏与手势滑动优化全解析 在移动端数据可视化项目中&#xff0c;ECharts的默认滚动条交互常常成为用户体验的"阿喀琉斯之踵"。当用户手指在狭小的滚动条上艰难拖动时&#xff0c;那种顿挫感和操作失败率会让精心设计的数据图…...

别再手动填Token了!用Knife4j的OAuth2配置,一键搞定接口文档自动化认证

告别手动Token时代&#xff1a;Knife4j与OAuth2的自动化认证实战 每次调试API都要复制粘贴Token的日子该结束了。作为后端开发者&#xff0c;我们花了大量时间在接口文档和认证流程之间来回切换——这不仅是效率问题&#xff0c;更是一种思维中断。想象一下&#xff0c;当你的微…...

虚拟光驱软件Daemon Tools Lite

链接&#xff1a;https://pan.quark.cn/s/ebc5b998a07bDaemon Tools Lite 是一款免费、稳定、方便、优秀的虚拟光驱软件。安装后会自动在资源管理器生成一个和真实光驱一样的盘符&#xff0c;让您像访问真正光驱一样来访问虚拟光驱。Daemon Tools Lite 还可以模拟备份并且合并保…...

【悬疑言情小说推荐】《血语玫瑰》

​​​​​​《血语玫瑰》国际标准书号&#xff1a;ISBN&#xff1a;978-986-6364-30-3 作者:追月逐花 本书地址&#xff1a;http://e.dangdang.com/products/1901197341.html 每个女孩都期待男友年轻英俊、家境优渥、学识出众&#xff0c;而 “魔鬼” 恰好符合所有条件&…...

树莓派5跑n8n稳吗?实测Docker部署性能与避坑指南(Ubuntu 24.04 + 安全加固)

树莓派5实战&#xff1a;n8n工作流自动化平台的Docker部署与性能调优指南 在物联网与自动化技术蓬勃发展的今天&#xff0c;如何以最低成本构建稳定可靠的工作流自动化系统成为许多开发者和企业关注的重点。树莓派5凭借其出色的性价比和低功耗特性&#xff0c;配合Docker容器化…...

双摆控制系统:LQR、LQG、LQI控制器及龙伯格观测器文件清单

移动小车上双摆的LQR、LQG、LQI控制器和龙伯格观测器文件列表&#xff1a; LQG.m LQG_non_linear.m LQI.m LQR.m LQR_Non_linear.m Luenberger_observer.m Observer_non_linear.m 最近蹲在实验室的工位上啃移动小车双摆的控制代码&#xff0c;翻来覆去调了快两周&#xff0c;终…...

别再手动拆任务了!用CrewAI+DeepSeek打造你的第一个AI小团队(附PDF解析实战)

用CrewAI构建自动化AI团队&#xff1a;从PDF解析到智能协作实战 在传统AI开发中&#xff0c;开发者往往需要手动编写复杂的任务流程&#xff0c;像指挥一个士兵完成所有战斗。而CrewAI带来的革命性变化在于——它让你能够组建一支训练有素的AI特种部队&#xff0c;每个成员各司…...

Redis监听Key过期事件报错?教你两种绕过CONFIG命令的实用方案

Redis监听Key过期事件的两种安全实践方案 Redis的Key过期事件监听是许多业务场景中的核心需求&#xff0c;比如订单超时处理、会话管理、缓存刷新等。但在云服务环境中&#xff0c;开发者常会遇到ERR unknown command CONFIG的报错&#xff0c;这通常是因为云服务提供商出于安全…...

毕业论文神器 2026 降AI率平台推荐:工具对比+最好用AI推荐

2026年真正好用的AI论文降重与改写工具&#xff0c;核心看降重效果、去AI味、格式保留、学术适配四大指标。综合实测&#xff0c;千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队&#xff0c;覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 …...

Otter模型对比学习:提升跨模态表示质量的技术方案

Otter模型对比学习&#xff1a;提升跨模态表示质量的技术方案 【免费下载链接】Otter &#x1f9a6; Otter, a multi-modal model based on OpenFlamingo (open-sourced version of DeepMinds Flamingo), trained on MIMIC-IT and showcasing improved instruction-following a…...