国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

Open3D-GUI系列教程(五)鼠標事件(拾取頂點)

這篇具有很好參考價值的文章主要介紹了Open3D-GUI系列教程(五)鼠標事件(拾取頂點)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

鼠標事件(拾取頂點)

這里實現(xiàn)一下鼠標拾取頂點的操作。open3d本身提供了交互選點的操作gui.SceneWidget.Controls.PICK_POINTS,但是出于某些超出我認知范圍的因素,這玩意兒根本不起作用。所以只能另辟蹊徑。

最新的open3d 0.15.1好像修復了這個bug,我試了一下好像還不行,或許是我真的不會用。
open3d 響應鼠標事件,Open3D-GUI,python,3d,gui,mesh


open3d版本:0.14.1

1. 注冊鼠標事件

通過gui.SceneWidget.set_on_mouse(Callable)注冊一個鼠標回調(diào)回調(diào)函數(shù)

  • 這個函數(shù)傳入一個MouseEvent對象
  • 必須返回以下三個之一
    • EventCallbackResult.IGNORED
    • EventCallbackResult.HANDLED
    • EventCallbackResult.CONSUMED

2. 定義鼠標事件

2.1 空間變換

通常一個模型要呈現(xiàn)在屏幕上需要經(jīng)過一系列的變化,即MVP矩陣。所以要得到模型上的坐標,只需要進行一次逆變換即可。即 ( M V P ) ? 1 (MVP)^{-1} (MVP)?1

Camera中提供的函數(shù)unproject()可以完成屏幕空間到世界空間的變換。如果加載的模型沒有進行個平移,那么模型的原點坐標和世界的原點是重合的,所以此時世界坐標等價于模型坐標。

unproject(arg0, arg1, arg2, arg3, arg4)

  • arg0: 視圖中的x
  • arg1: 視圖中的y
  • arg2: 視圖中的z(深度值)
  • arg3: 寬度view_width
  • arg3: 高度view_height

2.2 實現(xiàn)

需要實現(xiàn)的功能是ctrl+鼠標左鍵選擇一個頂點,ctrl+鼠標右鍵刪除最后選擇的頂點。

def _on_mouse_widget3d(self, event):
    

2.2.1 左鍵

	if event.type == gui.MouseEvent.Type.BUTTON_DOWN and event.is_button_down(gui.MouseButton.LEFT) and event.is_modifier_down(gui.KeyModifier.CTRL):

為了獲取屏幕中某一點的深度值,需要使用rendering.Scene.render_to_depth_image( Callable ),該函數(shù)只能用于GUI程序,并將深度圖傳入回調(diào)函數(shù)。

首先定義該回調(diào)函數(shù):

        def depth_callback(depth_image):
        	
            x = event.x - self._scene.frame.x
            y = event.y - self._scene.frame.y
            # np.asarray()翻轉(zhuǎn)軸
            depth = np.asarray(depth_image)[y, x]
            
            if depth == 1.0:
                # 遠平面(沒有選中任何物體)
                text = ""
            else:
                # 這里解投影注意y軸,因為模型空間向上為y軸正方向,屏幕空間向下為y軸正方向
                world = self._scene.scene.camera.unproject(x, self._scene.frame.height - y, depth, self._scene.frame.width, self._scene.frame.height)
                
                text = "({:.3f}, {:.3f}, {:.3f})".format(world[0],world[1],world[2])
                
                # world在模型曲面上,但不一定是頂點數(shù)據(jù)
                # 使用最近點算法在頂點中搜索一個最近的作為選擇點
                idx = self._calc_prefer_indicate(world)
                true_point = np.asarray(self.pcd.points)[idx]
                
                # 存儲選擇的頂點
                self._pick_num += 1
                self._picked_indicates.append(idx)
                self._picked_points.append(true_point)
                
                # 輸出坐標
                print(f"Pick point #{idx} at ({true_point[0]}, {true_point[1]}, {true_point[2]})")

