qt项目-《图像标注软件》源码阅读笔记-Shape类绘图及其子类

目录

1. Shape 概览 

2. Shape 基类

2.1 字段

2.2 方法

2.3 嵌套类型

3. Shape2D 2d形状纯虚基类

3.1 字段

3.2 方法

4. Shape3D 3d形状纯虚基类

5. Shape2D子类

5.1 Rectangle 矩形类


1. Shape 概览 

功能:Shape类及其子类负责形状的绘制及形状的存储。Shape有7个子类。

  1. Brush代表画刷形状,用于分割标注;
  2. Rectangle代表矩形形状;
  3. Polygons代表多边形形状;
  4. Circle代表圆形形状;
  5. Curve代表平滑曲线形状;
  6. Rectangle3D代表3d长方体形状;
  7. Brush3D代表3d画刷形状,用于3d分割标注。

2. Shape 基类

2.1 字段

  1. color:标注颜色;
  2. isFill:是否被选中,选中时为true,并填充标注形状的内部,便于用户交互;
  3. isHide:是否隐藏标注;
  4. isHover:是否悬浮,若鼠标悬浮在标注内部,则变为true,填充内部颜色,便于用户交互;
  5. label:标注对应的标签文字;
  6. type:标注类型,枚举类型,各种形状(Brush,Rectangle,Polygons,...);

2.2 方法

  1. Shape(Type t):构造函数,初始化标注类型,即标注的形状;
  2. virtual ~Shape()=0;纯虚函数,使其成为抽象类。

2.3 嵌套类型

enum Type{Brush,Rectangle,Polygons,Circle,Curve,Rectangle3D,Brush3D};// 子类可以通过继承 My::Shape 类并在构造函数中初始化 type 成员来使用这个枚举类型。

嵌套类型有很多种,包括内部类、嵌套结构、嵌套枚举等。嵌套类型提供了一种将相关的类型组织在一起并隐藏其实现细节的方式。这里使用的是枚举类型的嵌套。

作用和优点

  • 清晰的类型定义, 通过将这些类型放入枚举中,代码提供了一种清晰的、可读性强的方式来表示标注形状的类型。这样的设计使得在代码中使用这些类型更为直观,提高了代码的可维护性。
  • 扩展性,如果需要添加新的标注形状,只需在 enum Type 中添加新的成员。这样的设计使得系统更具有扩展性,而无需修改大量现有代码。
#ifndef SHAPE_H
#define SHAPE_H
#include<QString>
#include<QColor>#include"Namespace.h"  // 头文件内声明了Shape, Brush,Rectangle,Polygons,...等类名/// \brief 所有标注形状的基类
///
/// 负责形状的绘制及形状的存储
class My::Shape{
public:/// \brief 标注形状的类型////// Brush代表画刷形状,用于分割标注,/// Rectangle代表矩形形状,Polygons代表多边形形状,Circle代表圆形形状,Curve代表平滑曲线形状/// Rectangle3D代表3d长方体形状,Brush3D代表3d画刷形状,用于3d分割标注enum Type{Brush,Rectangle,Polygons,Circle,Curve,Rectangle3D,Brush3D};/// \brief 标注类型const Type type;/// \brief 标注对应的标签文字QString label;/// \brief 默认标注颜色QColor color=QColor(100,255,0,100);/// \brief 是否填充内部////// 当被选中时,isFill会变为true,填充标注形状的内部,便于用户交互bool isFill=false;/// \brief 是否隐藏标注bool isHide=false;/// \brief 是否悬浮////// 当前鼠标是否悬浮在标注内部,若悬浮在内部,则变为true,填充内部颜色,便于用户交互bool isHover=false;Shape(Type t);virtual ~Shape()=0;
};
#endif // SHAPE_H

