[segyio] 02/376: Added GUI segyviewer as an example
Jørgen Kvalsvik
jokva-guest at moszumanska.debian.org
Wed Sep 20 08:03:57 UTC 2017
This is an automated email from the git hooks/post-receive script.
jokva-guest pushed a commit to branch debian
in repository segyio.
commit 64e94c17d79fe995f7848a3cdeced39dc66411d5
Author: Thorvald Johannessen <thorvjo at statoil.com>
Date: Mon Oct 3 12:15:44 2016 +0200
Added GUI segyviewer as an example
And added depth_plane functionality in python layer
---
examples/CMakeLists.txt | 2 +
examples/segyviewer.py | 387 ++++++++++++++++++++++++++++++++++++++++++++++++
python/segyio/segy.py | 47 ++++++
tests/test_segy.py | 88 ++++++++++-
4 files changed, 522 insertions(+), 2 deletions(-)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 46036bb..2c1f261 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,6 +1,8 @@
configure_file(../tests/test-data/small.sgy test-data/small.sgy COPYONLY)
+configure_file(segyviewer.py segyviewer.py COPYONLY)
add_python_example(python.examples.about about.py test-data/small.sgy INLINE_3D CROSSLINE_3D)
add_python_example(python.examples.write write.py test-data/small.sgy)
add_python_example(python.examples.makefile make-file.py test-data/large-file.sgy 20 1 20 1 20)
add_python_example(python.examples.subcube copy-sub-cube.py test-data/small.sgy test-data/copy.sgy)
+
diff --git a/examples/segyviewer.py b/examples/segyviewer.py
new file mode 100644
index 0000000..369a93e
--- /dev/null
+++ b/examples/segyviewer.py
@@ -0,0 +1,387 @@
+from PyQt4.QtGui import QAction
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+import segyio
+
+from pylab import *
+
+from PyQt4 import QtGui, QtCore
+
+from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.figure import Figure
+
+import matplotlib.patches as patches
+
+
+class LineSelectionMonitor(QObject):
+ ilineChanged = pyqtSignal(int)
+ xlineChanged = pyqtSignal(int)
+ depthChanged = pyqtSignal(int)
+
+ def __init__(self, parent=None):
+ QObject.__init__(self, parent)
+
+ def ilineUpdated(self, new_index):
+ print("iline:{0} updated", new_index)
+ self.ilineChanged.emit(new_index)
+
+ def xlineUpdated(self, new_index):
+ print("xline:{0} updated", new_index)
+ self.xlineChanged.emit(new_index)
+
+ def depthUpdated(self, new_index):
+ print("depth:{0} updated", new_index)
+ self.depthChanged.emit(new_index)
+
+
+class ColorMapMonitor(QObject):
+ cmap_changed = pyqtSignal(str)
+
+ def __init__(self, parent=None):
+ QObject.__init__(self, parent)
+
+ def colormapUpdated(self, value):
+ self.cmap_changed.emit(str(value))
+
+
+class PlotCanvas(FigureCanvas):
+ """
+ Generic plot canvas for all plane views
+ """
+
+ indexChanged = pyqtSignal(int)
+
+ def __init__(self, planes, indexes, dataset_title, cmap, display_horizontal_indicator=False,
+ display_vertical_indicator=False, parent=None, width=800, height=100, dpi=20):
+
+ self.planes = planes
+ self.indexes = indexes
+
+ self.dataset_title = dataset_title
+
+ self.fig = Figure(figsize=(width, height), dpi=dpi, facecolor='white')
+ FigureCanvas.__init__(self, self.fig)
+
+ self.setParent(parent)
+
+ self.axes = self.fig.add_subplot(111)
+ # self.axes.set_title(self.dataset_title, fontsize=50)
+ self.axes.set_xticks(indexes)
+ self.axes.tick_params(axis='both', labelsize=30)
+
+ # the default colormap
+ self.cmap = cmap
+ self.im = self.axes.imshow(planes[indexes[0]].T, interpolation="nearest", aspect="auto", cmap=self.cmap)
+
+ # initialize plot and indicator_rect
+ # self.plot_image(self.planes[self.indexes[0]])
+
+ self.current_index = 0
+
+ # connecting matplotlib mouse signals
+ self.mpl_connect('motion_notify_event', self.mouse_moved)
+ self.mpl_connect('button_press_event', self.mouse_clicked)
+ self.mpl_connect('axes_leave_event', self.mouse_left)
+
+ if display_vertical_indicator:
+ self.verdical_indicator_rect = self.axes.add_patch(
+ patches.Rectangle(
+ (-0.5, 0), # (x,y)
+ 1, # width - bredde er pr dot ikke pixel...
+ len(self.planes[self.indexes[0]][0]), # height / depth
+ fill=False,
+ alpha=1,
+ color='black',
+ linestyle='--',
+ linewidth=2
+ )
+ )
+
+ if display_horizontal_indicator:
+ self.horizontal_indicator_rect = self.axes.add_patch(
+ patches.Rectangle(
+ (-0.5, 0), # (x,y)
+ len(self.planes[self.indexes[0]][0]), # width - bredde er pr dot ikke pixel...
+ 1, # height / depth
+ fill=False,
+ alpha=1,
+ color='black',
+ linestyle='--',
+ linewidth=2
+ )
+ )
+
+ self.disabled_overlay = self.axes.add_patch(
+ patches.Rectangle(
+ (-0.5, 0), # (x,y)
+ len(self.planes[self.indexes[0]][0]), # width - bredde er pr dot ikke pixel...
+ len(self.planes[self.indexes[0]][0]), # height / depth
+ alpha=0.5,
+ color='gray',
+ visible=False
+ )
+ )
+
+ # initialize plot
+ print(self.dataset_title, self.cmap)
+
+ # self.plot_image(planes[indexes[0]])
+
+ def mouse_left(self, evt):
+ pass
+ # self.set_vertical_line_indicator(self.current_index)
+
+ def mouse_clicked(self, evt):
+ if evt.inaxes:
+ self.current_index = int(evt.xdata)
+ self.indexChanged.emit(self.indexes[int(evt.xdata)])
+
+ def mouse_moved(self, evt):
+ # do nothing
+ # if evt.inaxes:
+ # self.set_vertical_line_indicator(int(evt.xdata))
+ pass
+
+ def set_colormap(self, cmap):
+ self.cmap = cmap
+ self.im.set_cmap(cmap)
+ self.draw()
+
+ def update_image(self, index):
+ self.im.set_data(self.planes[index].T)
+ self.draw()
+
+ def set_vertical_line_indicator(self, line_index):
+ self.verdical_indicator_rect.set_x(line_index - 0.5)
+ self.verdical_indicator_rect.set_y(0)
+ self.draw()
+
+ def set_horizontal_line_indicator(self, line_index):
+ self.horizontal_indicator_rect.set_x(-0.5)
+ self.horizontal_indicator_rect.set_y(line_index)
+ self.draw()
+
+ def enable_overlay(self):
+ self.disabled_overlay.set_visible(True)
+ self.draw()
+
+ def disable_overlay(self):
+ self.disabled_overlay.set_visible(False)
+ self.draw()
+
+
+class PlotWidget(QtGui.QWidget):
+ """
+ Main widget holding the figure and slider
+ """
+
+ # indexChanged = pyqtSignal(int)
+
+ def __init__(self, planes, indexes, dataset_title, default_cmap='seismic',
+ show_h_indicator=False, show_v_indicator=False):
+ super(PlotWidget, self).__init__()
+
+ self.planes = planes
+ self.indexes = indexes
+ self.dataset_title = dataset_title
+ self.default_cmap = default_cmap
+ self.show_h_indicator = show_h_indicator
+ self.show_v_indicator = show_v_indicator
+ self.setAutoFillBackground(True)
+ p = self.palette()
+ p.setColor(self.backgroundRole(), Qt.white)
+ self.setPalette(p)
+
+ self.plotCanvas = PlotCanvas(self.planes, self.indexes, self.dataset_title, self.default_cmap,
+ display_horizontal_indicator=self.show_h_indicator,
+ display_vertical_indicator=self.show_v_indicator)
+
+ self.layout = QtGui.QVBoxLayout(self)
+ self.layout.addWidget(self.plotCanvas)
+
+ def set_cmap(self, action):
+ self.plotCanvas.set_colormap(str(action))
+
+ def set_vertical_line_indicator(self, line):
+ print("set vertical line ind:", line)
+ self.plotCanvas.set_vertical_line_indicator(line)
+
+ def set_horizontal_line_indicator(self, line):
+ print("set horizontal line ind:", line)
+ self.plotCanvas.set_horizontal_line_indicator(line)
+
+
+class TopMenu(QMenuBar):
+ def __init__(self, parent, colormap_monitor):
+ super(QMenuBar, self).__init__(parent)
+
+ self.fileMenu = self.addMenu('&File')
+ exitAction = QtGui.QAction('&Exit', self)
+ self.fileMenu.addAction(exitAction)
+
+ self.viewMenu = self.addMenu('&View')
+ self.colormapMenu = self.viewMenu.addMenu("&Colormap")
+
+ self.colormap_monitor = colormap_monitor
+
+ self.colormap_monitor.cmap_changed.connect(self.set_selected_cmap)
+
+ def colormapChanger(color_map_name):
+ def performColorMapChange():
+ self.colormap_monitor.colormapUpdated(color_map_name)
+
+ return performColorMapChange
+
+ for item in ['seismic', 'spectral', 'RdGy', 'hot', 'jet', 'gray']:
+ action = self.colormapMenu.addAction(item)
+ action.setCheckable(True)
+ action.triggered.connect(colormapChanger(item))
+
+ def set_selected_cmap(self, cmap_name):
+ for item in self.colormapMenu.actions():
+ item.setChecked(str(item.text()) == cmap_name)
+
+
+class LineSelector(QtGui.QWidget):
+ indexChanged = pyqtSignal(int)
+
+ def __init__(self, parent, label, indexes, monitor_func):
+ super(QtGui.QWidget, self).__init__(parent)
+ self.label = label
+ self.indexes = indexes
+ self.monitor_func = monitor_func
+
+ self.layout = QHBoxLayout()
+ self.slabel = QLabel(self.label)
+ self.sbox = QtGui.QSpinBox(self)
+ self.sbox.setRange(self.indexes[0], self.indexes[-1])
+ self.sbox.valueChanged.connect(self.monitor_func)
+ self.layout.addWidget(self.slabel)
+ self.layout.addWidget(self.sbox)
+ self.setLayout(self.layout)
+
+ def index_changed(self, val):
+ self.indexChanged.emit(self.indexes[val])
+
+ def set_index(self, val):
+ self.sbox.setValue(val)
+
+
+class ToolBar(QToolBar):
+ def __init__(self, xline_indexes, iline_indexes, depth_indexes, line_selection_monitor):
+ super(ToolBar, self).__init__("")
+ self.xline_indexes = xline_indexes
+ self.iline_indexes = iline_indexes
+ self.depth_indexes = depth_indexes
+ self.line_selection_monitor = line_selection_monitor
+
+ # xline
+ self.xline_selector = LineSelector(self, "xline", self.xline_indexes, self.line_selection_monitor.xlineUpdated)
+ self.line_selection_monitor.xlineChanged.connect(self.xline_selector.set_index)
+ self.addWidget(self.xline_selector)
+
+ # iline
+ self.iline_selector = LineSelector(self, "iline", self.iline_indexes, self.line_selection_monitor.ilineUpdated)
+ self.line_selection_monitor.ilineChanged.connect(self.iline_selector.set_index)
+ self.addWidget(self.iline_selector)
+
+ # iline
+
+ self.depth_selector = LineSelector(self, "depth", self.depth_indexes, self.line_selection_monitor.depthUpdated)
+ self.addWidget(self.depth_selector)
+
+
+class AppWindow(QtGui.QMainWindow):
+ def __init__(self, s):
+ QtGui.QMainWindow.__init__(self)
+ self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
+ self.setWindowTitle("Segy Viewer")
+ self.main_widget = QtGui.QWidget(self)
+
+ # signal monitors
+ colormap_monitor = ColorMapMonitor(self)
+ line_monitor = LineSelectionMonitor(self)
+
+ self.setMenuBar(TopMenu(self, colormap_monitor))
+ self.addToolBar(ToolBar(s.xlines, s.ilines, range(s.samples), line_monitor))
+ self.statusBar()
+
+ # depth = s.samples
+ # depth_plane = [] # [ : for s.xline[:,depth] in s.xline]
+ # all_traces = np.empty(shape=((len(s.ilines) * len(s.xlines)), s.samples), dtype=np.float32)
+ #
+ # for i, t in enumerate(s.trace):
+ # all_traces[i] = t
+ #
+ # all_traces2 = all_traces.reshape(len(s.ilines), len(s.xlines), s.samples)
+ #
+ # all_traces3 = all_traces2.transpose(2, 0, 1)
+
+
+
+ # initialize
+ x_plane_canvas = PlotWidget(s.xline, s.xlines, "xlines", show_v_indicator=True)
+ i_plane_canvas = PlotWidget(s.iline, s.ilines, "ilines", show_v_indicator=True)
+ depth_plane_canvas = PlotWidget(s.depth_plane, range(s.samples), "depth",
+ show_v_indicator=True, show_h_indicator=True)
+
+ # attach signals
+ x_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.ilineUpdated)
+ i_plane_canvas.plotCanvas.indexChanged.connect(line_monitor.xlineUpdated)
+
+ line_monitor.ilineChanged.connect(x_plane_canvas.set_vertical_line_indicator)
+ line_monitor.ilineChanged.connect(depth_plane_canvas.set_horizontal_line_indicator)
+ line_monitor.ilineChanged.connect(i_plane_canvas.plotCanvas.update_image)
+
+ line_monitor.xlineChanged.connect(i_plane_canvas.set_vertical_line_indicator)
+ line_monitor.xlineChanged.connect(depth_plane_canvas.set_vertical_line_indicator)
+ line_monitor.xlineChanged.connect(x_plane_canvas.plotCanvas.update_image)
+
+ line_monitor.depthChanged.connect(depth_plane_canvas.plotCanvas.update_image)
+
+ # colormap signals
+ colormap_monitor.cmap_changed.connect(x_plane_canvas.set_cmap)
+ colormap_monitor.cmap_changed.connect(i_plane_canvas.set_cmap)
+ colormap_monitor.cmap_changed.connect(depth_plane_canvas.set_cmap)
+
+ # layout
+ xdock = QDockWidget("x-lines")
+ xdock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
+ xdock.setWidget(x_plane_canvas)
+
+ idock = QDockWidget("i-lines")
+ idock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
+ idock.setWidget(i_plane_canvas)
+
+ ddock = QDockWidget("depth plane")
+ ddock.setFeatures(QtGui.QDockWidget.DockWidgetFloatable | QtGui.QDockWidget.DockWidgetMovable)
+ ddock.setWidget(depth_plane_canvas)
+
+ self.setDockOptions(QMainWindow.AllowNestedDocks)
+ self.addDockWidget(Qt.TopDockWidgetArea, xdock)
+ self.addDockWidget(Qt.BottomDockWidgetArea, idock)
+ self.addDockWidget(Qt.BottomDockWidgetArea, ddock)
+
+ self.main_widget.setFocus()
+ self.setCentralWidget(self.main_widget)
+ self.main_widget.hide()
+
+
+def main():
+ if len(sys.argv) < 2:
+ sys.exit("Usage: segyviewer.py [file]")
+
+ filename = sys.argv[1]
+
+ # read the file given by cmd arg
+ with segyio.open(filename, "r") as s:
+ qApp = QtGui.QApplication(sys.argv)
+ aw = AppWindow(s)
+ aw.show()
+ sys.exit(qApp.exec_())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/python/segyio/segy.py b/python/segyio/segy.py
index 1ef6c59..24e842f 100644
--- a/python/segyio/segy.py
+++ b/python/segyio/segy.py
@@ -171,6 +171,27 @@ class _line:
for i in self.lines:
yield self.__getitem__(i, buf)
+
+class _DepthPlane:
+
+ def __init__(self, samples, sorting, read_fn):
+
+ self.samples = samples
+ self.sorting = sorting
+ self.read_fn = read_fn
+
+ def __getitem__(self, depth):
+ if isinstance(depth, int):
+ return self.read_fn(self.sorting, depth)
+
+ def __len__(self):
+ return len(self.samples)
+
+ def __iter__(self):
+ for i in range(self.samples):
+ yield self.__getitem__(i)
+
+
class _header:
def __init__(self, segy):
self.segy = segy
@@ -1036,6 +1057,31 @@ class file(BaseCClass):
def xline(self, value):
self.xline[:] = value
+ @property
+ def depth_plane(self):
+
+ def depth_plane(sorting, depth):
+ il_len = self._iline_length
+ xl_len = self._xline_length
+
+ dim = None
+ if sorting == 1:
+ dim = (xl_len, il_len)
+ if sorting == 2:
+ dim = (il_len, xl_len)
+
+ if not dim:
+ raise Exception("TODO")
+
+ plane = np.empty(shape=dim[0] * dim[1], dtype=np.float32)
+
+ for i, t in enumerate(self.trace):
+ plane[i] = t[depth]
+
+ return plane.reshape(dim)
+
+ return _DepthPlane(self.samples, self.sorting, depth_plane)
+
def _readh(self, index, buf = None):
errc = self._read_header(index, buf, self._tr0, self._bsz)
err = _error(errc)
@@ -1300,3 +1346,4 @@ class spec:
self.format = None
self.sorting = None
self.t0 = 1111.0
+ self.depth_plane = None
diff --git a/tests/test_segy.py b/tests/test_segy.py
index 96b2cb0..4693240 100644
--- a/tests/test_segy.py
+++ b/tests/test_segy.py
@@ -3,9 +3,9 @@ from unittest import TestCase
import segyio
from segyio import TraceField, BinField
import shutil
-import sys
-import time
import filecmp
+import itertools
+
class TestSegy(TestCase):
@@ -75,6 +75,90 @@ class TestSegy(TestCase):
# last sample
self.assertAlmostEqual(5.22049, data[last_line, f.samples-1], places = 6)
+ @staticmethod
+ def make_file(filename, samples, first_iline, last_iline, first_xline, last_xline):
+
+ spec = segyio.spec()
+ # to create a file from nothing, we need to tell segyio about the structure of
+ # the file, i.e. its inline numbers, crossline numbers, etc. You can also add
+ # more structural information, but offsets etc. have sensible defaults. This is
+ # the absolute minimal specification for a N-by-M volume
+ spec.sorting = 2
+ spec.format = 1
+ spec.samples = samples
+ spec.ilines = range(*map(int, [first_iline, last_iline]))
+ spec.xlines = range(*map(int, [first_xline, last_xline]))
+
+ with segyio.create(filename, spec) as f:
+ start = 0.0
+ step = 0.00001
+ # fill a trace with predictable values: left-of-comma is the inline
+ # number. Immediately right of comma is the crossline number
+ # the rightmost digits is the index of the sample in that trace meaning
+ # looking up an inline's i's jth crosslines' k should be roughly equal
+ # to i.j0k
+ trace = np.arange(start = start,
+ stop = start + step * spec.samples,
+ step = step,
+ dtype = np.float32)
+
+ # one inline is N traces concatenated. We fill in the xline number
+ line = np.concatenate([trace + (xl / 100.0) for xl in spec.xlines])
+
+ # write the line itself to the file
+ # write the inline number in all this line's headers
+ for ilno in spec.ilines:
+ f.iline[ilno] = (line + ilno)
+ f.header.iline[ilno] = { segyio.TraceField.INLINE_3D: ilno,
+ segyio.TraceField.offset: 1
+ }
+
+ # then do the same for xlines
+ for xlno in spec.xlines:
+ f.header.xline[xlno] = { segyio.TraceField.CROSSLINE_3D: xlno }
+
+ @staticmethod
+ def il_sample(s):
+ return int(s)
+
+ @staticmethod
+ def xl_sample(s):
+ return int(round((s-int(s))*100))
+
+ @classmethod
+ def depth_sample(cls, s):
+ return int(round((s - cls.il_sample(s) - cls.xl_sample(s)/100.0)*10e2,2)*100)
+
+ def test_make_file(self):
+ filename = "test.segy"
+ samples = 10
+ self.make_file(filename, samples, 0, 2, 10, 13)
+
+ with segyio.open(filename, "r") as f:
+ for xlno, xl in itertools.izip(f.xlines, f.xline):
+ for ilno, trace in itertools.izip(f.ilines, xl):
+ for sample_index, sample in itertools.izip(range(samples), trace):
+ self.assertEqual(self.il_sample(sample), ilno,
+ ("sample: {0}, ilno {1}".format(self.il_sample(sample), ilno)))
+ self.assertEqual(self.xl_sample(sample), xlno,
+ ("sample: {0}, xlno {1}, sample {2}".format(
+ self.xl_sample(sample), xlno, sample)))
+ self.assertEqual(self.depth_sample(sample), sample_index,
+ ("sample: {0}, sample_index {1}, real_sample {2}".format(
+ self.depth_sample(sample), sample_index, sample)))
+
+ def test_read_all_depth_planes(self):
+ filename = "test.segy"
+ samples = 10
+ self.make_file(filename, samples, 0, 2, 10, 13)
+
+ with segyio.open(filename, "r") as f:
+ for i, plane in enumerate(f.depth_plane):
+ for ilno, xlno in itertools.product(range(len(f.ilines)), range(len(f.xlines))):
+ self.assertEqual(self.depth_sample(plane[xlno, ilno]), i,
+ "plane[{0},{1}] == {2}, should be 0".format(
+ ilno, xlno, self.depth_sample(plane[xlno, ilno])))
+
def test_iline_slicing(self):
with segyio.open(self.filename, "r") as f:
self.assertEqual(len(f.ilines), sum(1 for _ in f.iline))
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/segyio.git
More information about the debian-science-commits
mailing list