QSlider 이벤트로 영상 명암을 조정하고싶습니다
조회수 1050회
QSlider 이벤트로 3D영상의 명암을 조정하고 싶습니다.
현재 제가 짠 코드는 버튼1을 누르고 슬라이드를 조정하고 버튼2를 누르면 명암이 수정됩니다
하지만 제가 원하는 코드는 버튼1을 이용하여 영상을 출력하고
이후 QSlider를 조정하면 설정한 값만큼 3D영상의 명암에 영향을 주고 싶습니다.
이때 버튼2는 누르지 않으려고 합니다.
어떻게 수정해야할까요 ㅠㅠ
vtk.py
import vtk
import sys
from PyQt5 import QtCore,QtWidgets
from PyQt5.QtGui import *
from vtk.qt.QVTKRenderWindowInteractor import QVTKRenderWindowInteractor
from PyQt5.QtWidgets import QMainWindow, QApplication
from foo import Ui_MainWindow
from PyQt5 import Qt
class MainWindow(QMainWindow, Ui_MainWindow):
def __init__(self, parent=None):#메인윈도우 띄우고
super(MainWindow, self).__init__(parent) #메인윈도우 상속받고
self.setupUi(self) #setupui상속받고
self.pushButton.clicked.connect(self.OpenVTK)
#self.pushButton1.clicked.connect(self.ETC)
self.pushButton2.clicked.connect(QtCore.QCoreApplication.instance().quit)
self.pushButton3.clicked.connect(self.UsingFilter)
self.pushButton4.clicked.connect(self.clear)
self.setMouseTracking(True)
def clear(self):
self.setupUi(self) #setupui상속받고
self.pushButton.clicked.connect(self.OpenVTK)
# self.pushButton1.clicked.connect(self.ETC)
self.pushButton2.clicked.connect(QtCore.QCoreApplication.instance().quit)
self.pushButton3.clicked.connect(self.UsingFilter)
self.pushButton4.clicked.connect(self.clear)
self.setMouseTracking(True)
def OpenVTK(self):
self.vtkWidget = QVTKRenderWindowInteractor(self.frame)
self.vl = Qt.QVBoxLayout()
self.vl.addWidget(self.vtkWidget)
self.ren = vtk.vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) # vtk widget에 렌더링할 ren을 넣어주고
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor() # 출력을 담당할 iren에 vtk widget정보를 입력
# renWin = vtk.vtkRenderWindow()
# renWin.AddRenderer(self.ren)
# self.iren = vtk.vtkRenderWindowInteractor()
# self.iren.SetRenderWindow(renWin)
# self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) #vtk widget에 렌더링할 ren을 넣어주고
# self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()#출력을 담당할 iren에 vtk widget정보를 입력
# Create source
colors = vtk.vtkNamedColors()
colors.SetColor("SkinColor", [255, 125, 64, 255])
colors.SetColor("BkgColor", [51, 77, 102, 255])
reader = vtk.vtkMetaImageReader()
reader.SetFileName('C:\\Users\\admin\\Downloads\\FullHead.mhd')
reader.Update()
skinExtractor = vtk.vtkMarchingCubes()
skinExtractor.SetInputConnection(reader.GetOutputPort())
skinExtractor.SetValue(0, 500)
skinExtractor.Update()
skinStripper = vtk.vtkStripper()
skinStripper.SetInputConnection(skinExtractor.GetOutputPort())
skinStripper.Update()
skinMapper = vtk.vtkPolyDataMapper()
skinMapper.SetInputConnection(skinStripper.GetOutputPort())
skinMapper.ScalarVisibilityOff()
skin = vtk.vtkActor()
skin.SetMapper(skinMapper)
skin.GetProperty().SetDiffuseColor(colors.GetColor3d("SkinColor"))
skin.GetProperty().SetSpecular(.3)
skin.GetProperty().SetSpecularPower(20)
# An isosurface, or contour value of 1150 is known to correspond to
# the bone of the patient.
# The triangle stripper is used to create triangle
# strips from the isosurface these render much faster on may
# systems.
boneExtractor = vtk.vtkMarchingCubes()
boneExtractor.SetInputConnection(reader.GetOutputPort())
boneExtractor.SetValue(0, 1150)
boneStripper = vtk.vtkStripper()
boneStripper.SetInputConnection(boneExtractor.GetOutputPort())
boneMapper = vtk.vtkPolyDataMapper()
boneMapper.SetInputConnection(boneStripper.GetOutputPort())
boneMapper.ScalarVisibilityOff()
bone = vtk.vtkActor()
bone.SetMapper(boneMapper)
bone.GetProperty().SetDiffuseColor(colors.GetColor3d("Ivory"))
# An outline provides context around the data.
#
outlineData = vtk.vtkOutlineFilter()
outlineData.SetInputConnection(reader.GetOutputPort())
outlineData.Update()
mapOutline = vtk.vtkPolyDataMapper()
mapOutline.SetInputConnection(outlineData.GetOutputPort())
outline = vtk.vtkActor()
outline.SetMapper(mapOutline)
outline.GetProperty().SetColor(colors.GetColor3d("White"))
# Now we are creating three orthogonal planes passing through the
# volume. Each plane uses a different texture map and therefore has
# different coloration.
# Start by creating a black/white lookup table.
value = int(self.horizontalSlider.value()) * 30
print(2000 + value)
bwLut = vtk.vtkLookupTable()
bwLut.SetTableRange(0, 2000+value)
bwLut.SetSaturationRange(0, 0)
bwLut.SetHueRange(0, 0)
bwLut.SetValueRange(0, 1)
bwLut.Build() # effective built
# Now create a lookup table that consists of the full hue circle
# (from HSV).
hueLut = vtk.vtkLookupTable()
hueLut.SetTableRange(0, 2000)
hueLut.SetHueRange(0, 0)
hueLut.SetSaturationRange(1, 1)
hueLut.SetValueRange(0, 1)
hueLut.Build() # effective built
# Finally, create a lookup table with a single hue but having a range
# in the saturation of the hue.
satLut = vtk.vtkLookupTable()
satLut.SetTableRange(0, 2000)
satLut.SetHueRange(.6, .6)
satLut.SetSaturationRange(0, 1)
satLut.SetValueRange(1, 1)
satLut.Build() # effective built
# Create the first of the three planes. The filter vtkImageMapToColors
# maps the data through the corresponding lookup table created above. The
# vtkImageActor is a type of vtkProp and conveniently displays an image on
# a single quadrilateral plane. It does this using texture mapping and as
# a result is quite fast. (Note: the input image has to be unsigned char
# values, which the vtkImageMapToColors produces.) Note also that by
# specifying the DisplayExtent, the pipeline requests data of this extent
# and the vtkImageMapToColors only processes a slice of data.
sagittalColors = vtk.vtkImageMapToColors()
sagittalColors.SetInputConnection(reader.GetOutputPort())
sagittalColors.SetLookupTable(bwLut)
sagittalColors.Update()
sagittal = vtk.vtkImageActor()
sagittal.GetMapper().SetInputConnection(sagittalColors.GetOutputPort())
sagittal.SetDisplayExtent(128, 128, 0, 255, 0, 92) ###앞 두 파라미터로 Sagittal 의 위치조절
# Create the second (axial) plane of the three planes. We use the
# same approach as before except that the extent differs.
axialColors = vtk.vtkImageMapToColors()
axialColors.SetInputConnection(reader.GetOutputPort())
axialColors.SetLookupTable(bwLut)
axialColors.Update()
axial = vtk.vtkImageActor()
axial.GetMapper().SetInputConnection(axialColors.GetOutputPort())
axial.SetDisplayExtent(0, 255, 0, 255, 46, 46)
# Create the third (coronal) plane of the three planes. We use
# the same approach as before except that the extent differs.
coronalColors = vtk.vtkImageMapToColors()
coronalColors.SetInputConnection(reader.GetOutputPort())
coronalColors.SetLookupTable(bwLut)
coronalColors.Update()
coronal = vtk.vtkImageActor()
coronal.GetMapper().SetInputConnection(coronalColors.GetOutputPort())
coronal.SetDisplayExtent(0, 255, 128, 128, 0, 92)
# It is convenient to create an initial view of the data. The
# FocalPoint and Position form a vector direction. Later on
# (ResetCamera() method) this vector is used to position the camera
# to look at the data in this direction.
aCamera = vtk.vtkCamera()
aCamera.SetViewUp(0, 0, -1)
aCamera.SetPosition(0, -1, 0)
aCamera.SetFocalPoint(0, 0, 0)
aCamera.ComputeViewPlaneNormal()
aCamera.Azimuth(0.0)
aCamera.Elevation(0.0)
# Actors are added to the renderer.
self.ren.AddActor(outline)
self.ren.AddActor(sagittal)
self.ren.AddActor(axial)
self.ren.AddActor(coronal)
# self.ren.AddActor(skin)
# self.ren.AddActor(bone)
# Turn off bone for this example.
bone.VisibilityOn()
# Set skin to semi-transparent.
skin.GetProperty().SetOpacity(0.5)
# An initial camera view is created. The Dolly() method moves
# the camera towards the FocalPoint, thereby enlarging the image.
self.ren.SetActiveCamera(aCamera)
# Calling Render() directly on a vtkRenderer is strictly forbidden.
# Only calling Render() on the vtkRenderWindow is a valid call.
# renWin.Render()
self.show()
self.ren.ResetCamera()
self.frame.setLayout(self.vl)
aCamera.Dolly(1.5)
# Note that when camera movement occurs (as it does in the Dolly()
# method), the clipping planes often need adjusting. Clipping planes
# consist of two planes: near and far along the view direction. The
# near plane clips out objects in front of the plane; the far plane
# clips out objects behind the plane. This way only what is drawn
# between the planes is actually rendered.
self.ren.ResetCameraClippingRange()
# Interact with the data.
# renWin.Render()
self.show()
self.iren.Initialize()
self.iren.Start()
def UsingFilter(self, value):
self.vtkWidget = QVTKRenderWindowInteractor(self.frame1)
self.v2 = Qt.QVBoxLayout()
self.v2.addWidget(self.vtkWidget)
self.ren = vtk.vtkRenderer()
self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) # vtk widget에 렌더링할 ren을 넣어주고
self.iren = self.vtkWidget.GetRenderWindow().GetInteractor() # 출력을 담당할 iren에 vtk widget정보를 입력
# renWin = vtk.vtkRenderWindow()
# renWin.AddRenderer(self.ren)
# self.iren = vtk.vtkRenderWindowInteractor()
# self.iren.SetRenderWindow(renWin)
# self.vtkWidget.GetRenderWindow().AddRenderer(self.ren) #vtk widget에 렌더링할 ren을 넣어주고
# self.iren = self.vtkWidget.GetRenderWindow().GetInteractor()#출력을 담당할 iren에 vtk widget정보를 입력
# Create source
colors = vtk.vtkNamedColors()
colors.SetColor("SkinColor", [255, 125, 64, 255])
colors.SetColor("BkgColor", [51, 77, 102, 255])
reader = vtk.vtkMetaImageReader()
reader.SetFileName('C:\\Users\\admin\\Downloads\\FullHead.mhd')
reader.Update()
skinExtractor = vtk.vtkMarchingCubes()
skinExtractor.SetInputConnection(reader.GetOutputPort())
skinExtractor.SetValue(0, 500)
skinExtractor.Update()
skinStripper = vtk.vtkStripper()
skinStripper.SetInputConnection(skinExtractor.GetOutputPort())
skinStripper.Update()
skinMapper = vtk.vtkPolyDataMapper()
skinMapper.SetInputConnection(skinStripper.GetOutputPort())
skinMapper.ScalarVisibilityOff()
skin = vtk.vtkActor()
skin.SetMapper(skinMapper)
skin.GetProperty().SetDiffuseColor(colors.GetColor3d("SkinColor"))
skin.GetProperty().SetSpecular(.3)
skin.GetProperty().SetSpecularPower(20)
# An isosurface, or contour value of 1150 is known to correspond to
# the bone of the patient.
# The triangle stripper is used to create triangle
# strips from the isosurface these render much faster on may
# systems.
boneExtractor = vtk.vtkMarchingCubes()
boneExtractor.SetInputConnection(reader.GetOutputPort())
boneExtractor.SetValue(0, 1150)
boneStripper = vtk.vtkStripper()
boneStripper.SetInputConnection(boneExtractor.GetOutputPort())
boneMapper = vtk.vtkPolyDataMapper()
boneMapper.SetInputConnection(boneStripper.GetOutputPort())
boneMapper.ScalarVisibilityOff()
bone = vtk.vtkActor()
bone.SetMapper(boneMapper)
bone.GetProperty().SetDiffuseColor(colors.GetColor3d("Ivory"))
# An outline provides context around the data.
#
outlineData = vtk.vtkOutlineFilter()
outlineData.SetInputConnection(reader.GetOutputPort())
outlineData.Update()
mapOutline = vtk.vtkPolyDataMapper()
mapOutline.SetInputConnection(outlineData.GetOutputPort())
outline = vtk.vtkActor()
outline.SetMapper(mapOutline)
outline.GetProperty().SetColor(colors.GetColor3d("White"))
# Now we are creating three orthogonal planes passing through the
# volume. Each plane uses a different texture map and therefore has
# different coloration.
# Start by creating a black/white lookup table.
value = int(self.horizontalSlider.value()) * 30
print(2000 + value)
bwLut = vtk.vtkLookupTable()
bwLut.SetTableRange(0, 2000 + value)
bwLut.SetSaturationRange(0, 0)
bwLut.SetHueRange(0, 0)
bwLut.SetValueRange(0, 1)
bwLut.Build() # effective built
# Now create a lookup table that consists of the full hue circle
# (from HSV).
hueLut = vtk.vtkLookupTable()
hueLut.SetTableRange(0, 2000)
hueLut.SetHueRange(0, 0)
hueLut.SetSaturationRange(1, 1)
hueLut.SetValueRange(0, 1)
hueLut.Build() # effective built
# Finally, create a lookup table with a single hue but having a range
# in the saturation of the hue.
satLut = vtk.vtkLookupTable()
satLut.SetTableRange(0, 2000)
satLut.SetHueRange(.6, .6)
satLut.SetSaturationRange(0, 1)
satLut.SetValueRange(1, 1)
satLut.Build() # effective built
# Create the first of the three planes. The filter vtkImageMapToColors
# maps the data through the corresponding lookup table created above. The
# vtkImageActor is a type of vtkProp and conveniently displays an image on
# a single quadrilateral plane. It does this using texture mapping and as
# a result is quite fast. (Note: the input image has to be unsigned char
# values, which the vtkImageMapToColors produces.) Note also that by
# specifying the DisplayExtent, the pipeline requests data of this extent
# and the vtkImageMapToColors only processes a slice of data.
sagittalColors = vtk.vtkImageMapToColors()
sagittalColors.SetInputConnection(reader.GetOutputPort())
sagittalColors.SetLookupTable(bwLut)
sagittalColors.Update()
sagittal = vtk.vtkImageActor()
sagittal.GetMapper().SetInputConnection(sagittalColors.GetOutputPort())
sagittal.SetDisplayExtent(128, 128, 0, 255, 0, 92) ###앞 두 파라미터로 Sagittal 의 위치조절
# Create the second (axial) plane of the three planes. We use the
# same approach as before except that the extent differs.
axialColors = vtk.vtkImageMapToColors()
axialColors.SetInputConnection(reader.GetOutputPort())
axialColors.SetLookupTable(bwLut)
axialColors.Update()
axial = vtk.vtkImageActor()
axial.GetMapper().SetInputConnection(axialColors.GetOutputPort())
axial.SetDisplayExtent(0, 255, 0, 255, 46, 46)
# Create the third (coronal) plane of the three planes. We use
# the same approach as before except that the extent differs.
coronalColors = vtk.vtkImageMapToColors()
coronalColors.SetInputConnection(reader.GetOutputPort())
coronalColors.SetLookupTable(bwLut)
coronalColors.Update()
coronal = vtk.vtkImageActor()
coronal.GetMapper().SetInputConnection(coronalColors.GetOutputPort())
coronal.SetDisplayExtent(0, 255, 128, 128, 0, 92)
# It is convenient to create an initial view of the data. The
# FocalPoint and Position form a vector direction. Later on
# (ResetCamera() method) this vector is used to position the camera
# to look at the data in this direction.
aCamera = vtk.vtkCamera()
aCamera.SetViewUp(0, 0, -1)
aCamera.SetPosition(0, -1, 0)
aCamera.SetFocalPoint(0, 0, 0)
aCamera.ComputeViewPlaneNormal()
aCamera.Azimuth(0.0)
aCamera.Elevation(0.0)
# Actors are added to the renderer.
self.ren.AddActor(outline)
self.ren.AddActor(sagittal)
self.ren.AddActor(axial)
self.ren.AddActor(coronal)
#self.ren.AddActor(skin)
# self.ren.AddActor(bone)
# Turn off bone for this example.
bone.VisibilityOn()
# Set skin to semi-transparent.
skin.GetProperty().SetOpacity(0.5)
# An initial camera view is created. The Dolly() method moves
# the camera towards the FocalPoint, thereby enlarging the image.
self.ren.SetActiveCamera(aCamera)
# Calling Render() directly on a vtkRenderer is strictly forbidden.
# Only calling Render() on the vtkRenderWindow is a valid call.
# renWin.Render()
self.show()
self.ren.ResetCamera()
self.frame1.setLayout(self.v2)
aCamera.Dolly(1.5)
# Note that when camera movement occurs (as it does in the Dolly()
# method), the clipping planes often need adjusting. Clipping planes
# consist of two planes: near and far along the view direction. The
# near plane clips out objects in front of the plane; the far plane
# clips out objects behind the plane. This way only what is drawn
# between the planes is actually rendered.
self.ren.ResetCameraClippingRange()
# Interact with the data.
# renWin.Render()
self.show()
self.iren.Initialize()
self.iren.Start()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec_())
foo.py
from PyQt5 import QtCore, QtWidgets,QtGui
import numpy as np
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QMainWindow, QApplication, QDialog, QFileDialog, QSlider
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PIL import Image
import sys
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(900, 300)
vs1=0
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.frame = QtWidgets.QFrame(self.centralwidget)
self.frame.setGeometry(QtCore.QRect(220, 0, 310, 310))
self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame.setObjectName("frame")
self.frame1 = QtWidgets.QFrame(self.centralwidget)
self.frame1.setGeometry(QtCore.QRect(550, 10, 310, 300))
self.frame1.setFrameShape(QtWidgets.QFrame.StyledPanel)
self.frame1.setFrameShadow(QtWidgets.QFrame.Raised)
self.frame1.setObjectName("frame1")
self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
self.verticalLayoutWidget.setGeometry(QtCore.QRect(10, 10, 171, 301))
self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
self.verticalLayout.setContentsMargins(0, 0, 0, 0)
self.verticalLayout.setObjectName("verticalLayout")
self.horizontalSlider = QtWidgets.QSlider(self.verticalLayoutWidget)
self.horizontalSlider.setOrientation(QtCore.Qt.Horizontal)
self.verticalLayout.addWidget(self.horizontalSlider)
self.horizontalSlider.setMinimum(0)
self.horizontalSlider.setMaximum(100)
self.horizontalSlider.setValue(vs1)
self.horizontalSlider.setTickInterval(10)
self.horizontalSlider.setTickPosition(QSlider.TicksBelow)
self.horizontalSlider.setObjectName("horizontalSlider")
self.horizontalSlider.valueChanged.connect(self.valuechange)
self.horizontalSlider.bl
#print(self.horizontalSlider.value)
self.pushButton = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.pushButton.setObjectName("pushButton")
self.verticalLayout.addWidget(self.pushButton)
self.pushButton3 = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.pushButton3.setObjectName("pushButton3")
self.verticalLayout.addWidget(self.pushButton3)
self.pushButton2 = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.pushButton2.setObjectName("pushButton2")
self.verticalLayout.addWidget(self.pushButton2)
self.pushButton4 = QtWidgets.QPushButton(self.verticalLayoutWidget)
self.pushButton4.setObjectName("pushButton4")
self.verticalLayout.addWidget(self.pushButton2)
self.statusbar = self.statusBar()
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 671, 21))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
MainWindow.setCentralWidget(self.centralwidget)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
print(self.hasMouseTracking())
self.show()
count = 0
def valuechange(self,val):
self.count +=1
print("Scroll Value", self.horizontalSlider.value())
print("count", self.count)
def mousePressEvent(self, event):
txt = "Mouse 위치 ; x={0},y={1}, MR 영상내 위치={2},{3}".format(event.x(), event.y(), event.x()-550, event.y()-10)
self.statusbar.showMessage(txt)
print(event.globalX())
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
self.pushButton.setText(_translate("MainWindow", "OpenVTK"))
self.pushButton2.setText(_translate("MainWindow", "Close"))
self.pushButton3.setText(_translate("MainWindow", "Change_Intensity"))
self.pushButton4.setText(_translate("MainWindow", "Clear"))
-
(•́ ✖ •̀)
알 수 없는 사용자
2 답변
-
첫번째로 Qt를 학습하셔야 합니다.
슬라이더의 이벤트는
valueChanged, sliderPressed, sliderMoved, sliderReleased
이렇게 4개가 있습니다.valueChanged 는 슬라이더 값이 변할때마다 호출입니다.
sliderPressed 는 슬라이더 위젯을 누르자마자 호출합니다.
sliderMoved 는 슬라이더 위젯을 사용자가 드래그 할 때 호출됩니다.
sliderReleased 는 슬라이더에서 마우스 버튼을 클릭후 놓았을때(released) 호출합니다.
상기 질문의 예에서는 sliderReleased 를 사용해야 합니다.
foo.py 에서 아랫부분을 삭제합니다.
self.horizontalSlider.valueChanged.connect(self.valuechange) def valuechange(self,val): self.count +=1 print("Scroll Value", self.horizontalSlider.value()) print("count", self.count)
vtk.py 의 MainWindow 클래스내
__init__
에 아랫부분 추가합니다.self.horizontalSlider.sliderReleased.connect(self.valuechange)
vtk.py의 MainWindow 클래스내 아랫부분 추가합니다.
count = 0 def valuechange(self): self.count +=1 print("Scroll Value", self.horizontalSlider.value()) print("count", self.count) self.UsingFilter(self.horizontalSlider.value())
이렇게 하면 슬라이더를 움직이고 마우스 클릭버튼을 놓는 순간 UsingFilter 를 호출하여 바로 우측창에 변경된 이미지가 나타납니다.
그러나 두번째 이벤트부터는 안될겁니다.
- 만약 UsingFilter내에서 위젯을 선언하는 코드를 전역변수로 수정해서 함수밖으로 뺀다면 두번째 동작이 가능해질까요?? 알 수 없는 사용자 2019.5.20 12:56
-
github 에 레파지토리를 만들어두었습니다.
슬라이더를 이용할때마다 업데이트 되도록 수정했습니다.
다만 제가 vtk 를 잘 알고 있지는 못하므로 최소한의 수준에서 수정을 진행했습니다.
아래 링크에서 참고하시면 됩니다.
댓글 입력