pyqtgraph:GLSurfacePlotItem如何在三维地形表面自定义颜色分布(读取图片给三维平面分区域着色)

本文探讨了如何在Pyqtgraph的GLSurfacePlotItem上使用matplotlib的terrain颜色分布,并尝试通过传入自定义地貌图片实现按区域着色。解决了color_list的限制,最终通过修改glColorPointer类型解决了颜色划分问题,但颜色显示仍有待优化。
摘要由CSDN通过智能技术生成

一、关于pyqtgraph

  来找这个问题的朋友想必是在使用pyqtgraph,并且知道怎样用GLSurfacePlotItem部件绘图的基本操作了。这是一个使用OpenGL加速3D绘图的Python工具包,除了三维数据的可视化之外,同样也可以用于2D数据可视化与数据实时更新展示,启用GPU加速其效率可想而知,相关功能在这里不再赘述。
  由于课题研究需要,本人在绘制三维高程图的过程中,不想按照不同高度来区分三维地形颜色,而是按照不同地貌划分颜色区域,花了很长时间去查资料都没有找到相关内容,有的只是自定义颜色区间,同样全网又是千篇一律的无用答案,本文只要记录开发过程的解决按区域划分pyqtgraph:GLSurfacePlotItem部件的平面图着色问题,并且尚有未解决的缺陷,望大神不吝赐教,其他问题请移步 官方说明文档.。

二、本文要实现的功能

1.默认效果

  在读取高程数据,得到元素值为地形高度的二维矩阵ele(此处以ele.shape=(200,200)为例)后,使用

import pyqtgraph.opengl as gl
p = gl.GLSurfacePlotItem(z=ele, shader='shaded')	# shaded效果是取消了反光,没有这个参数值生成的平面亮度很高

得到的可视化效果为:
默认效果图

图1

2.使用matplotlib自带的terrain地形颜色

  如果在GLSurfacePlotItem中使用matplotlib自带的地形颜色分布(‘terrain’)

import pyqtgraph.opengl as gl
from matplotlib import pyplot as plt
terrain_map = plt.get_cmap('terrain')
land_map = plt.get_cmap('Dark2')
map_color = terrain_map((ele - low) / (high - low))		# 这里的low和high分别指的是ele矩阵中最小值和最大值
p = gl.GLSurfacePlotItem(z=ele / 50, shader='shaded', colors=map_color)

将得到如下可视化效果:
在这里插入图片描述

图2

3.按地貌划区分布的颜色效果

  虽然图2很美观了,但是这并不是我想要的颜色分布,这种颜色分布是根据地形高度自动生成的,而我想要的是在表面生成以下分区效果的颜色分布(即按照不同地貌划分不同区域,该效果在地形表面展示出来,而高度只需要转动地形视角观看他的三维视觉效果):
在这里插入图片描述

图3

  经过漫长的折腾,终于勉强达到需求…吧,长这样(缺陷如你所见,与想要的原分布图比起来颜色发生异常=.=):
在这里插入图片描述

图4

三、最初的思路及遇到的问题

  接下来就好好谈一谈GLSurfacePlotItem 部件的下面几个参数吧

1.关于参数color_list(只可以用来更改按高度显示的颜色区间)

  网上能查到的资料都是使用这个参数来调整成自己想要的颜色区间,并不是想要的颜色分布注意颜色区间指的是颜色值范围,分布则是上图需求所示。时间有限,这种满天飞的内容此处不多说了,想要实现这个需求的可以去查关键字 pygtgraph GLSurfacePlotItem color_list 自定义颜色,全是这方面的。

2.关于参数colors(满足本文需求的突破点)

  在全网查资料的过程中,对于本文需求来说,唯一有用的一个知识点,就是pyqtgraph中GLSurfacePlotItem部件的参数可以传入像前文所述的matplotlib自带颜色分布,甚至还可以传入具有alpha通道的彩色图像!一看到后面这句就看到了希望!如果能传入四通道图像,那岂不是能够实现完全自由的自定义颜色分布了,在三维地表绘制地貌分布的需求不就迎刃而解?大胆一点假设,如果有平面卫星图,岂不是可以用Python的pyqtgraph替代ArcGis那种麻烦的三维地形图复原过程(不过仔细一想不太现实,小范围还是有可能的,但是一个高程数据就那么大,连完整读取速度和内存都是个问题)

  言归正传,我立即用PS自己画了一个RGB彩色图像(图3所示),当做我想要的地貌分布,然后开始尝试铺在我读取的三维高程地形图上。果然,事情并没有想象中的那么简单…

  首先通过PIL.Image读取自己的地貌图片,然后为其添加alpha通道,转为RGBA四通道图像,数据类型转换到numpy,当将它赋给colors参数时,直接运行却得到了以下结果:
在这里插入图片描述

图5

  显然颜色分布出现异常,不同颜色也会被划分到相同区域,便开始以为是自己对该方法和该参数的使用方式不对,经过反复检查确认后,RGB图像转换为RGBA的过程并没有错,又是一番查资料看官方文档,结果收获寥寥无几,但是面对自己课题研究的刚需,好在还有最后一条路——硬着头皮看源码。

四、解决过程

  借助pycharm的方便依次打开GLSurfacePlotItem部件代码,尤其是追溯colors参数的使用过程,包括这个参数传给了谁,一直到最后是谁用什么方式绘制传入的colors颜色值,一层一层深入,终于找到了问题的根源。

  根据pycharm对类和方法的追踪(Ctrl+鼠标单击),并查看colors参数传递过程,其中混入了对于其他参数的判断条件增加了阅读难度,所以本人还是选择print大法好,说到底还是菜啊:),这样便得知colors到底走的判断语句中哪一步了,colors传递的路程输出如下:
在这里插入图片描述
  根据自己定义的print输出可以看到经过哪个类的哪个方法,以及出现if-else语句时走的是哪一条语句,除此之外还可以看到,输出colors第一行数据片段发现,四通道的颜色值自始至终未发生任何变化,疑惑又加深了。
跟踪GLSurfacePlotItem类,进入其构造方法,可以看到colors在构造方法中传入了setData()方法,
在这里插入图片描述
所以继续跟踪setData(),这时候来到了GLSurfacePlotItem.py文件,在setData()方法中colors被传给了_meshdata对象,
在这里插入图片描述
于是再回到构造方法查看_meshdata对象,他是另一个MeshData类的对象,可以得知,上方在setData()方法中,传递colors所用的setVertexColors()方法必定是在MeshData类中,继续跟踪MeshData类,
在这里插入图片描述
在该类中追溯到setVertexColors()方法时,再次看到colors被传递的过程,这次是传给了类中的_vertexColors对象,但是这里只能看到传递过程,却没有了如何继续使用,
在这里插入图片描述
但是搞清楚前面的_meshdata对象后,再回到GLSurfacePlotItem.py,继续往下看,下面的self.setData()方法我们已经看过了,但没有绘制过程,因此可以推断,绘制过程在中间的GLMeshItem.init()实现,所以这时候再去查看这个GLMeshItem构造方法做了什么。
在这里插入图片描述
当进入这个GLMeshItem.py文件后,其实不需要再花多余的时间了,因为最下面有个很明显的方法叫paint(),这里一定就是绘制过程了,并且能够看到glColorPointerf(color)这一步,就是color被传送的终点!
在这里插入图片描述
  跟踪glColorPointerf()的位置后,在其对应的pointers文件中可以看到,这里可用的着色方式有很多种,每个glColorPointer后面都有额外的一两个字母,指的是不同数据类型,然而而整个colors传递过程中并没有看到哪里是可以更改这个着色方式的,哪怕colors切换了不同的数据类型,依旧是执行glColorPointerf类型,所以在这里开始尝试更改类型,以解决颜色划分不正确的问题。
在这里插入图片描述

五、最终解决方案

  根据前文,在尝试修改了各种类型后,发现有的类型导致地形图直接全黑,有的跟图5一样颜色划分出现错误,只有将源码中GLMeshItem.py文件内paint()方法中,glColorPointerf修改为glColorPointerb时,如下:
在这里插入图片描述

  颜色划分终于正确了,可惜显示的颜色不对,尚没有解决,如图6。
在这里插入图片描述

图6

  由于时间有限,只是对glColorPointer简单搜索了解了一下,发现这部分正是OpenGL方面的内容,我对于OpenGL几乎一无所知,关于这个颜色显示异常的问题,只有熟悉OpenGL的大佬帮忙解答了,或许能帮忙找到最准确的实现本文需求的方式,还请不吝赐教!

原创不易,转载请注明出处!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值