得到頂點后,為了安全的改變UI(這里更新一個Label的文本和可見性),需要讓這個函數(shù)在主線程中執(zhí)行,即提供一個函數(shù),調(diào)用gui.Application.instance.post_to_main_thread(window, function)

定義這個繪制函數(shù)用來畫出選擇點:

			def draw_point():
        		self._info.text = text
                self._info.visible = (text != "")
                # 改變layout
                self.window.set_needs_layout()
                
                if depth != 1.0:
                    label3d = self._scene.add_3d_label(true_point, "#"+str(self._pick_num))
                    self._label3d_list.append(label3d)
                    
                    # 標記球,半徑看著調(diào)
                    sphere = o3d.geometry.TriangleMesh.create_sphere(0.0025)
                    sphere.paint_uniform_color([1,0,0])
                    sphere.translate(true_point)
                    material = rendering.MaterialRecord()
                    material.shader = 'defaultUnlit'
 					self._scene.scene.add_geometry("sphere"+str(self._pick_num), sphere, material)
                    self._scene.force_redraw()
            
            gui.Application.instance.post_to_main_thread(self.window, draw_point)
        
        self._scene.scene.scene.render_to_depth_image(depth_callback)
        return gui.Widget.EventCallbackResult.HANDLED

depth_callback中的最近點搜索

def _cacl_prefer_indicate(self, point):
        pcd = copy.deepcopy(self.pcd)
        pcd.points.append(np.asarray(point))

        pcd_tree = o3d.geometry.KDTreeFlann(pcd)
        # 搜索兩個最近點,第一個是自身
        [k, idx, _]=pcd_tree.search_knn_vector_3d(pcd.points[-1], 2)
        return idx[-1]

2.2.2 右鍵

簡單的刪除工作

	elif event.type == gui.MouseEvent.Type.BUTTON_DOWN and event.is_button_down(gui.MouseButton.RIGHT) and event.is_modifier_down(gui.KeyModifier.CTRL):
        if self._pick_num > 0:
            idx = self._picked_indicates.pop()
            point = self._picked_points.pop()

            print(f"Undo pick: #{idx} at ({point[0]}, {point[1]}, {point[2]})")

            self._scene.scene.remove_geometry('sphere'+str(self._pick_num))
            self._pick_num -= 1
            self._scene.remove_3d_label(self._label3d_list.pop())
            self._scene.force_redraw()
        else:
            print("Undo no point!")
        return gui.Widget.EventCallbackResult.HANDLED
    return gui.Widget.EventCallbackResult.IGNORED
        

2.2.3 總結(jié)

上面這段代碼的大體框架是,其中存儲只是選擇點只是一些列表,從用法應該也看得出來,就懶得寫了。具體可以看后面的源碼。

def _on_mouse_widget3d(self, event):
    if 左鍵:
        def depth_callback(depth_image):
           	# ...
            def draw_point():
                #...
            gui.Application.instance.post_to_main_thread(self.window, draw_point)
            
        self._scene.scene.scene.render_to_depth_image(depth_callback)     
        return gui.Widget.EventCallbackResult.HANDLED
    
    elif 右鍵:
        # ...
        return gui.Widget.EventCallbackResult.HANDLED
    # 其他事件忽略
    return gui.Widget.EventCallbackResult.IGNORED        

2.3 運行結(jié)果

open3d 響應鼠標事件,Open3D-GUI,python,3d,gui,mesh

2.4 完整源代碼

import open3d as o3d
import open3d.visualization.gui as gui
import open3d.visualization.rendering as rendering
import numpy as np
import copy

