first-breaks-picking 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
File without changes
first_breaks/const.py ADDED
@@ -0,0 +1,32 @@
1
+ from os import environ
2
+ from sys import platform
3
+ from pathlib import Path
4
+
5
+
6
+ def get_cache_folder() -> Path:
7
+ if platform == "linux" or platform == "linux2":
8
+ return Path(environ.get("XDG_CACHE_HOME", Path.home() / ".cache")) / "first_breaks_picking"
9
+
10
+ elif platform == "darwin": # mac os
11
+ return Path.home() / "Library" / "Caches" / "first_breaks_picking"
12
+
13
+ elif platform.startswith("win"):
14
+ return Path.home() / ".cache" / "first_breaks_picking"
15
+
16
+ else:
17
+ raise ValueError(f"Unexpected platform {platform}.")
18
+
19
+
20
+ PROJECT_ROOT = Path(__file__).parent.parent
21
+ CACHE_FOLDER = get_cache_folder()
22
+
23
+ DEMO_SGY_PATH = CACHE_FOLDER / 'real_gather.sgy'
24
+ DEMO_SGY_URL = 'https://raw.githubusercontent.com/DaloroAT/first_breaks_picking/main/data/real_gather.sgy'
25
+ DEMO_SGY_HASH = '92fe2992b57d69c6f572c672f63960cf'
26
+
27
+
28
+ MODEL_ONNX_PATH = CACHE_FOLDER / 'fb.onnx'
29
+ MODEL_ONNX_URL = 'https://oml.daloroserver.com/download/seis/fb.onnx'
30
+ MODEL_ONNX_HASH = '7e39e017b01325180e36885eccaeb17a'
31
+
32
+ TIMEOUT = 60
File without changes
@@ -0,0 +1,398 @@
1
+ import warnings
2
+ from pathlib import Path
3
+ from typing import Union, Tuple, Sequence, List, Optional
4
+
5
+ import numpy as np
6
+ import pyqtgraph as pg
7
+ from PyQt5.QtCore import Qt, QTimer, pyqtSlot
8
+ from PyQt5.QtGui import QFont, QPen, QPainterPath, QColor
9
+ from PyQt5.QtWidgets import QApplication
10
+ from pyqtgraph.exporters import ImageExporter
11
+
12
+ from first_breaks.picking.task import Task
13
+ from first_breaks.picking.utils import preprocess_gather
14
+ from first_breaks.sgy.reader import SGY
15
+
16
+
17
+ TColor = Union[Tuple[int, int, int, int], Tuple[int, int, int]]
18
+
19
+
20
+ class GraphDefaults:
21
+ normalize: bool = True
22
+ gain: float = 1.0
23
+ clip: float = 0.9
24
+ fill_black_left: bool = True
25
+ picks_color: TColor = (255, 0, 0)
26
+ region_contour_color: TColor = (100, 100, 100)
27
+ region_contour_width: float = 0.2
28
+ region_poly_color: TColor = (100, 100, 100, 50)
29
+
30
+
31
+
32
+ class GraphWidget(pg.PlotWidget):
33
+
34
+ def __init__(self, *args, **kwargs):
35
+ super().__init__(*args, **kwargs)
36
+ self.getPlotItem().disableAutoRange()
37
+ self.setAntialiasing(True)
38
+ self.getPlotItem().setClipToView(True)
39
+ self.getPlotItem().setDownsampling(mode='peak')
40
+
41
+ self.getPlotItem().invertY(True)
42
+ self.getPlotItem().showAxis('top', True)
43
+ self.getPlotItem().showAxis('bottom', False)
44
+ x_ax = self.getPlotItem().getAxis('top')
45
+ y_ax = self.getPlotItem().getAxis('left')
46
+ text_size = 12
47
+ labelstyle = {'font-size': f'{text_size}pt'}
48
+ font = QFont()
49
+ font.setPointSize(text_size)
50
+ x_ax.setLabel('trace', **labelstyle)
51
+ y_ax.setLabel('t, ms', **labelstyle)
52
+ x_ax.setTickFont(font)
53
+ y_ax.setTickFont(font)
54
+ self.plotItem.ctrlMenu = None
55
+
56
+ self.sgy = None
57
+ self.picks_as_item = None
58
+ self.processing_region_as_items = []
59
+ self.traces_as_items = []
60
+
61
+ def plotseis(self,
62
+ sgy: SGY,
63
+ clip: float = GraphDefaults.clip,
64
+ gain: float = GraphDefaults.gain,
65
+ normalize: bool = GraphDefaults.normalize,
66
+ fill_black_left: bool = GraphDefaults.fill_black_left,
67
+ refresh_view: bool = True):
68
+
69
+ self.sgy = sgy
70
+ traces = self.sgy.read()
71
+
72
+ traces = preprocess_gather(traces, gain=gain, clip=clip, normalize=normalize, copy=True)
73
+
74
+ self.remove_traces()
75
+ num_sample, num_traces = self.sgy.shape
76
+ t = np.arange(num_sample) * self.sgy.dt_ms
77
+
78
+ self.getViewBox().setLimits(xMin=0, xMax=num_traces + 1, yMin=0, yMax=t[-1])
79
+
80
+ if refresh_view:
81
+ self.getPlotItem().setYRange(0, t[-1])
82
+ self.getPlotItem().setXRange(0, num_traces + 1)
83
+
84
+ for idx in range(num_traces):
85
+ self._plot_trace_fast(traces[:, idx], t, idx + 1, fill_black_left)
86
+
87
+ def _plot_trace_fast(self, trace: np.ndarray, t: np.ndarray, shift: int, fill_black_left: bool):
88
+ connect = np.ones(len(t), dtype=np.int32)
89
+ connect[-1] = 0
90
+
91
+ trace[0] = 0
92
+ trace[-1] = 0
93
+
94
+ shifted_trace = trace + shift
95
+ path = pg.arrayToQPath(shifted_trace, t, connect)
96
+
97
+ item = pg.QtWidgets.QGraphicsPathItem(path)
98
+ pen = QPen(Qt.black, 1, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)
99
+ # pen.setWidthF(0.01)
100
+ pen.setWidth(0.1)
101
+ item.setPen(pen)
102
+ item.setBrush(Qt.white)
103
+ self.addItem(item)
104
+
105
+ rect = QPainterPath()
106
+
107
+ sign = -1 if fill_black_left else 1
108
+ rect.addRect(shift, t[0], sign * 1.1 * max(np.abs(trace)), t[-1])
109
+
110
+ patch = path.intersected(rect)
111
+ item = pg.QtWidgets.QGraphicsPathItem(patch)
112
+
113
+ pen = QPen(QColor(255, 255, 255, 0), 1, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)
114
+ # pen.setWidthF(0.01)
115
+ pen.setWidth(0.1)
116
+ item.setPen(pen)
117
+ item.setBrush(Qt.black)
118
+ self.addItem(item)
119
+ self.traces_as_items.append(item)
120
+
121
+ def remove_picks(self):
122
+ if self.picks_as_item:
123
+ self.removeItem(self.picks_as_item)
124
+
125
+ def remove_processing_region(self):
126
+ if self.processing_region_as_items:
127
+ for item in self.processing_region_as_items:
128
+ self.removeItem(item)
129
+
130
+ def remove_traces(self):
131
+ if self.traces_as_items:
132
+ for item in self.traces_as_items:
133
+ self.removeItem(item)
134
+
135
+ def plot_processing_region(self,
136
+ traces_per_gather: int,
137
+ maximum_time: float,
138
+ contour_color: TColor = GraphDefaults.region_contour_color,
139
+ poly_color: TColor = GraphDefaults.region_poly_color,
140
+ contour_width: float = GraphDefaults.region_contour_width
141
+ ):
142
+ self.remove_processing_region()
143
+
144
+ num_sample, num_traces = self.sgy.shape
145
+ sgy_end_time = (num_sample + 2) * self.sgy.dt_ms
146
+ region_start_time = maximum_time if maximum_time > 0 else sgy_end_time
147
+
148
+ contour_pen = QPen(QColor(*contour_color), contour_width, Qt.DashLine, Qt.FlatCap, Qt.MiterJoin)
149
+ poly_brush = QColor(*poly_color)
150
+
151
+ # Vertical lines
152
+ line_t = np.array([0, region_start_time])
153
+ for idx in np.arange(traces_per_gather + 0.5, num_traces - 1, traces_per_gather):
154
+ line_x = np.array([idx, idx])
155
+ line_path = pg.arrayToQPath(line_x, line_t, np.ones(2, dtype=np.int32))
156
+ line_item = pg.QtWidgets.QGraphicsPathItem(line_path)
157
+ line_item.setPen(contour_pen)
158
+ self.processing_region_as_items.append(line_item)
159
+ self.addItem(line_item)
160
+
161
+ # Transparent polygon on bottom part
162
+ poly_x = np.array([-2, num_traces + 2, num_traces + 2, -2])
163
+ poly_t = np.array([region_start_time, region_start_time, sgy_end_time, sgy_end_time])
164
+ poly_path = pg.arrayToQPath(poly_x, poly_t, np.ones(4, dtype=np.int32))
165
+ poly_item = pg.QtWidgets.QGraphicsPathItem(poly_path)
166
+ poly_item.setPen(contour_pen)
167
+ poly_item.setBrush(poly_brush)
168
+ self.processing_region_as_items.append(poly_item)
169
+ self.addItem(poly_item)
170
+
171
+ def plot_picks(self, picks_ms: Sequence[float], color: TColor = GraphDefaults.picks_color):
172
+ self.remove_picks()
173
+
174
+ num_traces = self.sgy.shape[1]
175
+ picks_ms = np.array(picks_ms)
176
+ ids = np.arange(num_traces) + 1
177
+
178
+ path = pg.arrayToQPath(ids, picks_ms, np.ones(num_traces, dtype=np.int32))
179
+ self.picks_as_item = pg.QtWidgets.QGraphicsPathItem(path)
180
+
181
+ pen = pg.mkPen(color=color, width=3)
182
+ self.picks_as_item.setPen(pen)
183
+ self.addItem(self.picks_as_item)
184
+
185
+
186
+ class HighMemoryConsumption(Exception):
187
+ pass
188
+
189
+
190
+ class UnsupportedImageSize(Exception):
191
+ pass
192
+
193
+
194
+ need_kwargs_exception = ValueError("Please, use named arguments instead of ordered for visualizations parameters. "
195
+ "E.g. instead of `export(sgy, 'gather.png', 1.5)` use"
196
+ " `export(sgy, 'gather.png', clip=1.5)`")
197
+
198
+
199
+ class GraphExporter(GraphWidget):
200
+ MAX_SIDE_SIZE = 65000
201
+ MAX_NUM_PIXELS = MAX_SIDE_SIZE * 2000
202
+
203
+ @classmethod
204
+ def avoid_memory_bomb(cls,
205
+ height: int,
206
+ width_per_trace: int,
207
+ pixels_for_headers: int,
208
+ num_traces: int
209
+ ):
210
+ height_image = height + pixels_for_headers
211
+ width_image = pixels_for_headers + width_per_trace * num_traces
212
+
213
+ if height_image > cls.MAX_SIDE_SIZE:
214
+ message = f'It is not possible to render a picture of the given height = {height_image}. ' \
215
+ f'Max height = {cls.MAX_SIDE_SIZE}. Decrease rendering parameters.'
216
+ raise UnsupportedImageSize(message)
217
+
218
+ if width_image > cls.MAX_SIDE_SIZE:
219
+ message = f'It is not possible to render a picture of the given width = {width_image}. ' \
220
+ f'Max width = {cls.MAX_SIDE_SIZE}. Decrease rendering parameters.'
221
+ raise UnsupportedImageSize(message)
222
+
223
+ num_pixels = height_image * width_image
224
+
225
+ if num_pixels > cls.MAX_NUM_PIXELS:
226
+ message = f'The size of the picture will turn out to be too large ({num_pixels} pixels) for ' \
227
+ f'this SGY file. Decrease rendering parameters.'
228
+ raise HighMemoryConsumption(message)
229
+
230
+ def export(self,
231
+ sgy: SGY,
232
+ image_filename: Optional[Union[str, Path]],
233
+ *args,
234
+ clip: float = GraphDefaults.clip,
235
+ gain: float = GraphDefaults.gain,
236
+ normalize: bool = GraphDefaults.normalize,
237
+ fill_black_left: bool = GraphDefaults.fill_black_left,
238
+ time_window: Optional[Tuple[float, float]] = None,
239
+ traces_window: Optional[Tuple[float, float]] = None,
240
+ picks_ms: Optional[Sequence[float]] = None,
241
+ task: Optional[Task] = None,
242
+ show_processing_region: bool = True,
243
+ picks_color: TColor = GraphDefaults.picks_color,
244
+ contour_color: TColor = GraphDefaults.region_contour_color,
245
+ poly_color: TColor = GraphDefaults.region_poly_color,
246
+ contour_width: float = GraphDefaults.region_contour_width,
247
+ height: int = 500,
248
+ width_per_trace: int = 20,
249
+ pixels_for_headers: int = 20,
250
+ ):
251
+ if args:
252
+ raise need_kwargs_exception
253
+
254
+ if picks_ms is not None and task is not None:
255
+ raise ValueError("'picks_ms' and 'task' are mutually exclusive. Use only one of them or none")
256
+
257
+ num_traces = sgy.num_traces
258
+ self.avoid_memory_bomb(height, width_per_trace, pixels_for_headers, num_traces)
259
+
260
+ self.clear()
261
+ self.plotseis(sgy,
262
+ normalize=normalize,
263
+ clip=clip,
264
+ gain=gain,
265
+ fill_black_left=fill_black_left,
266
+ refresh_view=True)
267
+
268
+ if task:
269
+ picks_to_plot = task.picks_in_ms
270
+ elif picks_ms:
271
+ picks_to_plot = picks_ms
272
+ else:
273
+ picks_to_plot = None
274
+
275
+ if picks_to_plot is not None:
276
+ self.plot_picks(picks_to_plot, color=picks_color)
277
+
278
+ if task is not None and show_processing_region:
279
+ self.plot_processing_region(maximum_time=task.maximum_time_parsed,
280
+ traces_per_gather=task.traces_per_gather_parsed,
281
+ contour_color=contour_color,
282
+ poly_color=poly_color,
283
+ contour_width=contour_width)
284
+
285
+ self.plotItem.setFixedHeight(pixels_for_headers + height)
286
+ self.plotItem.setFixedWidth(pixels_for_headers + width_per_trace * num_traces)
287
+
288
+ if time_window:
289
+ self.getPlotItem().setYRange(time_window[0], time_window[1], padding=0)
290
+
291
+ if traces_window:
292
+ self.getPlotItem().setXRange(traces_window[0], traces_window[1], padding=0)
293
+
294
+ image = ImageExporter(self.plotItem).export(toBytes=True)
295
+
296
+ if image_filename:
297
+ Path(image_filename).parent.mkdir(exist_ok=True, parents=True)
298
+ image.save(str(image_filename), quality=100)
299
+
300
+ QTimer.singleShot(0, self.close_widget)
301
+
302
+ @pyqtSlot()
303
+ def close_widget(self):
304
+ self.close()
305
+
306
+
307
+ def export_image(source: Union[str, Path, bytes, SGY],
308
+ image_filename: Optional[Union[str, Path]],
309
+ *args,
310
+ dt_mcs: float = 1e3,
311
+ clip: float = GraphDefaults.clip,
312
+ gain: float = GraphDefaults.gain,
313
+ normalize: bool = GraphDefaults.normalize,
314
+ fill_black_left: bool = GraphDefaults.fill_black_left,
315
+ time_window: Optional[Tuple[float, float]] = None,
316
+ traces_window: Optional[Tuple[float, float]] = None,
317
+ picks_ms: Optional[Sequence[float]] = None,
318
+ task: Optional[Task] = None,
319
+ show_processing_region: bool = True,
320
+ picks_color: TColor = GraphDefaults.picks_color,
321
+ contour_color: TColor = GraphDefaults.region_contour_color,
322
+ poly_color: TColor = GraphDefaults.region_poly_color,
323
+ contour_width: float = GraphDefaults.region_contour_width,
324
+ height: int = 500,
325
+ width_per_trace: int = 20,
326
+ pixels_for_headers: int = 20
327
+ ):
328
+ if args:
329
+ raise need_kwargs_exception
330
+
331
+ if isinstance(source, (str, Path, bytes)):
332
+ sgy = SGY(source)
333
+ elif isinstance(source, np.ndarray):
334
+ sgy = SGY(source, dt_mcs=dt_mcs)
335
+ elif isinstance(source, SGY):
336
+ sgy = source
337
+ else:
338
+ raise TypeError("Unsupported type for 'source'")
339
+
340
+ app = QApplication([])
341
+ app.setQuitOnLastWindowClosed(True)
342
+ window = GraphExporter(background='w')
343
+ window.hide()
344
+ window.export(sgy=sgy,
345
+ image_filename=image_filename,
346
+ clip=clip,
347
+ gain=gain,
348
+ normalize=normalize,
349
+ fill_black_left=fill_black_left,
350
+ time_window=time_window,
351
+ traces_window=traces_window,
352
+ picks_ms=picks_ms,
353
+ task=task,
354
+ show_processing_region=show_processing_region,
355
+ picks_color=picks_color,
356
+ contour_color=contour_color,
357
+ poly_color=poly_color,
358
+ contour_width=contour_width,
359
+ height=height,
360
+ width_per_trace=width_per_trace,
361
+ pixels_for_headers=pixels_for_headers
362
+ )
363
+ app.exec()
364
+
365
+
366
+ if __name__ == '__main__':
367
+ # task = BaseTask(Path('/home/daloro/small.sgy'), traces_per_shot=24, time_window=(0, 100), time_unit='ms')
368
+ # task.result.picks_samples = np.random.randint(100, 200, 96)[:45]
369
+ # # task.result.picks_samples[9: 15] = nn_config.not_presented_fb_value
370
+ #
371
+ # task.result.picks_samples = task.result.picks_samples.astype(int).tolist()
372
+ #
373
+ # task.result.processed_traces = list(range(96))[:45]
374
+ import numpy as np
375
+ import time
376
+ from first_breaks.const import PROJECT_ROOT, DEMO_SGY_PATH
377
+
378
+ sgy = SGY(DEMO_SGY_PATH)
379
+ # sgy = SGY(np.random.uniform(-2, 2, (1000, 200)), dt_mcs=1e3)
380
+ print(sgy.traces_headers.head())
381
+ task = Task(sgy, maximum_time=100)
382
+ task.picks_in_samples = np.random.uniform(0, 100, sgy.num_traces)
383
+
384
+ st = time.perf_counter()
385
+ export_image(image_filename=PROJECT_ROOT / 'data/export.jpg',
386
+ source=sgy,
387
+ height=600,
388
+ width_per_trace=20,
389
+ pixels_for_headers=10,
390
+ task=task,
391
+ # time_window=(0, 100),
392
+ # traces_window=(10, 20),
393
+ show_processing_region=True,
394
+ gain=1,
395
+ clip=1)
396
+ print(time.perf_counter() - st)
397
+
398
+