使用matplotlib绘制3D图像时插入图片

       最近学了matplotlib,想要在绘制3D图像的时候插入图片,但在网上找来找去没找到比较好的,唯一找到的一个速度有点慢。于是就自己动手吧。

        先放下在stackoverflow看到的答案:

# 注释为国外大佬写的,汉字部分我的补充
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.cbook import get_sample_data
import cv2

# Read the image with Opencv
img = cv2.imread('test.png')
# Change the color from BGR to RGB
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# Orgird to store data
x, y = np.ogrid[0:img.shape[0], 0:img.shape[1]]
# In Python3 matplotlib assumes rgbdata in range 0.0 to 1.0
img = img.astype('float32')/255
fig = plt.Figure()
# gca do not work thus use figure objects inbuilt function.
ax = fig.add_subplot(projection='3d')

# Plot data
# rstride,cstride相当于设置图片显示的像素,当两项均等于1的时候相当于我写的程序
ax.plot_surface(x, y, np.atleast_2d(0), rstride=10, cstride=10, facecolors=img)
fig.show() # Throws a AttributeError

        这个方法我试了试,但有点慢,画一个800*800的图片需要将近50s。

        matplotlib的3D的画图应该是没有直接提供插入图片的选项的,那自己划入图片的思维最简单就是一个一个的写入。

        使用Image先获得图片,再读取其size,其中size[0]为宽,size[1]为高。

img = Image.open('african.jpg')
pix = img.load()
width = img.size[0]
height = img.size[1]

        获得宽高之后便可以使用numpy创建x,y轴的列表。我的图片是800*800的,所以这里的宽高就是两个长度为800的列表。

        而后使用meshgrid()函数(实际上我并不清楚这个函数的用处,应该是类似将两个数组中的元素匹配。如果没有这一步,画出来的图将会只有800个点,有了这一步才能画出一张640000像素的画。如果有大佬知道这个函数具体干啥的希望能指导下)

        接下来创建图像的z轴坐标。因为这张图像使用的是散点画法,总共要画640000个点,所以应该有这么多的z轴坐标,并且应该将其设置为一个一维数组而不是二维,二维数组放入应该会报错。

x = np.arange(0,width)
y = np.arange(0,height)
x,y = np.meshgrid(x,y)
z = np.zeros(width*height)

        然后再处理颜色。根据其官方文档中的关于color的描述,可以看出c传入的参数可以是一个代表颜色的字符串,或者是一个长度为N的列表,或者是一个2维的数组。但二维数组我尝试过却报错了,不知道是我哪里写错了。有大佬的画希望可以指点一下。

使用matplotlib绘制3D图像时插入图片_第1张图片

         那么剩下的就是获取每个像素点的像素值了。通过遍历长宽可以获得每个像素点的rgb值,其中pix[x,y]返回的按次序为rgb的十进制值。但color参数似乎并不支持直接传10进制的rgb值,他支持16进制的颜色值或者是一个三元素元组,其中每个元素代表这种颜色所占比例,因此每个10进制的值需要除以255。

        如果不需要每个像素都一样的,可以设置其步长,效果相当于前面代码的更改rstride的值。

color = []
for i in range(width):
    for j in range(height):
        rgb= tuple(np.array(pix[i, j])/255)
        color.append(rgb)

        最后直接开始画图。alpha = 1代表不透明度为100%

ax.scatter(x,y,z,c = color,alpha = 1)

        运行结果如下 

使用matplotlib绘制3D图像时插入图片_第2张图片       

 

        下面是完整的代码,可以和上面的代码实现一样的功能,速度更快了,运行大概需要15s:

from PIL import Image
import numpy as np
from matplotlib import pyplot as plt

# 创建3D对象
fig = plt.figure(figsize=(20,8),dpi=100)
ax = fig.add_subplot(111, projection='3d')

# 读取图片
img = Image.open('test.jpg')
pix = img.load()
# 获取图片长宽
width = img.size[0]
height = img.size[1]
# 创建x,y轴的长度
x = np.arange(0,width)
y = np.arange(0,height)
x,y = np.meshgrid(x,y)
z = np.zeros(width*height)
# 建立列表,后期使用
color = []
# 遍历长宽,获得每个像素点的RGB值
for i in range(width):
    for j in range(height):
        # 转化rgb为相对占比的元组
        rgb= tuple(np.array(pix[i, j])/255)
        color.append(rgb)

# 在z=0的平面上作图

# 设置颜色,像素
ax.scatter(x,y,z,c = color,alpha = 1)
plt.show()

        代码可以再优化,比如连续出现的相同像素等等。

        新手第一次发帖,纯萌新,有错误希望大佬指出。

你可能感兴趣的:(python,matplotlib)