python由tif影像绘制png图片并制作动图

笔者最近需要下载一个区域的长时间序列影像,然后将其制作成动图展示其动态变化过程。这其中涉及到两个问题,一是将tif数据绘制成PNG或jpg等格式图片,二是由图片绘制动图GIF。

一、由TIF绘制PNG

 这里需要用到matplotlib和GDAL两个库来将tif格式图像绘制为PNG图片,如果没有相关包,则需要提前pip安装。

1.1 导入相关库

from osgeo import gdal
import numpy as np
import os
#os.environ['PROJ_LIB'] = r'E:\ProgramData\Anaconda3\envs\qt39\Lib\site-packages\pyproj\proj_dir\share\proj'
# os.environ['GDAL_DATA'] = r'E:\ProgramData\Anaconda3\envs\qt39\Library\share'
import random
import matplotlib.pyplot as plt
import numpy as np
import imageio

1.2 确定文件读取和输出路径

tifDir = './tifs'
outputDir = './imgs'
if not os.path.exists(outputDir):
    os.makedirs(outputDir)
 
tifs = [i for i in os.listdir(tifDir) if i.endswith(".tif")]
 
print("有 %s 个tif文件" % len(tifs))

1.3 绘制图像

tif = tifs[0]
year = tif.split('_')[2].split('.')[0]
print("正在处理第 %s 年" % year)
path = os.path.join(tifDir,tif)
ds = gdal.Open(path)

def scaleCCC(x):
    return((x - np.nanpercentile(x, 2))/(np.nanpercentile(x, 98) - np.nanpercentile(x,2)))
nir = ds.GetRasterBand(1).ReadAsArray()
r = ds.GetRasterBand(2).ReadAsArray()
g = ds.GetRasterBand(3).ReadAsArray()

ds = None

nir = scaleCCC(nir)
r = scaleCCC(r)
g = scaleCCC(g)

rgb = np.dstack((nir,r,g))
plt.figure()
plt.imshow(rgb)
plt.xticks([])
plt.yticks([])
plt.text(rgb.shape[1]-4, 15, year, fontsize=15, color='white', 
                 verticalalignment='bottom', horizontalalignment='right', 
                 bbox=dict(facecolor='black', alpha=0.5))
plt.tight_layout()

plt.savefig(os.path.join(outputDir, year + '.png'),dpi=300,bbox_inches='tight', pad_inches=0.05)
# plt.show()

这里面是读取了影像的三个波段,分别映射为RGB三个通道,使用np.dstack合成,用线性拉伸的方法增强图像展示效果。

二、由PNG图片绘制GIF动图

2.1 先将PNG图片按照时间顺序排列好

笔者发现,用os.lisdir批量读取文件夹路径后,发现其并不是按照 年份数值大小从小到大读取的,顺序是乱的(其实是根据文件名ASCII码大小从左到右比较后的顺序读取)。因而需要通过某种方式使其读取文件的顺序是我们想要的,即按照文件名中的年份大小一次读取。

imgDir = './imgs'
imgs = [i for i in os.listdir(imgDir) if i.endswith(".png")]

#先定义一个排序的空列表
sort_num_list = []
for img in imgs:
    sort_num_list.append(int(img.split('.')[0])) #去掉前面的字符串和下划线以及后缀,只留下数字并转换为整数方便后面排序
    sort_num_list.sort() #然后再重新排序
    
#print(sort_num_list)
#接着再重新排序读取文件
sorted_file = []
for sort_num in sort_num_list:
    for img in imgs:
        if str(sort_num) == img.split('.')[0]:
            sorted_file.append(img)
            
print(sorted_file)
pathlist = []
for file in sorted_file:
    pathlist.append(os.path.join(imgDir,file))
print(pathlist)

 2.2 绘制GIF

import imageio
 
def imgs2gif(imgPaths, saveName, duration=None, loop=0, fps=None):
    """
    生成动态图片 格式为 gif
    :param imgPaths: 一系列图片路径
    :param saveName: 保存gif的名字
    :param duration: gif每帧间隔,单位 秒
    :param fps: 帧率
    :param loop: 播放次数(在不同的播放器上有所区别), 0代表循环播放
    :return:
    """
    if fps:
        duration = 1 / fps
    images = [imageio.imread(str(img_path)) for img_path in imgPaths]
    imageio.mimsave(saveName, images, "gif", duration=duration, loop=loop, fps = 1/duration)
 
 
 
p_lis = []
for n, p in enumerate(pathlist):
    # if n % 5 == 0:
    p_lis.append(p)
 
imgs2gif(p_lis, "spatiotemporal_change_yh.gif", duration=1, loop = 0)

2.3 压缩动图

如果遇到动图文件过大,则可能需要对其进行压缩,一般是降低每一帧图片的大小。


import imageio
from PIL import Image
import io

def compress_gif(input_path, output_path, quality=30):
    """
    压缩GIF动图
    :param input_path: 输入GIF文件的路径
    :param output_path: 输出压缩后GIF文件的路径
    :param quality: 压缩质量,范围从1到95,数值越小压缩率越高
    """
    # 使用get_reader逐帧读取GIF
    reader = imageio.get_reader(input_path)

    # 准备输出文件
    output_file = io.BytesIO()

    # 保存每一帧,同时指定压缩质量
    optimize = True if quality < 90 else False
    save_kwargs = {
        'format': 'GIF',
        'optimize': optimize,
        'quality': quality,
        'loop': 0  # 设置为0表示无限循环
    }
    
    # 转换每一帧为Pillow Image对象并保存
    frames = []
    for frame in reader:
        img = Image.fromarray(frame)
        frames.append(img)

    frames[0].save(output_file, save_all=True, append_images=frames[1:], **save_kwargs)

    # 将压缩后的GIF写入文件
    with open(output_path, 'wb') as f:
        f.write(output_file.getvalue())

# 示例使用
compress_gif('spatiotemporal_change_yh.gif', 'spatiotemporal_change_yh1.gif', quality=50)

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值