You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Python Tkinter的NDVI计算TIF图像应用问题求助

修复Tkinter NDVI计算应用的功能问题

我想要制作一个小应用,用于打开并读取.tif图像,计算NDVI(归一化植被指数),并将最终结果显示在应用界面上。尽管代码运行时没有报错,但功能并未正常实现。使用Python和Tkinter开发,代码如下:

import tkinter as tk
import rasterio as rs
from tkinter import *
from tkinter import filedialog
from PIL import ImageTk, Image


root = tk.Tk()
root.geometry("1000x700")


frame = tk.Frame(root, bg='#45aaf2')

lbl_pic_path = tk.Label(frame, text='Image Path:', padx=25, pady=25,
                        font=('verdana',16), bg='#45aaf2')
lbl_show_pic = tk.Label(frame, bg='#45aaf2')
lbl_show_pic2 = tk.Label(frame, bg='#45aaf2')

entry_pic_path = tk.Entry(frame, font=('verdana',16))
btn_browse = tk.Button(frame, text='Select Image',bg='grey', fg='#ffffff',
                       font=('verdana',16))


def selectPic():
    global img
    filename = filedialog.askopenfilename(initialdir="/images", title="Select Image",
                           filetypes=( (("All files", ".*"),("TIFF files", ".tif"))))
    img = Image.open(filename)
    img = img.resize((200,200), Image.ANTIALIAS)
    img = ImageTk.PhotoImage(img)
    lbl_show_pic['image'] = img
    entry_pic_path.insert(0, filename)
    

def calcNDVI():
    global img2
    filename2 = filedialog.askopenfilename(initialdir="/images", title="Select Image",
                           filetypes=( (("All files", ".*"),("TIFF files", ".tif"))))
    img2 = Image.open(filename2)
    img2 = img.resize((200,200), Image.ANTIALIAS)
    img2 = ImageTk.PhotoImage(img2)
    B1 = rs.read((1))
    B2 = rs.read((2))
    NDVI = (B1-B2) / (B1 + B2)
    lbl_show_pic2['image2'] = NDVI


btn_browse['command'] = selectPic

frame.pack()

lbl_pic_path.grid(row=0, column=0)
entry_pic_path.grid(row=0, column=1, padx=(0,20))
lbl_show_pic.grid(row=1, column=0, columnspan="2")
lbl_show_pic2.grid(row=2, column=2, columnspan="4")

btn_browse.grid(row=2, column=0, columnspan="2", padx=10, pady=10)

root.mainloop()

问题分析与修复方案

  • 缺少计算触发按钮:代码定义了calcNDVI函数,但未绑定到任何按钮,无法触发计算逻辑。
  • 遥感影像读取错误:rs.read((1))是错误用法,必须先通过rasterio.open()打开影像文件,获取数据集对象后再读取波段。
  • NDVI计算逻辑问题:公式应为(近红外波段-红波段)/(近红外波段+红波段),且未处理分母为0的情况,会引发计算错误。
  • 图像显示错误:lbl_show_pic2['image2'] = NDVI中,Label的图像属性是image而非image2,且NDVI是数值数组,需转为PIL图像才能显示。
  • 变量引用错误:calcNDVI中误用全局变量img做resize操作,应改为img2.resize。
  • 图像引用丢失:Tkinter的PhotoImage需保留引用才不会被垃圾回收,需将其绑定到Label属性上。

修正后的完整代码

import tkinter as tk
import rasterio as rs
import numpy as np
from tkinter import filedialog
from PIL import ImageTk, Image


root = tk.Tk()
root.geometry("1000x700")
root.title("NDVI计算器")

frame = tk.Frame(root, bg='#45aaf2')

# 界面组件
lbl_pic_path = tk.Label(frame, text='Image Path:', padx=25, pady=25,
                        font=('verdana',16), bg='#45aaf2')
lbl_show_pic = tk.Label(frame, bg='#45aaf2')
lbl_show_ndvi = tk.Label(frame, bg='#45aaf2', text="NDVI结果将显示在这里", font=('verdana',12))

entry_pic_path = tk.Entry(frame, font=('verdana',16), width=30)
btn_browse = tk.Button(frame, text='Select Image', bg='grey', fg='#ffffff',
                       font=('verdana',16))
btn_calc_ndvi = tk.Button(frame, text='计算NDVI', bg='#2d3436', fg='#ffffff',
                          font=('verdana',16))

def selectPic():
    filename = filedialog.askopenfilename(initialdir="/images", title="Select Image",
                           filetypes=(("TIFF files", ".tif"), ("All files", ".*")))
    if not filename:
        return
    # 显示原始图像
    img = Image.open(filename)
    img_resized = img.resize((200,200), Image.Resampling.LANCZOS)  # 适配PIL新版API
    img_tk = ImageTk.PhotoImage(img_resized)
    lbl_show_pic['image'] = img_tk
    lbl_show_pic.image = img_tk  # 保留引用防止回收
    entry_pic_path.delete(0, tk.END)
    entry_pic_path.insert(0, filename)
    
def calcNDVI():
    filename = entry_pic_path.get()
    if not filename:
        lbl_show_ndvi.config(text="请先选择图像")
        return
    try:
        # 读取多波段影像
        with rs.open(filename) as src:
            # 假设波段1为红波段,波段2为近红外波段(根据你的影像调整)
            red_band = src.read(1).astype(np.float32)
            nir_band = src.read(2).astype(np.float32)
            
            # 计算NDVI,处理分母为0的情况
            denominator = red_band + nir_band
            ndvi = np.where(denominator == 0, 0, (nir_band - red_band) / denominator)
            
            # 归一化到0-255范围用于显示
            ndvi_normalized = ((ndvi + 1) * 127.5).astype(np.uint8)
            # 转换为PIL图像
            ndvi_img = Image.fromarray(ndvi_normalized)
            ndvi_img_resized = ndvi_img.resize((200,200), Image.Resampling.LANCZOS)
            ndvi_img_tk = ImageTk.PhotoImage(ndvi_img_resized)
            
            # 显示NDVI图像
            lbl_show_ndvi['image'] = ndvi_img_tk
            lbl_show_ndvi.image = ndvi_img_tk  # 保留引用
            lbl_show_ndvi.config(text="")
    except Exception as e:
        lbl_show_ndvi.config(text=f"计算错误: {str(e)}")

# 绑定命令
btn_browse['command'] = selectPic
btn_calc_ndvi['command'] = calcNDVI

# 布局调整
frame.pack(fill=tk.BOTH, expand=True)
lbl_pic_path.grid(row=0, column=0, sticky=tk.W)
entry_pic_path.grid(row=0, column=1, padx=(0,20), sticky=tk.EW)
lbl_show_pic.grid(row=1, column=0, pady=20, padx=20)
lbl_show_ndvi.grid(row=1, column=1, pady=20, padx=20)
btn_browse.grid(row=2, column=0, padx=10, pady=10)
btn_calc_ndvi.grid(row=2, column=1, padx=10, pady=10)

# 让输入框自适应宽度
frame.grid_columnconfigure(1, weight=1)

root.mainloop()

内容的提问来源于stack exchange,提问作者Giannis Psixogios

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 22:53:09