Yazar | Tianyuan Prodigal Son
Sorumlu Editör | Wu Xingling
Üretildi | CSDN Blogu
Üç boyutlu görüntüleme alanında, OpenGL tanrı benzeri bir varlıktır ve durumu bir programlama dilinde C gibidir. OpenGL'den türetilen birçok şube ve fraksiyon vardır.
Python şemsiyesi altında, en etkili 3B kitaplıkları, her biri büyük bir kullanıcı grubuna sahip olan pyOpenGl / VTK / Mayavi / Vispy, vb. İçerir. VTK tıp alanında yaygın olarak kullanılmaktadır ve Vispy'nin bilimsel araştırma alanında pek çok hayranı vardır.
Hem VTK hem de Vispy, OpenGL tabanlı uzantılardır ve Mayavi, VTK tabanlıdır.Bu nedenle, birçok tıbbi görüntüleme uygulaması Python + VTK + ITK + Mayavi'nin bir kombinasyonunu kullanır (ITK, OpenCV veya PIL'e benzer bir görüntü işleme kitaplığıdır).
PyOpenGl dahil olmak üzere yukarıda belirtilen 3B oluşturma kitaplıklarının hepsinin ortak bir özelliği vardır, yani yalnızca 3B işlevlerin gerçekleştirilmesine odaklanırlar ve kullanıcı arabirimi desteğini ihmal ederler. Vispy ile karşılaştırıldığında, arka uç olarak wx veya Qt'yi desteklese de, arka ucu bağladıktan sonra, pencere yönetimi ve etkileşimli işlemlerde hala birçok sorun vardır. PyOpenGl, bir GLUT kitaplığı sağlayarak, kullanıcı arayüzünü desteklese bile bunu kolaylaştırır.
Aslında, karmaşık bir üç boyutlu görüntüleme sisteminde UI OpenGL kadar önemlidir. OpenGL için bir UI ortağı bulabilirseniz, bu kesinlikle programın güvenilirliğini ve çalışabilirliğini artıracak ve kullanıcı deneyimini geliştirecektir. wxPython ve PyOpenGL böyle bir çift altın ortaktır. Bir şiir sözü vardır:
On yıldır duvarla karşı karşıya kalan duvar kırıldı, kılıç donma bıçağı denenmedi.
Sonbahar rüzgarı, yeni bir üç boyutluluk dünyası açmak için başkentten dışarı fırladı.
WxPython hakkında
Her zaman wxPython'un Python için en uygun GUI kitaplığı olduğunu düşünüyorum ve bunun için özel olarak bir blog yazısı yazdım. Ayrıntılar için bkz. "WxPython: Python'un tercih edilen GUI kitaplığı". Burada wxPython'u nasıl kullanacağımı tartışmayacağım. WxPython stilini göstermek için yalnızca geliştirme projelerinin birkaç ekran görüntüsünü gönderiyorum.
Aşağıdaki resim wxPython + PyOpenGL tarafından geliştirilen projenin ekran görüntüsüdür (hassas bilgiler gizlidir):
Aşağıdaki şekil arayüzün ayrıntılarını gösterir (hassas bilgiler gizlidir):
Aşağıdaki resim wxPython'un geleneksel tarzını göstermektedir:
PyOpenGL hakkında
PyOpenGL için birçok giriş öğreticisi var ve ayrıca "Python Programcıları için OpenGL Eğitimi" adlı bir blog yazım da var. Özel bir hatırlatma, bu blog gönderisinin sonunda köşe tampon nesnesi VBO'dan bahsediliyor ve demo kodu var. VBO kavramı çok önemli, çok önemli, çok önemli Sadece VBO'yu kullanmayı öğrenerek gerçekten harika OpenGL dünyasına girebilirsiniz.
İlk OpenGL, anında oluşturma modunu kullandı (Anında mod, yani sabit işleme hattı), konsept açık ve anlaşılması kolaydır, grafik çizme de çok uygundur, ancak verimlilik çok düşüktür. OpenGL3.2'den başlayarak, belirtim belgesi hemen oluşturma modunu terk etmeye başladı ve geliştiricilerin OpenGL Çekirdek profilinde geliştirmeleri teşvik edildi.Bu dalın özellikleri, eski özellikleri tamamen kaldırıyor.
VBO, OpenGL çekirdek modunun temelidir. VBO, tepe veri kümesini GPU'da depolar, bu da VBO verilerinin işlenmesinin hızlı olacağı anlamına gelir. Ancak RAM'den GPU'ya veri aktarmanın bir bedeli vardır. VBO, GPU üzerinde olmasına rağmen, GPU'nun hesaplama işlevlerini kullanmaz. VBO'nun üstünde VAO kavramı da vardır, yani Vertex Array Object, vertex array nesnesi. Bu konsept çok karmaşık, VAO'yu bir VBO yöneticisi olarak anlayabiliriz. VAO grafik kartına dayandığı ve çok yönlülüğü zayıf olduğu için, onu atlamayı seçtim.
Dürüst olmak gerekirse, OpenGL'nin çekirdek modları hakkında pek bir şey bilmiyorum ve GLSL gölgelendirici dili beni daha da korkutuyor ve VBO hakkındaki anlayışım ille de doğru değil. Model alma, hacimsel veri oluşturma ve 3B yeniden yapılandırma açısından kodumun sonuçları hala tatmin edici olmasa da, yine de yöntemimin ana akım düşünceden farklı olduğunu hissediyorum. Çoğu durumda, yöntemimin "yenilikçi" olduğunu söylemeyi seviyorum.
Aşağıda çalışmamda çizilen bazı üç boyutlu görüntülemeler var:
WxPython ve PyOpenGL arasında bir köprü oluşturun
wx.glcanvas.GLCanvas, wxPython tarafından OpenGL'yi görüntülemek için sağlanan bir sınıftır. Adından da anlaşılacağı gibi, bunu bir OpenGL çizim tahtası olarak anlayabiliriz. Bu çizim tahtası ile OpenGL tarafından sağlanan çeşitli araçları, üzerine çeşitli 3B modeller çizmek için kullanabiliriz.
Aşağıdaki kod, wx.glcanvas.GLCanvas'tan yeni bir sınıf WxGLScene türetir, fare tekerleği olayını bağlar ve Anında modunda iki üçgen çizer. Alan sınırlamaları nedeniyle, fare sürükleme işlemi silinir ve yalnızca kaydırma tekerleği yakınlaştırma işlevi korunur.
# - * - kodlama: utf-8 - * - wx içe aktar wx ithalatından glcanvas OpenGL.GL'den içe aktarma * OpenGL.GLU'dan içe aktarma * sınıf WxGLScene (glcanvas.GLCanvas): "" "GL Sahne Sınıfı" "" def __init __ (öz, ebeveyn, göz =, amaç =, yukarı =, görünüm =): "" "Yapıcı üst-üst pencere nesnesi göz gözlemcinin konumu (varsayılan z ekseni pozitif yönü) gözlemciye kadar (y ekseninin varsayılan pozitif yönü) görünüm-görüntüleme gövdesi "" " glcanvas.GLCanvas .__ init __ (self, parent, -1, style = glcanvas.WX_GL_RGBA | glcanvas.WX_GL_DOUBLEBUFFER | glcanvas.WX_GL_DEPTH_SIZE) self.parent = ebeveyn # ebeveyn pencere nesnesi self.eye = eye # Gözlemcinin konumu self.aim = aim # Hedefi gözlemleyin (varsayılan koordinatların başlangıcında) self.up = up # Gözlemciye kadar self.view = view # Gövdeyi görüntüle self.size = self.GetClientSize # OpenGL pencere boyutu self.context = glcanvas.GLContext (self) # OpenGL bağlamı self.zoom = 1.0 # Viewport yakınlaştırma faktörü self.mpos = Yok # fare konumu self.initGL # Tuval başlatma self.Bind (wx.EVT_SIZE, self.onResize) # bind pencere boyutu değişiklik olayı self.Bind (wx.EVT_ERASE_BACKGROUND, self.onErase) # arka plan silme olayını bağla self.Bind (wx.EVT_PAINT, self.onPaint) # bağlama yeniden boyama olayı self.Bind (wx.EVT_LEFT_DOWN, self.onLeftDown) # Sol fare düğmesi aşağı olayını bağlama self.Bind (wx.EVT_LEFT_UP, self.onLeftUp) # Sol fare düğmesi yukarı olayını bağlama self.Bind (wx.EVT_RIGHT_UP, self.onRightUp) # Sağ fare düğmesi yukarı olayını bağlayın self.Bind (wx.EVT_MOTION, self.onMouseMotion) # bind fare hareketi olayı self.Bind (wx.EVT_MOUSEWHEEL, self.onMouseWheel) # bağ fare tekerleği olayı def onResize (self, evt): "" "Pencere boyutu değişikliği etkinliğine yanıt verme" "" self.context ise: self.SetCurrent (self.context) self.size = self.GetClientSize self.Refresh (False) evt.Skip def onErase (self, evt): "" "Arka planda silme olayına yanıt verme" "" geçmek def onPaint (self, evt): "" "Yeniden çizilen olaylara yanıt verme" "" self.SetCurrent (self.context) glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Ekran ve derinlik arabelleğini temizle self.drawGL # Çizim self.SwapBuffers # Çizilen içeriği görüntülemek için arabellekleri değiştirin evt.Skip def onLeftDown (self, evt): "" "Sol fare düğmesi basılı olayına yanıt verme" "" self.CaptureMouse self.mpos = evt.GetPosition def onLeftUp (self, evt): "" "Sol fare düğmesi yukarı olayına yanıt verme" "" Deneyin: self.ReleaseMouse dışında: geçmek def onRightUp (self, evt): "" "Sağ fare düğmesi geri dönme olayına yanıt verme" "" geçmek def onMouseMotion (self, evt): "" "Fare hareketi olaylarına yanıt verme" "" if evt.Dragging ve evt.LeftIsDown: pos = evt.GetPosition Deneyin: dx, dy = pos-self.mpos dışında: dönüş self.mpos = konum # Alan sınırlamaları nedeniyle, gözlemcinin konumunu değiştirme kodu çıkarılmıştır self.Refresh (False) def onMouseWheel (self, evt): "" "Fare tekerleği olaylarına yanıt verme" "" eğer evt.WheelRotation < 0: self.zoom * = 1,1 self.zoom ise > 100: self.zoom = 100 elif evt.WheelRotation > 0: self.zoom * = 0.9 self.zoom ise < 0.01: self.zoom = 0.01 self.Refresh (False) def initGL (self): "" "GL'yi başlat" "" self.SetCurrent (self.context) glClearColor (0,0,0,0) # tuval arka plan rengini ayarla glEnable (GL_DEPTH_TEST) # Tıkanma ilişkisini gerçekleştirmek için derinlik testini açın glDepthFunc (GL_LEQUAL) # Derinlik testi fonksiyonunu ayarlayın glShadeModel (GL_SMOOTH) # GL_SMOOTH (yumuşak gölgeleme) / GL_FLAT (sabit gölgeleme) glEnable (GL_BLEND) # Karıştırmayı aç glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) # karma işlevi ayarlayın glEnable (GL_ALPHA_TEST) # Alfa testini etkinleştir glAlphaFunc (GL_GREATER, 0.05) # Geçmek için Alfa test koşulunu 0.05'ten büyük olacak şekilde ayarlayın glFrontFace (GL_CW) # Saat yönünün tersine endeksi öne ayarlayın (GL_CCW / GL_CW) glEnable (GL_LINE_SMOOTH) # Çizgi parçası kenar yumuşatmayı etkinleştir glHint (GL_LINE_SMOOTH_HINT, GL_NICEST) def drawGL (kendi): """çizmek""" # Ekranı ve derinlik arabelleğini temizle glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # Görüntü alanını ayarla glViewport (0, 0, self.size, self.size) # Projeksiyonu ayarla (perspektif projeksiyon) glMatrixMode (GL_PROJECTION) glLoadIdentity k = self.size / self.size eğer k > 1: glFrustum ( self.zoom * self.view * k, self.zoom * self.view * k, self.zoom * self.view, self.zoom * self.view, self.view, self.view ) Başka: glFrustum ( self.zoom * self.view, self.zoom * self.view, self.zoom * self.view / k, self.zoom * self.view / k, self.view, self.view ) # Bakış açısını ayarla gluLookAt ( self.eye, self.eye, self.eye, self.aim, self.aim, self.aim, self.up, self.up, self.up ) # Model görünümünü ayarlayın glMatrixMode (GL_MODELVIEW) glLoadIdentity # ------------------------------------------------- -------------- glBegin (GL_LINES) # Bir doğru parçası çizmeye başlayın (dünya koordinat sistemi) # X eksenini kırmızı olarak çizin glColor4f (1.0, 0.0, 0.0, 1.0) # Mevcut rengi kırmızı ve opak olarak ayarla glVertex3f (-0.8, 0.0, 0.0) # x ekseninin tepe noktasını ayarlayın (x ekseninin negatif yönü) glVertex3f (0.8, 0.0, 0.0) # x ekseninin tepe noktasını ayarlayın (x ekseninin pozitif yönü) # Y eksenini yeşil olarak çizin glColor4f (0.0, 1.0, 0.0, 1.0) # Mevcut rengi yeşil ve opak olarak ayarlayın glVertex3f (0.0, -0.8, 0.0) # y ekseni tepe noktasını ayarlayın (y ekseni negatif yönü) glVertex3f (0.0, 0.8, 0.0) # y ekseni tepe noktasını ayarlayın (y ekseni pozitif yönü) # Z eksenini mavi çizin glColor4f (0.0, 0.0, 1.0, 1.0) # Mevcut rengi maviye ve opak olarak ayarla glVertex3f (0.0, 0.0, -0.8) # Z ekseninin tepe noktasını ayarlayın (z ekseninin negatif yönü) glVertex3f (0.0, 0.0, 0.8) # z ekseninin tepe noktasını ayarlayın (z ekseninin pozitif yönü) glEnd # end çizgi parçasını çizme # ------------------------------------------------- -------------- glBegin (GL_TRIANGLES) # Bir üçgen çizmeye başlayın (z ekseninin negatif yarısı) glColor4f (1.0, 0.0, 0.0, 1.0) # Mevcut rengi kırmızı ve opak olarak ayarla glVertex3f (-0.5, -0.366, -0.5) # set üçgen köşeleri glColor4f (0.0, 1.0, 0.0, 1.0) # Mevcut rengi yeşil ve opak olarak ayarlayın glVertex3f (0.5, -0.366, -0.5) # set üçgen köşeleri glColor4f (0.0, 0.0, 1.0, 1.0) # Mevcut rengi maviye ve opak olarak ayarla glVertex3f (0.0, 0.5, -0.5) # set üçgen köşeleri glEnd # end çizim üçgeni # ------------------------------------------------- -------------- glBegin (GL_TRIANGLES) # Bir üçgen çizmeye başlayın (z ekseninin pozitif yarısı) glColor4f (1.0, 0.0, 0.0, 1.0) # Mevcut rengi kırmızı ve opak olarak ayarla glVertex3f (-0.5, 0.5, 0.5) # set üçgen köşeleri glColor4f (0.0, 1.0, 0.0, 1.0) # Mevcut rengi yeşil ve opak olarak ayarlayın glVertex3f (0.5, 0.5, 0.5) # set üçgen köşeleri glColor4f (0.0, 0.0, 1.0, 1.0) # Mevcut rengi maviye ve opak olarak ayarla glVertex3f (0.0, -0.366, 0.5) # set üçgen köşeleri glEnd # end çizim üçgeni WxGLScene sınıfını kullanma örneği: # - * - kodlama: utf-8 - * - wx içe aktar sahne içe aktarımından * APP_TITLE = u'WxPython ve pyOpenGL arasında bir köprü oluştur ' sınıf mainFrame (wx.Frame): "" "Wx.Frame'den miras alınan programın ana pencere sınıfı" "" def __init __ (öz): "" "Yapıcı" "" wx.Frame .__ init __ (self, None, -1, APP_TITLE, style = wx.DEFAULT_FRAME_STYLE) self.SetBackgroundColour (wx.Colour (224, 224, 224)) self.SetSize ((800, 600)) self.Center self.scene = WxGLScene (öz) sınıf mainApp (wx.App): def OnInit (kendi): self.SetAppName (APP_TITLE) self.Frame = mainFrame self.Frame.Show True döndür __name__ == "__main__" ise: app = mainApp app.MainLoopWxGLScene sınıfını kullanma örneği:
# - * - kodlama: utf-8 - * -
wx içe aktar
sahne içe aktarımından *
APP_TITLE = u'WxPython ve pyOpenGL arasında bir köprü oluştur '
sınıf mainFrame (wx.Frame):
"" "Wx.Frame'den miras alınan programın ana pencere sınıfı" ""
def __init __ (öz):
"" "Yapıcı" ""
wx.Frame .__ init __ (self, None, -1, APP_TITLE, style = wx.DEFAULT_FRAME_STYLE)
self.SetBackgroundColour (wx.Colour (224, 224, 224))
self.SetSize ((800, 600))
self.Center
self.scene = WxGLScene (öz)
sınıf mainApp (wx.App):
def OnInit (kendi):
self.SetAppName (APP_TITLE)
self.Frame = mainFrame
self.Frame.Show
True döndür
__name__ == "__main__" ise:
app = mainApp
app.MainLoop
Arayüz efektleri aşağıdaki gibidir:
Sahneler, görünüm alanları ve modeller
OpenGL, kullanıcıların birden çok görüntü portu ayarlamak için glViewport komutunu kullanmalarına izin verir, bu da görüntü ekranında birden çok görüntü alanını bölebileceğimiz anlamına gelir.Bu alanlar birbiriyle örtüşebilir ve mantıksal olarak tamamen bağımsızdır. WxGLScene'i bir sahne olarak adlandırabiliriz, glViewport komutu tarafından oluşturulan görünüm alanına bölge denir ve aynı ada sahip bir 3D bileşen model olarak tanımlanır. Bir sahne birden çok görüntü alanı ekleyebilir ve bir görüntü alanı birden çok model oluşturabilir.
Yüzey modelini örnek alırsak fonksiyon prototipi aşağıdaki gibidir:
def drawSurface (self, name, v, c = None, t = None, texture = None, method = 'Q', mode = None, display = True, pick = False):
"" "Bir yüzey çizin
isim-model adı
v-Köşe koordinat seti, numpy.ndarray türü, şekil = (sütunlar, 3)
c-köşe renk kümesi, numpy.ndarray türü, şekil = (3 | 4,) | (sütunlar, 3 | 4)
t-köşenin doku koordinat kümesi, numpy.ndarray türü, şekil = (cols, 2)
texture-2D doku nesnesi
yöntem-çizim yöntemi
'Q'-Dörtgen
0347
| | | |
1256
'T'-üçgen
0235
\ / \ /
14
'Q +' - yan yana sürekli dörtgen
024
| | |
135
'T +' - yan yana sürekli üçgenler
024
\ / _ \ / _ \
13 5
'F'-Sektör
'P'-Çokgen
mod görüntüleme modu
Hiçbiri kullanım mevcut ayarı
'FCBC'-Ön ve arka dolgu rengi FCBC
'FLBL'-gösterme hatları FLBL öncesi ve sonrası
'FCBL' - Önde renkli ve arkada FCBL ile doldurun
Önde 'FLBC' gösterme çizgileri ve arkada FLBC ile doldurma
gösterilip gösterilmeyeceği
seçilip seçilemeyeceği
"" "
Yüzey modelinin köşe kümesini ve dizin kümesini oluşturmak için işlev prototipi aşağıdaki gibidir:
def _createSurface (self, v, c, t): "" "Üretilen yüzeyin köşe kümesi, dizin kümesi ve köşe dizisi türü v-Köşe koordinat seti, numpy.ndarray türü, şekil = (close, 3) c-vertex renk kümesi, Yok veya numpy.ndarray türü, şekil = (3 | 4,) | (cols, 3 | 4) t-köşenin doku koordinat seti, Yok veya numpy.ndarray türü, şekil = (cols, 2) "" "3D rekonstrüksiyon örneği
Baş BT'nin 109 tomografik görüntüsüne sahibim.Başın üç boyutlu rekonstrüksiyonunu denemek için bu görüntüleri kullanmayı planlıyorum. Temel görevlerden biri, bu görüntü verilerini okumak ve bunları üç boyutlu bir veri yapısında düzenlemektir (aslında dört boyutlu, çünkü her pikselin dört RGBA kanalı vardır).
Bu veri yapısı doğal olarak bir ndarray nesnesidir.Görüntü dosyalarını okumak için PIL kullanmaya alışkınım. Bu nedenle, iki modülün içe aktarılması gerekir:
numpy'yi np olarak içe aktar
PIL'den içe aktarma görüntüsü
Sonra, 109 resmi 109x256x256x4 numpy dizisine okumak için 172 milisaniye süren bir kod satırı kullandım:
veri = np.stack (, axis = 0)
3D rekonstrüksiyon kodu aşağıdaki gibidir:
# - * - kodlama: utf-8 - * -
numpy'yi np olarak içe aktar
PIL'den içe aktarma görüntüsü
wx içe aktar
ithal win32api
ithalat sys, os
wxgl.scene'den içe aktarma *
wxgl.colormap içe aktarımından *
FONT_FILE = r "C: \ Windows \ Yazı Tipleri \ simfang.ttf"
APP_TITLE = u'CT tomografi 3B yeniden yapılandırma aracı '
APP_ICON = 'res / head.ico'
sınıf mainFrame (wx.Frame):
'' 'Wx.Frame'den miras alınan program ana pencere sınıfı' ''
def __init __ (öz):
'' 'Yapıcı' ''
wx.Frame .__ init __ (self, None, -1, APP_TITLE, style = wx.DEFAULT_FRAME_STYLE)
self.SetBackgroundColour (wx.Colour (224, 224, 224))
self.SetSize ((800, 600))
self.Center
# Aşağıdaki kod, simgeyi
hasattr (sys, "frozen") ve getattr (sys, "frozen") == "windows_exe" ise:
exeName = win32api.GetModuleFileName (win32api.GetModuleHandle (Yok))
icon = wx.Icon (exeName, wx.BITMAP_TYPE_ICO)
Başka:
icon = wx.Icon (APP_ICON, wx.BITMAP_TYPE_ICO)
self.SetIcon (simge)
self.scene = WxGLScene (self, FONT_FILE, bg =)
# self.scene.setView ()
# self.scene.setPosture (elevation = 30, azimuth = -45, save = True)
self.master = self.scene.addRegion ((0,0,1,1))
# Baş BT'nin 109 tomografik görüntüsünü okuyun
veri = np.stack (, axis = 0)
# Üç boyutlu rekonstrüksiyon (esasen çizim verileri)
self.master.drawVolume ('birim', veri / 255.0, yöntem = 'Q', pürüzsüz = Yanlış)
self.master.update
sınıf mainApp (wx.App):
def OnInit (kendi):
self.SetAppName (APP_TITLE)
self.Frame = mainFrame
self.Frame.Show
True döndür
__name__ == "__main__" ise:
app = mainApp
app.MainLoop
3D rekonstrüksiyondan sonraki etki aşağıdaki gibidir:
Bu konuyla ilgileniyorsanız, lütfen doğrudan benimle iletişime geçin: xufive@sdysit.com
Orijinal: https://blog.csdn.net/xufive/article/details/97020456
Sorumluluk Reddi: Bu makale CSDN blogunun orijinal makalesidir, lütfen yeniden yazdırmak için orijinal yazarla iletişime geçin.
SON