点云标注工具开发记录(四)之点云根据类别展示与加速渲染

先前我们使用的是Open3D进行点云加载与展示,但由于Open3D更侧重于点云处理,其缺少一些相关的GUI控件,因此采用PyQt进行开发,同时使用OpenGL进行3D渲染,那么具体要如何实现呢?

复选框类别选中点云展示

如何开发根据点云类别展示点云的功能呢,效果如下:

当我们的复选框被取消时,相应的点云会不显示

在这里插入图片描述

main.py文件中,其代码如下:

首先需要给复选框绑定事件

check_box.stateChanged.connect(functools.partial(self.point_cloud_visible))

self.openGLWidget.categorys是点云的类别,其值如下:

在这里插入图片描述

接下来的事件处理都是围绕着self.openGLWidget.categorys展开的,其值代表着每个点云所属的类别
事件处理逻辑如下:

def point_cloud_visible(self):
		#首先是判断当前的展示类型,是否是CATEGORY
        if self.openGLWidget.display == DISPLAY.CATEGORY:
        #根据点云类别颜色生成蒙版,初始全为True
            mask = np.ones(self.openGLWidget.categorys.shape, dtype=bool)
            #遍历类别复选框,根据索引号与self.openGLWidget.categorys中的点云类别值做匹配,从而选择这些点云是否可见
            for index in range(self.label_listWidget.count()):#self.label_listWidget.count()的值为8
                item = self.label_listWidget.item(index)
                widget = self.label_listWidget.itemWidget(item)
                check_box = widget.findChild(QtWidgets.QCheckBox, 'check_box')
                #如果当前的索引下被选中
                if not check_box.isChecked():
                	#根据索引index使对应的mask变为Fale,即不可见
                    mask[self.openGLWidget.categorys==index] = False
                    self.openGLWidget.category_display_state_dict[index] = False
                    #其值{0: True, 1:False, 2: True, 3: True, 4: True, 5: True, 6: True, 7: True}
                else:
                    self.openGLWidget.category_display_state_dict[index] = True
			#获得最终的mask
            self.openGLWidget.mask = mask
            #根据mask更改当前点云,包含坐标和颜色
            self.openGLWidget.current_vertices = self.openGLWidget.pointcloud.xyz[self.openGLWidget.mask]
            self.openGLWidget.current_colors = self.openGLWidget.category_color[self.openGLWidget.mask]

        elif self.openGLWidget.display == DISPLAY.INSTANCE:
            mask = np.ones(self.openGLWidget.instances.shape, dtype=bool)
            for index in range(self.label_listWidget.count()):
                item = self.label_listWidget.item(index)
                widget = self.label_listWidget.itemWidget(item)
                label_instance = widget.findChild(QtWidgets.QLabel, 'label_instance')
                label_instance = int(label_instance.text())
                check_box = widget.findChild(QtWidgets.QCheckBox, 'check_box')
                if not check_box.isChecked():
                    mask[self.openGLWidget.instances==label_instance] = False
                    self.openGLWidget.instance_display_state_dict[label_instance] = False
                else:
                    self.openGLWidget.instance_display_state_dict[label_instance] = True

            self.openGLWidget.mask = mask
            self.openGLWidget.current_vertices = self.openGLWidget.pointcloud.xyz[self.openGLWidget.mask]
            self.openGLWidget.current_colors = self.openGLWidget.instance_color[self.openGLWidget.mask]
        #重新渲染点云
        self.openGLWidget.init_vertex_vao()
        self.openGLWidget.update()

最终效果如下:

在这里插入图片描述

点云渲染加速

每当我们执行了某个操作,最后要想对点云有效果,最后都需要调用一段代码:

self.openGLWidget.init_vertex_vao()
self.openGLWidget.update()

其中init_vertex_vao()是我们自定义的,即初始化点云(每次点云发生修改都需要进行初始化,这里的初始化可以认为是重写)而update函数则是OpenGL的更新函数

opengl_widget.py中的init_vertex_vao函数定义代码如下:

