Immvision以为没有传入ndarray,而type() 显示它确实是一个ndarray

编程语言 2026-07-08

我正在尝试用Python的 imgui_bundle cartopy应用,尽管我在做一个 ndarray,我仍然收到一个错误,提示需要一个 numpy.ndarray,尽管当我 type() 时,它说它是一个 numpy.ndarray。单纯在只有cartopy的情况下做一个 show() 时,地图确实会显示。现在我想在 imgui 中实现并使其具有交互性(即让用户鼠标悬停以识别经纬度)。单独的 imgui 部分(不显示地图)也能工作,cartopy的编码(不带 gltexture 的部分,只是一个简单的 show())也能工作。现在当我尝试把两者结合起来时,就不行了:

import io
import os
import numpy as np
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from imgui_bundle import imgui, immvision, ImVec2, immapp

class MapApp:
    def __init__(self):
        # Base map bounding box
        self.base_lat_min, self.base_lat_max = 3, 5
        self.base_lon_min, self.base_lon_max = -10, -7

        # Current zoom extents start as base extents
        self.lat_min, self.lat_max = self.base_lat_min, self.base_lat_max
        self.lon_min, self.lon_max = self.base_lon_min, self.base_lon_max

        self.fig, self.ax = plt.subplots(figsize=(8, 6), subplot_kw={'projection': ccrs.PlateCarree()})
        self.texture_id = None
        self.needs_redraw = True

        self.width, self.height = 800, 600  # Image display size

        self.zoom_factor = 1.0  # 1.0 means no zoom
        cartopy.config['pre_existing_data_dir'] = os.getcwd()
        cartopy.config['data_dir'] = os.getcwd()

    def update_map_texture(self):
        self.ax.clear()
        self.ax.set_extent([self.lon_min, self.lon_max, self.lat_min, self.lat_max], crs=ccrs.PlateCarree())
        self.ax.coastlines('50m')
        self.ax.add_feature(cfeature.BORDERS, linestyle=':')
        self.ax.add_feature(cfeature.LAND, facecolor='lightgray')
        self.ax.add_feature(cfeature.OCEAN, facecolor='lightblue')

        canvas = self.fig.canvas
        canvas.draw()
        w, h = canvas.get_width_height()
        print(f"w: {w}, h: {h}")
        buf = canvas.tostring_argb()
        img_array = np.frombuffer(buf, dtype=np.uint8).reshape(h, w, 4)
        img_array = img_array[:, :, [1, 2, 3, 0]]
        print(F"img_array.shape({img_array.size}): {img_array.shape} type: {type(img_array)}")
        if self.texture_id is None:
            self.texture_id = immvision.GlTexture(img_array)
            print(f"texture_id: {self.texture_id}")
        else:
            immvision.GlTexture.update_from_image(self.texture_id, img_array, False)

        self.needs_redraw = False

    def gui(self):
        imgui.begin("Interactive Map with Zoom")

        if self.needs_redraw or self.texture_id is None:
            self.update_map_texture()

        imgui.image(self.texture_id, ImVec2(self.width, self.height))

        # Mouse position and coordinate display
        mouse_pos = imgui.get_mouse_pos()
        window_pos = imgui.get_window_position()
        cursor_pos = imgui.get_cursor_pos()
        image_pos = (window_pos.x + cursor_pos.x, window_pos.y + cursor_pos.y)

        rel_x = mouse_pos.x - image_pos[0]
        rel_y = mouse_pos.y - image_pos[1]

        if 0 <= rel_x < self.width and 0 <= rel_y < self.height:
            lon = self.lon_min + (rel_x / self.width) * (self.lon_max - self.lon_min)
            lat = self.lat_max - (rel_y / self.height) * (self.lat_max - self.lat_min)
            imgui.text(f"Mouse Lon: {lon:.4f}  Lat: {lat:.4f}")

        imgui.end()

if __name__ == "__main__":
    app = MapApp()
    immapp.run(gui_function=app.gui, window_title="imgui Cartopy Map with Zoom")

我得到的错误是:

    self.texture_id = immvision.GlTexture(img_array)
                      ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
TypeError: __init__(): incompatible function arguments. The following argument types are supported:
    1. __init__(self) -> None
    2. __init__(self, image: numpy.ndarray, is_color_order_bgr: bool = False) -> None
    3. __init__(self, other: imgui_bundle._imgui_bundle.immvision.GlTexture) -> None

Invoked with types: imgui_bundle._imgui_bundle.immvision.GlTexture, ndarray

尽管我的打印语句显示它是一个 numpy.ndarray

w: 800, h: 600
img_array.shape(1920000): (600, 800, 4) type: <class 'numpy.ndarray'>

我到底少了什么?

解决方案

不知道为什么,但当我创建新数组时代码就能工作

img_array = np.zeros((self.height, self.width, 4), dtype=np.uint8)

看起来它的大小、形状、dtype都相同,但出于某种原因它就能工作。

我还发现把你的数组转换为 np.array() 时代码也能工作

img_array = np.frombuffer(buf, dtype=np.uint8).reshape(h, w, 4)