使用 #include "Namespace.h" 的形式是为了引入命名空间 My 中的其他类。这样做的主要优点包括:

  • 代码组织: 使用命名空间可以将相关的类、函数、变量等组织在一起,提高了代码的可读性和可维护性。通过引入命名空间 My,你可以将所有与项目有关的类都放在同一个命名空间下,使代码更有条理。
  • 避免命名冲突: 命名空间提供了一种防止命名冲突的机制。如果在项目中有多个独立开发的部分,使用命名空间可以避免不同部分定义相同名称的类或函数时的冲突问题。

3. Shape2D 2d形状纯虚基类

3.1 字段

points: 存储标注形状的像素点位

3.2 方法

  • draw: 绘制标注形状,虚函数;
  • isInShape: 判断鼠标是否在标注形状内部,虚函数;
  • offset:偏移标注形状,虚函数;
#ifndef SHAPE2D_H
#define SHAPE2D_H
#include<QVector>
#include<QPoint>
#include<QLabel>
#include<opencv2/opencv.hpp>
#include"Shape.h"/// \brief 2d标注形状的基类
class My::Shape2D:public My::Shape{
public:/// \brief 存储标注形状的像素点位QVector<QPointF> points;Shape2D(My::Shape::Type t):Shape(t){}/// \brief 绘制标注形状虚函数virtual void draw(QWidget* w);/// \brief 判断鼠标是否在标注形状内部虚函数virtual bool isInShape(QPointF p,QWidget* w);/// \brief 偏移标注形状虚函数virtual void offset(float xOffset,float yOffset);virtual ~Shape2D()=0;
};#endif // SHAPE_H

4. Shape3D 3d形状纯虚基类

#ifndef SHAPE3D_H
#define SHAPE3D_H
#include<QVector>
#include<opencv2/opencv.hpp>
#include"Shape.h"/// \brief 3d标注形状的基类
class My::Shape3D:public My::Shape{
public:/// \brief 存储像素点位QVector<cv::Point3f> points;
public:Shape3D(Type t);virtual ~Shape3D()=0;
};#endif // SHAPE3D_H

5. Shape2D子类

每个子类都有3个相同的方法(继承自Shape2D基类):

  • draw: 绘制标注形状,虚函数;
  • isInShape: 判断鼠标是否在标注形状内部,虚函数;
  • offset:偏移标注形状,虚函数;

5.1 Rectangle 矩形类

字段:

  • width: 存储矩形的宽,相对Widget宽度的百分比;
  • height:存储矩形的高,也是相对值;

继承的points存放的x, y也是相对Widget百分比值。

方法:

Rectangle: 默认构造函数,在头文件做好了实现,用于构造自己和父类。

