当前位置:首页>python>Python学习|RS Python入门02:列表、字典与流程控制:像元集合的编队【六】函数

Python学习|RS Python入门02:列表、字典与流程控制:像元集合的编队【六】函数

  • 2026-10-11 08:05:12
Python学习|RS Python入门02:列表、字典与流程控制:像元集合的编队【六】函数

点击上方蓝字 关注我们

2026

诸位,在上一章《Python学习|RS Python入门02:列表、字典与流程控制:像元集合的编队【五】简单语句》中,我们学习了如何用循环让程序重复干活。但也许你可能已经发现:如果一段重复操作特别复杂,比如对一景遥感影像做辐射定标、大气校正、裁剪、计算NDVI……每次循环都写一遍这几十行代码,既臃肿又难维护。

这时候,我们来介绍一个新的工具,函数就登场了。

函数是什么?简单说,就是把一段有特定功能的代码打包,给它起个名字。以后想用这段功能,只需要“喊”它的名字就行,不用再把代码重新写一遍。

生活中,函数就像“菜谱”一样,想象你学会了做“西红柿炒鸡蛋”。只要记住这个名字,以后随时需要,就照菜谱做一遍,而不是每次都重新研究怎么切西红柿、怎么打鸡蛋。

遥感中的函数就像预处理流程一样重复使用。

在遥感数据处理中,函数可以把常用操作封装起来,大大提高工作效率:

  • 批量计算植被指数(如 NDVI、SAVI、EVI);

  • 统一对多景影像进行辐射定标、大气校正;

  • 将不同影像重采样到相同分辨率;

  • 对时间序列数据做平滑滤波(如 S-G 滤波);

  • 自动批量读取、预处理和保存遥感数据;

  • 实现质量控制掩膜(QA)的解码与筛选……

Part.01

函数的基础知识

1.1 定义和调用函数

大家学编程,最耳熟能详的便是hello world语句吧,好今天便继续来到这个语句,当然我们呢通过函数实现而非直接print他。

首先让我们来定义一个Hello函数,如下

def say_hello():    print("诸君,欢迎来到GeoPyLab!")    print("请保证你的思维时刻在线~")

好了我们应该如何去调用呢?

say_hello()

至此我们便可以得到如下的运行结果:

下面我们来观察一下定义到调用一个函数的一些关键点:

  • def 是关键词,表示定义二字,这是固定格式,熟记即可

  • say_hello是函数名,这个自己起,要见名知意,不要过于复杂,当然也要保证自己函数的可读性,即就是要保证别人拿到你的代码能够看明白你的目的是什么

  • () 用来放参数,倘若没有参数就空着

  • : 表示函数体开始,下面便要开始定义你的函数内容

  • 函数内部的代码要缩进

  • 定义函数时,函数体内的代码不会立即执行只有调用函数时,函数体内的代码才会运行

1.2 带参数的函数,即可给函数提供数据

很多时候,函数需要外部数据才能工作。比如写一个计算 NDVI 的函数,它需要知道红波段和近红外波段的数值。

下面我们便开始进一步了解一些内容,我们先来定义一个计算NDVI的函数,具体计算公式我们不再展开赘述。

def calculate_ndvi(red, nir):    ndvi = (nir - red) / (nir + red + 1e-10)    return ndvi

好的我们下一步便开始在调用函数时传入一些数据:

result = calculate_ndvi(0.15, 0.45)print(f"像元1的NDVI值为:{result:.3f}")

当然了,我们也可以直接传入一些变量:

band_red = 0.12band_nir = 0.48result2 = calculate_ndvi(band_red, band_nir)print(f"像元2的NDVI值为:{result2:.3f}")

运行之后我们便可以得到如下的结果:

总是我们可以看到两种函数均可完成参数的传入,在实际编程的过程中我们可以择优进行选取使用哪种方式。

好的,诸位我们来看一些关键点:

  • red, nir 是形参,即形式参数,就像菜谱里的鸡蛋 需要3 个一样

  • -0.15, 0.45是实参,也就是实际给的数据一样

  • return是返回值,也就是把计算结果送出来,函数的出口便在此处

Part.02

函数的进阶用法

2.1 带默认参数的函数,即可省去重复输入

有些参数大部分情况都一样,可以设置默认值。下面我们来看一个关于植被指数计算的函数例子,我们定义一个计算EVI的函数,在其中我们可以使用一些默认参数:

def calculate_evi(nir, red, blue, G=2.5, C1=6.0, C2=7.5, L=1.0):    evi = G * (nir - red) / (nir + C1 * red - C2 * blue + L + 1e-10)    return evi