img_array = np.array(img_array)

我只能猜测原始的 img_array 会把数据保留为内存中不同位置的视图,但C/C++代码可能需要一个在连续内存中的对象。

差异显示为 img_array.flags


顺便说一句:

代码里还有其他问题。

它需要 get_window_pos() 而不是 get_window_position()。 (小笔误)

另一个问题是 immvision.GlTexture(img_array) 给出对象 GlTexture,不是数字 texture ID,变量 self.texture_id 可能会让人误解,因为在某些地方它需要 self.texture_id.texture_id 才能得到真正的 texture ID

在某个地方它还需要得到 texture ref 而不是 texture id

imgui.image(
    imgui.ImTextureRef(self.texture_id.texture_id),
    ImVec2(self.width, self.height),
)

修改后我得到这个:

enter image description here


完整可运行的代码:

import io
import os

import numpy as np
import matplotlib.pyplot as plt
import cartopy
import cartopy.crs as ccrs
import cartopy.feature as cfeature
from imgui_bundle import imgui, immvision, ImVec2, immapp


def info(arr):
    print("--- arr ---")
    print(f"size: {arr.size}")
    print(f"shape: {arr.shape}")
    print(f"type: {type(arr)}")
    print(f"dtype: {arr.dtype}")
    print(f"strides: {arr.strides}")
    print(f"flags:\n{arr.flags}", end="")
    print(f"base: {arr.base}")
    print("---")


class MapApp:
    def __init__(self):
        # Base map bounding box
        self.base_lat_min, self.base_lat_max = 3, 5
        self.base_lon_min, self.base_lon_max = -10, -7

        # Current zoom extents start as base extents
        self.lat_min, self.lat_max = self.base_lat_min, self.base_lat_max
        self.lon_min, self.lon_max = self.base_lon_min, self.base_lon_max

        self.fig, self.ax = plt.subplots(
            figsize=(8, 6), subplot_kw={"projection": ccrs.PlateCarree()}
        )
        self.texture = None
        self.needs_redraw = True

        self.width, self.height = 800, 600  # Image display size

        self.zoom_factor = 1.0  # 1.0 means no zoom
        cartopy.config["pre_existing_data_dir"] = os.getcwd()
        cartopy.config["data_dir"] = os.getcwd()

    def update_map_texture(self):
        self.ax.clear()
        self.ax.set_extent(
            [self.lon_min, self.lon_max, self.lat_min, self.lat_max],
            crs=ccrs.PlateCarree(),
        )
        self.ax.coastlines("50m")
        self.ax.add_feature(cfeature.BORDERS, linestyle=":")
        self.ax.add_feature(cfeature.LAND, facecolor="lightgray")
        self.ax.add_feature(cfeature.OCEAN, facecolor="lightblue")

        canvas = self.fig.canvas
        canvas.draw()
        w, h = canvas.get_width_height()
        print(f"w: {w}, h: {h}")

        buf = canvas.tostring_argb()
        img_array = np.frombuffer(buf, dtype=np.uint8).reshape(h, w, 4)
        # img_array = np.zeros((self.height, self.width, 4), dtype=np.uint8)
        info(img_array)

        img_array = np.array(img_array)
        info(img_array)

        if self.texture is None:
            self.texture = immvision.GlTexture(img_array)
            print(
                f"texture: {self.texture} | texture.texture_id: {self.texture.texture_id}"
            )
        else:
            immvision.GlTexture.update_from_image(self.texture, img_array, False)

        self.needs_redraw = False

    def gui(self):
        imgui.begin("Interactive Map with Zoom")

        if self.needs_redraw or self.texture is None:
            self.update_map_texture()

        imgui.image(
            imgui.ImTextureRef(
                self.texture.texture_id
            ),  # needs `.texture_id` and `ImTextureRef()`
            ImVec2(self.width, self.height),
        )

        # Mouse position and coordinate display
        mouse_pos = imgui.get_mouse_pos()
        window_pos = imgui.get_window_pos()  # ERROR: position
        cursor_pos = imgui.get_cursor_pos()
        image_pos = (window_pos.x + cursor_pos.x, window_pos.y + cursor_pos.y)

        rel_x = mouse_pos.x - image_pos[0]
        rel_y = mouse_pos.y - image_pos[1]

        if 0 <= rel_x < self.width and 0 <= rel_y < self.height:
            lon = self.lon_min + (rel_x / self.width) * (self.lon_max - self.lon_min)
            lat = self.lat_max - (rel_y / self.height) * (self.lat_max - self.lat_min)
            imgui.text(f"Mouse Lon: {lon:.4f}  Lat: {lat:.4f}")

        imgui.end()


if __name__ == "__main__":
    app = MapApp()
    immapp.run(gui_function=app.gui, window_title="imgui Cartopy Map with Zoom")

关于 np.zeros() 的思路,我在演示 demos_python/demos_imgui_explorer/implot3d_demo.py 中看到的,我是在GitHub的仓库 imgui_bundle 找到的。还有更多演示。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章