这段代码主要是将一些数据加载到缓冲区,用以提升渲染效率,这里我们需要了解一些基础概念:

  • VBO vertex buffer object 顶点缓冲对象 :VBOCPUGPU传递信息的桥梁,我们把数据存入VBO是在CPU上操作,VBO会自动将数据送至GPU。送至GPU不需要任何人为操作。
  • VAO vertex array object 顶点数组对象:VBO将顶点数据传送至GPU只是一堆数字,要怎么向GPU解释它们呢,就需要VAO,其内包含的是顶点数据的格式信息
  • EBO element(index) buffer object 索引缓冲对象
   def init_vertex_vao(self):
        self.vertex_vao = glGenVertexArrays(1)
		#请求生成 2 个新的缓冲区对象名称(VBO)
        vbos = glGenBuffers(2)
        #将特定的缓冲区对象名称(或称为“缓冲区标识符”即vbos[0])与特定的缓冲区目标绑定在一起,GL_ARRAY_BUFFER 是一个枚举值,指定了缓冲区的目标类型。在这个例子中,它表示该缓冲区将用作顶点属性数据(如顶点位置、颜色、纹理坐标等)的存储,即该段代码的含义是绑定
        glBindBuffer(GL_ARRAY_BUFFER, vbos[0])
        #加载是数据,glBufferData 或 glBufferSubData 函数来上传数据到缓冲区
        glBufferData(GL_ARRAY_BUFFER, self.current_vertices.nbytes, self.current_vertices, GL_STATIC_DRAW)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        
		#这段代码首先将vbos[1]绑定到GL_ARRAY_BUFFER目标上,然后使用glBufferData函数上传self.current_vertices数组中的数据到该缓冲区对象。self.current_colors.nbytes指定了数据的大小(以字节为单位),GL_STATIC_DRAW表示数据的使用模式(即数据很少更改,且会被多次绘制)。最后,将GL_ARRAY_BUFFER的绑定解除(绑定到0)。
        glBindBuffer(GL_ARRAY_BUFFER, vbos[1])
        glBufferData(GL_ARRAY_BUFFER, self.current_colors.nbytes, self.current_colors, GL_STATIC_DRAW)
        glBindBuffer(GL_ARRAY_BUFFER, 0)

        glBindVertexArray(self.vertex_vao)
        glBindBuffer(GL_ARRAY_BUFFER, vbos[0])
        glEnableVertexAttribArray(0)
        #glVertexAttribPointer函数的参数指定了顶点属性的位置、大小、类型、是否归一化、步长以及偏移量。
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, self.current_vertices.itemsize * 3, ctypes.c_void_p(0))
        #ctypes.c_void_p(0)用于指定顶点属性数据的偏移量。在这个例子中,由于数据是紧密打包的,且从缓冲区的开始位置读取,所以偏移量为0。
        glBindBuffer(GL_ARRAY_BUFFER, 0)

        glBindBuffer(GL_ARRAY_BUFFER, vbos[1])
        glEnableVertexAttribArray(1)
        glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, self.current_colors.itemsize * 3, ctypes.c_void_p(0))
        glBindBuffer(GL_ARRAY_BUFFER, 0)
		#将VAO的绑定解除。
        glBindVertexArray(0)

点云拾取

在点云标注中,获取点云坐标是一个十分重要的功能。
首先,双击鼠标触发事件,这里,获取点云坐标的功能是将该点作为坐标中心点

def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent):
		#获取点击的坐标
        x, y = event.pos().x(), self.height() - event.pos().y()
        if self.pointcloud is None:
            return
        #计算当前点的点云位置
        point = self.pickpoint(x, y)
        print(point)
        # 双击点移动到坐标中心
        if point.size:
            self.vertex_transform.setTranslationwithRotate(-point[0], -point[1], -point[2])
        self.update()

计算点云坐标的代码实现如下:

def pickpoint(self, x, y):
        if self.pointcloud is None:return np.array([])

        point1 = QVector3D(x, y, 0).unproject(self.camera.toMatrix() * self.vertex_transform.toMatrix(),
                                              self.projection,
                                              QtCore.QRect(0, 0, self.width(), self.height()))
        point2 = QVector3D(x, y, 1).unproject(self.camera.toMatrix() * self.vertex_transform.toMatrix(),
                                              self.projection,
                                              QtCore.QRect(0, 0, self.width(), self.height()))
        vector = (point2 - point1)  # 直线向量
        vector.normalize()

        # 点到直线(点向式)的距离
        t = (vector.x() * (self.current_vertices[:, 0] - point1.x()) +
             vector.y() * (self.current_vertices[:, 1] - point1.y()) +
             vector.z() * (self.current_vertices[:, 2] - point1.z())) / (vector.x() ** 2 + vector.y() ** 2 + vector.z() ** 2)

        d = (self.current_vertices[:, 0] - (vector.x() * t + point1.x())) ** 2 + \
            (self.current_vertices[:, 1] - (vector.y() * t + point1.y())) ** 2 + \
            (self.current_vertices[:, 2] - (vector.z() * t + point1.z())) ** 2

        pickpoint_radius = self.pickpoint_radius * self.ortho_change_scale
        mask = d < pickpoint_radius**2

        if not any(mask):
            return np.array([])

        mask1 = self.mask.copy()
        mask1[mask1==True] = mask
        points = self.pointcloud.xyz[mask1]
        index = np.argmin(points[:, 0] * vector.x() + points[:, 1] * vector.y() + points[:, 2] * vector.z())
        point = points[index]   # 取最近的点
        return point

效果如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彭祥.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值