// Rectangle()默认构造函数,调用基类构造函数,传递Type,
// 用于初始化 Rectangle 类的基类 Shape2D 的成员。
Rectangle():Shape2D(Shape2D::Rectangle){} 
#ifndef RECTANGLE_H
#define RECTANGLE_H
#include"Shape2D.h"/// \brief 代表矩形形状,继承Shape2D类
class My::Rectangle:public My::Shape2D{
public:/// \brief 存储矩形的宽float width;/// \brief 存储矩形的高float height;Rectangle():Shape2D(Shape2D::Rectangle){} // Rectangle()默认构造函数,调用基类构造函数,传递Type,用于初始化 Rectangle 类的基类 Shape2D 的成员。/// \brief 绘制形状函数virtual void draw(QWidget* w);/// \brief 判断是否在形状内函数virtual bool isInShape(QPointF p,QWidget* w);/// \brief 偏移形状函数virtual void offset(float xOffset,float yOffset);
};#endif // RECTANGLE_H
#include<QPainter>
#include"Rectangle.h"
#include<QDebug>
#include<math.h>/// \brief 绘制矩形
void My::Rectangle::draw(QWidget *w){if(isHide)return;  // 隐藏标注了,则不会下面的绘制。QPainter painter(w);  // 它用于在 QWidget 上进行绘制。QPen pen;pen.setColor(color);  // 设置线颜色pen.setWidth(4);  // 矩形线宽,固定是4个像素。painter.setPen(pen);if(isFill || isHover)painter.setBrush(color);  // 是否被选中,或者悬浮,则设置刷子颜色。else painter.setBrush(Qt::NoBrush);  // 否在不使用刷子if(points.length()==0)return;  // 为空,则说明没有点击,则不能绘制矩形,直接返回。// 分别绘制不同方向的矩形。// 这种操作的目的通常是为了将相对于控件大小的百分比坐标转化为实际的像素坐标。x,y,width,height都是相对值。if(width>=0&&height>=0){  // 矩形的宽度和高度都正常,不是负数. draw(x,y,w,h)painter.drawRect(int(points[0].x()*w->width()), int(points[0].y()*w->height()),int(width*w->width()),int(height*w->height()));}if(width<0&&height>=0){   // 宽度为负数,points[0]点在右上 draw(x,y,w,h)painter.drawRect(int(points[0].x()*w->width()+width* w->width()), int(points[0].y()*w->height()),int(abs(width*w->width())),int(height*w->height()));}if(width<0&&height<0){    // 均为负数,points[0]点在右下painter.drawRect(int(points[0].x()*w->width()+width* w->width()),int(points[0].y()*w->height()+height*w->height()),int(abs(width*w->width())),int(abs(height*w->height())));}if(width>=0&&height<0){   // 高度为负数. points[0]点在左下painter.drawRect(int(points[0].x()*w->width()),int(points[0].y()*w->height()+height*w->height()),int(width*w->width()),int(abs(height*w->height())));}}/// \brief 判断是否在矩形内部
bool My::Rectangle::isInShape(QPointF p,QWidget* w){if(points.length()==0)return false;  // 没有点击过,不存在矩形,则返回。// 矩形的四个顶点: 左上开始,顺时针走。std::vector<cv::Point2f> vec;vec.push_back(cv::Point2f(float(points[0].x())*w->width(),float(points[0].y())*w->height()));vec.push_back(cv::Point2f((float(points[0].x())+width)*w->width(),float(points[0].y())*w->height()));vec.push_back(cv::Point2f((float(points[0].x())+width)*w->width(),(float(points[0].y())+height)*w->height()));vec.push_back(cv::Point2f(float(points[0].x())*w->width(),(float(points[0].y())+height)*w->height()));cv::Point2f cvp(float(p.x())*w->width(),float(p.y())*w->height());double res=cv::pointPolygonTest(vec,cvp,false);if(res<0)return false;else return true;
}/// \brief 偏移标注
void My::Rectangle::offset(float xOffset,float yOffset){for(int i=0;i<points.length();i++){points[i].rx()+=double(xOffset);  // 它返回的是一个引用,允许我们修改这个点的 x 坐标。points[i].ry()+=double(yOffset);}
}

(1)在这段代码中,x = int(points[0].x()*w->width()),这里涉及到坐标的映射和缩放。

假设 points[0].x() 是矩形左上角点在相对于矩形区域的 x 方向上的百分比坐标(范围在 0.0 到 1.0 之间),w->width()QWidget 的宽度。通过乘以 w->width(),将相对坐标转换为绝对坐标,即在 QWidget 上的具体像素坐标。

这种操作的目的通常是为了将相对于控件大小的百分比坐标转化为实际的像素坐标。这是因为在绘制图形时,常常需要考虑到用户可能调整窗口大小,而相对于窗口大小的百分比坐标能够适应不同的窗口尺寸。

(2)if(width<0&&height>=0){   // 宽度为负数,points[0]点在右上

左上x = int(points[0].x()*w->width() + width* w->width()). 

int(points[0].x()*w->width()是矩形的右上x,width* w->width()是负数宽度,相加就得到左上x.

这里有个问题就是points是在哪里赋值的?

待续。。。

其他子类类似,就不细究了。


参考:GitHub - jameslahm/labelme: A image annotation software for 2D or 3D images

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全部没有问题 (一.5)

java mooc练习 基础练习&#xff1a; 进阶练习&#xff1a; final 赋值一次 局部 必须赋值 抽象类 多态测试 package com.book;public class moocDraft1 {static int variable1;public void fatherMethod(moocDraft1 a){System.out.println(variable);}public static void…

C语言进阶---------作业复习

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Python~字典快速上手

目录 Key的重要性 一 创建字典{} 二 字典用key查找 in(遍历)和[]用key查找 keyerror in和[]的效率对比 三 字典的插入/修改/删除(先查找) ​编辑 四 字典增删查改/遍历的效率 五 字典的遍历 for遍历可迭代对象拿到key 与创建顺序相同 keys/values/items方法 六 可…

Git本地仓库命令补充

说明&#xff1a;之前对Git本地仓库的基础使用总结过一篇笔记&#xff0c;Git本地仓库使用&#xff0c;本文对Git的一些基础命令进行补充。 一步提交 通常&#xff0c;我们本地仓库使用Git&#xff0c;文件都需要先 add&#xff0c;将文件从工作区加入到暂存区&#xff0c;然…

【PHY6222】绑定详解

1.函数详解 bStatus_t GAPBondMgr_SetParameter( uint16 param, uint8 len, void* pValue ) 设置绑定参数。 bStatus_t GAPBondMgr_GetParameter( uint16 param, void* pValue ) 获取绑定参数。 param&#xff1a; GAPBOND_PAIRING_MODE&#xff0c;配对模式&#xff0c;…

Flink CDC 1.0至3.0回忆录

Flink CDC 1.0至3.0回忆录 一、引言二、CDC概述三、Flink CDC 1.0&#xff1a;扬帆起航3.1 架构设计3.2 版本痛点 四、Flink CDC 2.0&#xff1a;成长突破4.1 DBlog 无锁算法4.2 FLIP-27 架构实现4.3 整体流程 五、Flink CDC 3.0&#xff1a;应运而生六、Flink CDC 的影响和价值…

创建型设计模式

创建型设计模式 一、六大基本原则1、单一职责原则2、开闭原则3、里氏代换原则4、依赖倒置原则5、接口隔离原则6、迪米特法则 二、设计模式总览三、具体代码实现工厂设计模式抽象工厂设计模式建造者设计模式原型设计模式单例设计模式 五种设计模式的主要代码以及实现包 一、六大…

前端常用的工具网站

前端常用的工具网站&#x1f516; 文章目录 前端常用的工具网站&#x1f516;1. 图片在线压缩2. iconfont--矢量图标3. JSON在线格式化4. EMOJIALL--表情符号5. removebg--去除图片背景6. FREE API--免费API接口7. Lorem picsum --随机图片8.UU在线工具 -- 聚合工具 1. 图片在线…

STM32实现三个小灯亮

led.c #include"led.h"void Led_Init(void) {GPIO_InitTypeDef GPIO_VALUE; //???RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//???GPIO_VALUE.GPIO_ModeGPIO_Mode_Out_PP;//???? ????GPIO_VALUE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2|GPIO_P…

【Node JS】node.js安装步骤详解

一、安装Node.js 1.下载 Node.js官网下载 根据自身系统下载对应的安装包&#xff08;我这里为Windows11 64位&#xff0c;故选择下载第一个安装包&#xff09; 2.安装 双击安装包&#xff0c;点击Next&#xff0c;勾选使用许可协议&#xff0c;点击Next&#xff0c;选择安装位…

Adobe InDesign各版本安装指南

下载链接 https://pan.baidu.com/s/1VWGKDUijTTETU9sVWFjCtg?pwd0531 #2024版本 1.鼠标右击【InCopy2024(64bit)】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;【解压到 InCopy2024(64bit)】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Setup…

蓝桥村的神秘农田

蓝桥村的神秘农田 问题描述 小蓝是蓝桥村的村长&#xff0c;他拥有一块神秘的农田。这块农田的奇特之处在于&#xff0c;每年可以种植两种作物&#xff0c;分别称为 "瑶瑶豆" 和 "坤坤果"。小蓝需要为每种作物选择一个整数的生长指数&#xff0c;瑶瑶豆的…