class App:

    MENU_OPEN = 1

    MENU_SHOW = 5

    MENU_QUIT = 20
    MENU_ABOUT = 21

    show = True

    _picked_indicates = []
    _picked_points = []
    _pick_num = 0

    _label3d_list = []

    def __init__(self):
        gui.Application.instance.initialize()

        self.window = gui.Application.instance.create_window("Pick Points",800,600)
        w = self.window
        em = w.theme.font_size

        # 渲染窗口
        self._scene = gui.SceneWidget()
        self._scene.scene = rendering.Open3DScene(w.renderer)
        self._scene.set_on_mouse(self._on_mouse_widget3d)
        
        self._info = gui.Label("")
        self._info.visible = False

        # 布局回調(diào)函數(shù)
        w.set_on_layout(self._on_layout)
        w.add_child(self._scene)
        w.add_child(self._info)

        # ---------------Menu----------------
        # 菜單欄是全局的(因為macOS上是全局的)
        # 無論創(chuàng)建多少窗口,菜單欄只創(chuàng)建一次。

        # ----以下只針對Windows的菜單欄創(chuàng)建----
        if gui.Application.instance.menubar is None:
            # 文件菜單欄
            file_menu = gui.Menu()
            file_menu.add_item("Open",App.MENU_OPEN)
            file_menu.add_separator()
            file_menu.add_item("Quit",App.MENU_QUIT)

            # 顯示菜單欄
            show_menu = gui.Menu()
            show_menu.add_item("Show Geometry",App.MENU_SHOW)
            show_menu.set_checked(App.MENU_SHOW,True)

            # 幫助菜單欄
            help_menu = gui.Menu()
            help_menu.add_item("About",App.MENU_ABOUT)
            help_menu.set_enabled(App.MENU_ABOUT,False)

            # 菜單欄
            menu = gui.Menu()
            menu.add_menu("File",file_menu)
            menu.add_menu("Show",show_menu)
            menu.add_menu("Help",help_menu)

            gui.Application.instance.menubar = menu
            
            #-----注冊菜單欄事件------
            w.set_on_menu_item_activated(App.MENU_OPEN,self._menu_open)
            w.set_on_menu_item_activated(App.MENU_QUIT,self._menu_quit)
            w.set_on_menu_item_activated(App.MENU_SHOW,self._menu_show)

    # 鼠標事件
    def _on_mouse_widget3d(self, event):
        if event.type == gui.MouseEvent.Type.BUTTON_DOWN and event.is_button_down(gui.MouseButton.LEFT) and event.is_modifier_down(gui.KeyModifier.CTRL):

            def depth_callback(depth_image):

                x = event.x - self._scene.frame.x
                y = event.y - self._scene.frame.y

                depth = np.asarray(depth_image)[y, x]

                if depth==1.0:
                    # 遠平面(沒有幾何體)
                    text = ""
                else:
                    world = self._scene.scene.camera.unproject(x, self._scene.frame.height - y, depth, self._scene.frame.width, self._scene.frame.height)

                    text = "({:.3f}, {:.3f}, {:.3f})".format(world[0],world[1],world[2])

                    idx = self._cacl_prefer_indicate(world)
                    true_point = np.asarray(self.pcd.points)[idx]
                    
                    self._pick_num += 1
                    self._picked_indicates.append(idx)
                    self._picked_points.append(true_point)
                    
                    print(f"Pick point #{idx} at ({true_point[0]}, {true_point[1]}, {true_point[2]})")
            
                def draw_point():
                    self._info.text = text
                    self._info.visible = (text != "")
                    self.window.set_needs_layout()

                    if depth != 1.0:
                        label3d = self._scene.add_3d_label(true_point, "#"+str(self._pick_num))
                        self._label3d_list.append(label3d)

                        # 標記球
                        sphere = o3d.geometry.TriangleMesh.create_sphere(0.0025)
                        sphere.paint_uniform_color([1,0,0])
                        sphere.translate(true_point)
                        material = rendering.MaterialRecord()
                        material.shader = 'defaultUnlit'
                        self._scene.scene.add_geometry("sphere"+str(self._pick_num),sphere,material)
                        self._scene.force_redraw()
                
                gui.Application.instance.post_to_main_thread(self.window, draw_point)
            
            self._scene.scene.scene.render_to_depth_image(depth_callback)
            return gui.Widget.EventCallbackResult.HANDLED
        elif event.type == gui.MouseEvent.Type.BUTTON_DOWN and event.is_button_down(gui.MouseButton.RIGHT) and event.is_modifier_down(gui.KeyModifier.CTRL):
            if self._pick_num > 0:
                idx = self._picked_indicates.pop()
                point = self._picked_points.pop()
                
                print(f"Undo pick: #{idx} at ({point[0]}, {point[1]}, {point[2]})")
                
                self._scene.scene.remove_geometry('sphere'+str(self._pick_num))
                self._pick_num -= 1
                self._scene.remove_3d_label(self._label3d_list.pop())
                self._scene.force_redraw()
            else:
                print("Undo no point!")
            return gui.Widget.EventCallbackResult.HANDLED
        return gui.Widget.EventCallbackResult.IGNORED
 


    def _cacl_prefer_indicate(self, point):
        pcd = copy.deepcopy(self.pcd)
        pcd.points.append(np.asarray(point))

        pcd_tree = o3d.geometry.KDTreeFlann(pcd)
        [k, idx, _]=pcd_tree.search_knn_vector_3d(pcd.points[-1], 2)
        return idx[-1]

    # 打開并顯示一個obj模型
    def _menu_open(self):
        # 文件拾取對話框
        file_picker = gui.FileDialog(gui.FileDialog.OPEN,"Select file...",self.window.theme)
        
        # 文件類型過濾
        file_picker.add_filter('.obj', 'obj model files')
        file_picker.add_filter('', 'All files')
        
        # 初始文件路徑
        file_picker.set_path('./model')

        # 設置對話框按鈕回調(diào)
        file_picker.set_on_cancel(self._on_cancel)
        file_picker.set_on_done(self._on_done)

        # 顯示對話框
        self.window.show_dialog(file_picker)
    
    def _on_cancel(self):
        # 關閉當前對話框
        self.window.close_dialog()

    def _on_done(self, filename): 
        self.window.close_dialog()
        self.load(filename)

    def load(self, file):
        # 讀取模型文件
        mesh = o3d.io.read_triangle_mesh(file)
        mesh.compute_vertex_normals()
        # 定義材質(zhì)
        material = rendering.MaterialRecord()
        material.shader = 'defaultLit'
        
        # 向場景中添加模型
        self._scene.scene.add_geometry('bunny',mesh,material)
        bounds = mesh.get_axis_aligned_bounding_box()
        self._scene.setup_camera(60,bounds,bounds.get_center())

        # 重繪
        self._scene.force_redraw()

        self.mesh = mesh

        self.pcd = o3d.geometry.PointCloud()
        self.pcd.points = o3d.utility.Vector3dVector(np.asarray(mesh.vertices))
        self.pcd.normals = o3d.utility.Vector3dVector(np.asarray(mesh.vertex_normals))

    # 退出應用
    def _menu_quit(self):
        self.window.close()

    # 切換顯示模型
    def _menu_show(self):
        self.show = not self.show
        gui.Application.instance.menubar.set_checked(App.MENU_SHOW,self.show)
        self._scene.scene.show_geometry('bunny',self.show)

    def _on_layout(self, layout_context):
        r = self.window.content_rect
        self._scene.frame = r

        pref = self._info.calc_preferred_size(layout_context, gui.Widget.Constraints())
        self._info.frame = gui.Rect(
            r.x, r.get_bottom()-pref.height, pref.width, pref.height)

    def run(self):
        gui.Application.instance.run()
        