至此我们便完成了EVI计算的定义,诸位,下面便开始不同方式的调用:

首先我们只传入必要的参数,其他值均为函数定义时的默认值:

evi1 = calculate_evi(0.45, 0.15, 0.08)print(f"EVI1 = {evi1:.3f}")

当然我们也可以在调用函数时,手动修改一些默认参数的值:

evi1 = calculate_evi(0.45, 0.15, 0.08)print(f"EVI1 = {evi1:.3f}")

同样我们也可以按参数位置去逐个修改函数参数:

evi3 = calculate_evi(0.45, 0.15, 0.08, 2.5, 6.0, 7.5, 0.5)print(f"EVI3 = {evi3:.3f}")

运行上述结果我们便可以看到即便是传入的近红外、红外、蓝波段的数值一样,但是由于参数的不同我们得到的结果是不相同的:

在此向诸位提个小小的注意事项,就是在定义函数时带默认值的参数必须放在没有默认值的参数后面。

2.2 关键字参数,即便是打乱顺序也能传

如下我们来通过一个简单的小例子来对其进行说明:

def describe_image(name, sensor, resolution, date):    print(f"影像名称:{name}")    print(f"传感器:{sensor}")    print(f"分辨率:{resolution}米")    print(f"获取日期:{date}")

我们可以正常按位置进行传参:

describe_image("影像001", "Landsat-8", 30, "2024-07-15")

当然也可以不按参数位置进行传参,这里便需要加上形参的名称:

describe_image(date="2024-07-15", name="影像002", resolution=10, sensor="Sentinel-2")

运行上述内容便可以得到如下的结果:

也就是说,关键字参数是为了让代码更易读,也更方便只修改某几个参数。

2.3 可变参数,处理不确定数量的输入

诸位,在有时候编程的过程中,你不知道用户会传几个值,比如要计算多个波段的平均值。

首先我们来看*args ,其可以接收任意多个位置参数,将其打包成元组,如下我们要计算某一像元多个波段的平均值:

def calculate_mean(*bands):    if len(bands) == 0:        return 0    return sum(bands) / len(bands)print(calculate_mean(0.1, 0.2, 0.3, 0.4))print(calculate_mean(0.15, 0.45))print(calculate_mean())

当然也可以使用**kwargs 接收任意多个关键字参数,将其传入的参数打包成字典,如下我们打印某一影像元数据:

def print_metadata(**metadata):      for key, value in metadata.items():            print(f"{key}: {value}")print_metadata(      name="LC08_L1TP_123038",      cloud_cover=12.5,      acquisition_time="10:30:00",      orbit=123)

便可得到如下的结果:

Part.03

函数循环结合,实现批量处理

3.1 实战练习,二者结合批量计算 NDVI

首先我们假设有三景影像的波段均值,想批量计算 NDVI,不需要写三次公式,只需要定义函数,将其结合循环便可以实现批量循环处理:

首先我们来定义NDVI计算函数:

def ndvi(red, nir):      return (nir - red) / (nir + red + 1e-10)

而后我们来传入一些影像参数与波段值:

images = [    ("Landsat_01", 0.12, 0.38),    ("Landsat_02", 0.14, 0.42),    ("Sentinel_01", 0.10, 0.35),    ("Sentinel_02", 0.11, 0.40),]

接下来我们便可以使用for循环对其进行批量运算:

for name, r, n in images:    value = ndvi(r, n)    print(f"{name:12} red={r:.3f} nir={n:.3f} 经过计算其NDVI = {value:.3f}")

运行上述内容,我们便可以得到如下的结果:

3.2 条件筛选,进一步结合找出植被覆盖度高的影像

诸位,我们可以使用函数与循环结合,借助NDVI来招数植被覆盖较高的影像,首先我们定义这个函数:

def classify_vegetation(ndvi_value):    if ndvi_value < 0:        return "水体/非植被"    elif ndvi_value < 0.2:        return "裸土/稀疏植被"    elif ndvi_value < 0.4:        return "中等植被覆盖"    elif ndvi_value < 0.6:        return "高植被覆盖"    else:        return "极高植被覆盖"

而后我们使用循环进行逐个寻找:

for name, r, n in images:    value = ndvi(r, n)    category = classify_vegetation(value)    print(f"{name} NDVI={value:.3f} -> {category}")

运行上述内容,我们便可以得到如下的结果:

3.3 批量保存处理结果

我们先来模拟一个影像批处理过程:

def process_image(name, red, nir):      ndvi_value = ndvi(red, nir)      category = classify_vegetation(ndvi_value)      return {            "image_name": name,            "ndvi": ndvi_value,            "category": category      }

而后我们创建结果并使用循环逐个保存:

results = []for name, r, n in images:      result = process_image(name, r, n)      results.append(result)for res in results:      print(f"{res['image_name']:12} NDVI={res['ndvi']:.3f} 分类={res['category']}")

即可得到如下的结果:

Part.04

综合实战,完整的遥感影像批处理框架

好了,经过上面的一些函数应用,诸位应该已经明白了函数的一些简便方法,下面我么来进行遥感影像批处理演示:

1. 定义各种植被指数的计算函数

2. 定义植被检查函数

3. 批量处理多景影像

4. 生成结果报告

好的,我们按上面的内容逐步完成:

首先我们先来定义一些植被指数的计算函数,如下:

import mathdef ndvi(red, nir, epsilon=1e-10):    return (nir - red) / (nir + red + epsilon)def ndwi(green, nir, epsilon=1e-10):    return (green - nir) / (green + nir + epsilon)def evi(nir, red, blue, G=2.5, C1=6.0, C2=7.5, L=1.0, epsilon=1e-10):    return G * (nir - red) / (nir + C1 * red - C2 * blue + L + epsilon)def savi(red, nir, L=0.5, epsilon=1e-10):    return ((nir - red) / (nir + red + L + epsilon)) * (1 + L)def msavi2(red, nir, epsilon=1e-10):    numerator = 2 * nir + 1 - math.sqrt((2 * nir + 1) ** 2 - 8 * (nir - red))    denominator = 2 + epsilon    return numerator / denominator

而后进一步定义植被检查函数,具体用于判读此处为何种地物:

def classify_vegetation(ndvi_value):    if ndvi_value < 0:        return "非植被"    elif ndvi_value < 0.2:        return "稀疏植被"    elif ndvi_value < 0.4:        return "中等植被"    elif ndvi_value < 0.6:        return "茂密植被"    else:        return "植被全覆盖"def water_detection(ndwi_value, threshold=0):    return ndwi_value > threshold

当计算与判读均已完成之后,我们下一步便是要进行影像的读取:

def process_single_image(image_id, bands, metadata=None):    result = {        "image_id": image_id,        "indices": {},        "classification": {},        "metadata": metadata or {}    }    blue = bands.get('blue', 0)    green = bands.get('green', 0)    red = bands.get('red', 0)    nir = bands.get('nir', 0)    result["indices"]["NDVI"] = ndvi(red, nir)    result["indices"]["NDWI"] = ndwi(green, nir)    result["indices"]["EVI"] = evi(nir, red, blue)    result["indices"]["SAVI"] = savi(red, nir)    result["indices"]["MSAVI2"] = msavi2(red, nir)    ndvi_val = result["indices"]["NDVI"]    result["classification"]["veg_type"] = classify_vegetation(ndvi_val)    result["classification"]["is_water"] = water_detection(result["indices"]["NDWI"])    return result

当然我们在实际应用中都是批量去生成、运行计算的,故而我们使用for循环进一步撰写批量运行处理函数:

def batch_process_images(image_dataset):    all_results = []    for idx, (img_id, bands, metadata) in enumerate(image_dataset, 1):        print(f"\n[{idx}/{len(image_dataset)}] 处理影像: {img_id}")        print(f"  波段值: B={bands.get('blue',0):.3f} G={bands.get('green',0):.3f} "              f"R={bands.get('red',0):.3f} NIR={bands.get('nir',0):.3f}")        result = process_single_image(img_id, bands, metadata)        all_results.append(result)        print(f"  NDVI = {result['indices']['NDVI']:.3f}")        print(f"  植被类型: {result['classification']['veg_type']}")        print(f"  是否水体: {'是'if result['classification']['is_water'] else'否'}")    return all_results

而后我们便要对处理的结果做一些总结,以显示我们所要处理的目的:

def generate_report(results):    water_count = sum(1 for r in results if r['classification']['is_water'])    veg_stats = {}    for r in results:        veg_type = r['classification']['veg_type']        veg_stats[veg_type] = veg_stats.get(veg_type, 0) + 1    print(f"\n影像总数: {len(results)}")    print(f"包含水体影像数: {water_count}")    print("\n植被覆盖统计:")    for veg_type, count in veg_stats.items():        print(f"  {veg_type}: {count} 景")    print("\n" + "-" * 60)    print(f"{'影像ID':<15}{'NDVI':<8}{'NDWI':<8}{'EVI':<8}{'植被分类':<15}")    print("-" * 60)    for r in results:        idx = r['indices']        print(f"{r['image_id']:<15}{idx['NDVI']:<8.3f}{idx['NDWI']:<8.3f} "              f"{idx['EVI']:<8.3f}{r['classification']['veg_type']:<15}")

最后我们便要做的是函数的调用,我们模拟一些数据来测试我们的函数是否正确运行:

if __name__ == "__main__":    test_dataset = [        ("LC08_20240715", {"blue": 0.08, "green": 0.12, "red": 0.15, "nir": 0.45},         {"sensor": "Landsat-8", "cloud": 5.2}),        ("LC08_20240801", {"blue": 0.09, "green": 0.13, "red": 0.18, "nir": 0.42},         {"sensor": "Landsat-8", "cloud": 8.1}),        ("S2A_20240720", {"blue": 0.07, "green": 0.10, "red": 0.12, "nir": 0.48},         {"sensor": "Sentinel-2", "cloud": 2.3}),        ("S2A_20240805", {"blue": 0.10, "green": 0.15, "red": 0.22, "nir": 0.35},         {"sensor": "Sentinel-2", "cloud": 15.6}),        ("LC08_20240820", {"blue": 0.20, "green": 0.18, "red": 0.16, "nir": 0.20},         {"sensor": "Landsat-8", "cloud": 0.5}),    ]    results = batch_process_images(test_dataset)    generate_report(results)

好了诸位,我们逐个运行上述代码便可以得到如下的结果:

至此,诸位,我们便已完成了整个函数流程的学习,同样,我们也完成了整个遥感影像的处理流程。

Part.5

思考与练习

函数是 Python 中实现代码复用的核心工具。通过 def 关键字,我们可以将一段具有特定功能的代码封装起来,给它起一个名字,然后反复调用。

当然诸位,本章的知识较为零碎,故而会刚开始实操可能会有的云里雾里,不过诸位,相信你们可以做到的。

好的,下面我们来做一个简短的知识梳理,帮助诸位更好的理解本章的内容。

好相信诸位已经跃跃欲试了吧,展示一下吧,下面来做两个思考:

  1. 写一个函数,接收两个参数 temperature和 emissivity,返回地表温度计算公式T = brightness_temp / emissivity的结果。

def land_surface_temperature(brightness_temp, emissivity):    T = brightness_temp / emissivity    return Tbt = 295.5em = 0.98lst = land_surface_temperature(bt, em)print(f"亮度温度: {bt} K")print(f"比辐射率: {em}")print(f"地表温度: {lst:.2f} K")print(f"地表温度: {lst - 273.15:.2f} °C")

好的,通过上述的代码运行,我们便可以得到如下的结果:

2. 写一个函数 batch_ndvi,接收一个影像列表,为了简化诸位的思考,假设该列表为每个元素是红波段和近红外波段的列表,而后返回所有 NDVI 值列表,并用循环调用它。

def ndvi(red, nir):    epsilon = 1e-10    return (nir - red) / (nir + red + epsilon)def batch_ndvi(image_list):    results = []    for item in image_list:        name, red, nir = item        value = ndvi(red, nir)        results.append((name, value))        print(f"{name}: 红={red:.3f}, 近红={nir:.3f} -> NDVI={value:.4f}")    return resultsimages_with_names = [    ["Landsat_01", 0.12, 0.38],    ["Landsat_02", 0.14, 0.42],    ["Sentinel_01", 0.10, 0.35],    ["Sentinel_02", 0.11, 0.40],    ["GF1_01", 0.13, 0.37],]result = batch_ndvi(images_with_names)print("\nNDVI 值列表:")for name, value in result:    print(f"  {name}: {value:.4f}")
我们通过上述两个小练习,应该更好的理解了函数这个工具。

如果你觉得“原来Python也能讲得这么遥感”,点个关注,后续系列推文将第一时间推送:

  • 文件与目录操作

  • NumPy入门

你在处理遥感数据时,遇到过哪些让循环“卡住”的场景?是千万级像元的遍历太慢,还是海量文件的批处理总报错?评论区聊聊,下期可能就针对你的痛点来讲解

用Python讲遥感,我们不讲噱头,只讲干货。

附赠可运行的 Notebook 源码,自行上传即可上手。本公众号后台回复【RS Python入门02-6】即可获取本节 Notebook 源码。

最新文章

随机文章