if __name__ == "__main__":
    app = App()
    app.run()

附:關于0.15.1版本解投影部分說明

在解投影部分:

world = self._scene.scene.camera.unproject(x, self._scene.frame.height - y, depth, self._scene.frame.width, self._scene.frame.height)

由于新版本中關于``unproject()`的實現(xiàn)發(fā)生改動,所以要得到正確結(jié)果只需將y直接作為參數(shù),不需要height-y這一步,即

world = self._scene.scene.camera.unproject(x, y, depth, self._scene.frame.width, self._scene.frame.height)

實現(xiàn)的具體改動如下:參考0.15.1和0.14.1版本的FilamentCamera.cpp文章來源地址http://www.zghlxwxcb.cn/news/detail-784867.html

  • 0.14.1中的unproject()
Eigen::Vector3f FilamentCamera::Unproject(
        float x, float y, float z, float view_width, float view_height) const 
{
   Eigen::Vector4f gl_pt(2.0f * x / view_width - 1.0f,
                          2.0f * y / view_height - 1.0f, 2.0f * z - 1.0f, 1.0f);

   auto proj = GetProjectionMatrix();
   Eigen::Vector4f obj_pt = (proj * GetViewMatrix()).inverse() * gl_pt;
   return {obj_pt.x() / obj_pt.w(), obj_pt.y() / obj_pt.w(),
           obj_pt.z() / obj_pt.w()};
}
  • 0.15.1中的unproject()實現(xiàn)中有height-y這一步:
Eigen::Vector3f FilamentCamera::Unproject(
        float x, float y, float z, float view_width, float view_height) const 
{
    Eigen::Vector4f gl_pt(2.0f * x / view_width - 1.0f,
                          2.0f * (view_height - y) / view_height - 1.0f,
                          2.0f * z - 1.0f, 1.0f);

    auto proj = GetProjectionMatrix();
    Eigen::Vector4f obj_pt = (proj * GetViewMatrix()).inverse() * gl_pt;
    return {obj_pt.x() / obj_pt.w(), obj_pt.y() / obj_pt.w(),
            obj_pt.z() / obj_pt.w()};
}

到了這里,關于Open3D-GUI系列教程(五)鼠標事件(拾取頂點)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • Open3D點云數(shù)據(jù)處理(一):VSCode配置python,并安裝open3d教程

    Open3D點云數(shù)據(jù)處理(一):VSCode配置python,并安裝open3d教程

    專欄地址:https://blog.csdn.net/weixin_46098577/category_11392993.html 在很久很久以前,我寫過這么一篇博客,講的是open3d點云處理的基本方法。?? 當時是 PyCharm + Anaconda + python3.8 + open3d 0.13 已經(jīng)是2023年了,現(xiàn)在有了全新版本。目前python由當年的3.8更新到了3.11版本,open3d也從0.13來到了

    2024年02月07日
    瀏覽(37)
  • open3d學習教程1--點云對象PointCloud

    open3d學習教程1--點云對象PointCloud

    點云簡單來說就是3d坐標下一個個點組成的數(shù)據(jù),每個點可以包含x,y,z,顏色、分類值、強度值、時間等等信息。點云是3d數(shù)據(jù)的表示形式之一。 open3d中用來表示點云的數(shù)據(jù)結(jié)構(gòu)。pointcloud對象包含了很多處理點云的成員方法,如點云體素下采樣,點云上色等等。 pointcloud的靜態(tài)

    2023年04月08日
    瀏覽(17)
  • 基于Python的Open3D庫進行點云聚類(詳細教程)

    基于Python的Open3D庫進行點云聚類(詳細教程) 隨著3D技術的不斷發(fā)展,點云處理已成為越來越重要的研究領域之一。而點云聚類作為其中的一個熱門問題,已經(jīng)成為廣大3D技術工作者必須掌握的技能之一。本文將介紹如何使用Python中的Open3D庫實現(xiàn)點云聚類,并給出詳細的代碼

    2024年02月09日
    瀏覽(26)
  • 【點云處理教程】00計算機視覺的Open3D簡介

    【點云處理教程】00計算機視覺的Open3D簡介

    ????????Open3D 是一個開源庫,使開發(fā)人員能夠處理 3D 數(shù)據(jù)。它提供了一組用于 3D 數(shù)據(jù)處理、可視化和機器學習任務的工具。該庫支持各種數(shù)據(jù)格式,例如 .ply、.obj、.stl 和 .xyz,并允許用戶創(chuàng)建自定義數(shù)據(jù)結(jié)構(gòu)并在程序中訪問它們。 Open3D 廣泛應用于機器人、增強現(xiàn)實和自

    2024年02月14日
    瀏覽(19)
  • open3d教程(二):可視化三維模型,并轉(zhuǎn)換成點云(Python版本)

    open3d教程(二):可視化三維模型,并轉(zhuǎn)換成點云(Python版本)

    可以自己用建模軟件建立一個模型 從free3d免費下載 open3d.visualization. draw_geometries 參數(shù): geometry_list ( List[open3d.geometry.Geometry]) : 要可視化的幾何體列表. window_name( str ,? optional ,? default=\\\'Open3D\\\'): 展示模型的可視化窗口名稱,默認是Open3d. width:?

    2024年02月11日
    瀏覽(27)
  • python如何實現(xiàn)點云可視化交互——Open3D實例教程(獲取所選點的信息)保姆級教學

    python如何實現(xiàn)點云可視化交互——Open3D實例教程(獲取所選點的信息)保姆級教學

    Open3D是目前python中可用的用于 3D 數(shù)據(jù)處理的現(xiàn)代庫,可以對點云、網(wǎng)格等三維數(shù)據(jù)進行讀取、采樣、配準、可視化等操作。其中對點云等三維模型進行可視化的功能在Python中顯得非常方便。 在通過對官方文檔的研究之后作者發(fā)現(xiàn)在Open3D的多種可視化函數(shù)中出現(xiàn)了返回所選點

    2024年02月02日
    瀏覽(41)
  • 【Open3D】如何在CMake/C++中調(diào)用Open3D

    qquad Open3D是點云的開源處理庫,支持Python或C++。其Python已有較全的教程,也可以直接使用 pip install open3d 直接進行安裝,而若想在C++中調(diào)用Open3D則麻煩一些,需要滿足以下條件: Open3D git源代碼(本教程針對0.16.1的版本) CMake = 3.20 clang = 7 分為以下幾步進行: 下載Open3D源代碼

    2023年04月18日
    瀏覽(14)
  • 基于Open3D的點云處理17-Open3d的C++版本

    基于Open3D的點云處理17-Open3d的C++版本

    http://www.open3d.org/docs/latest/cpp_api.html http://www.open3d.org/docs/latest/getting_started.html#c http://www.open3d.org/docs/release/cpp_project.html#cplusplus-example-project https://github.com/isl-org/open3d-cmake-find-package https://github.com/isl-org/open3d-cmake-external-project https://github.com/isl-org/Open3D/releases Note: -DBUILD_SHARED_LIBS

    2024年02月09日
    瀏覽(47)
  • open3d 0.17.0的open3d.visualization.ViewControl類有bug

    在使用過程中發(fā)現(xiàn) open3d.visualization.ViewControl 的如下方法,在 open3d 0.17.0 環(huán)境下不起作用,點云的顯示視場還是默認配置;而在 open3d 0.16.0 環(huán)境下卻正常工作。 rotate set_front set_lookat set_up set_zoom 上述測試代碼在如下虛擬環(huán)境中進行過測試,均失敗。 在如下虛擬環(huán)境中正常工作

    2024年02月21日
    瀏覽(19)
  • 【Open3D可視化——添加標簽】:如何在Open3D的可視化窗口中添加文字標簽?

    【Open3D可視化——添加標簽】:如何在Open3D的可視化窗口中添加文字標簽? Open3D是一個基于Python語言開發(fā)的跨平臺開源工具包,主要用于三維數(shù)據(jù)處理和可視化。在進行三維數(shù)據(jù)可視化過程中,往往需要在場景中添加標簽來標識物體、點云等信息。本文將介紹如何在Open3D的可

    2024年02月11日
    瀏覽(208)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包