/usr/lib/python3/dist-packages/segyio/tools.py is in python3-segyio 1.5.2-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 | import segyio
import numpy as np
import textwrap
def dt(f, fallback_dt=4000.0):
"""Since v1.1
Find a *dt* value in the SegyFile. If none is found use the provided *fallback_dt* value.
:type f: segyio.SegyFile
:type fallback_dt: float
:rtype: float
"""
return f.xfd.getdt(fallback_dt)
def sample_indexes(segyfile, t0=0.0, dt_override=None):
"""Since v1.1
Creates a list of values representing the samples in a trace at depth or time.
The list starts at *t0* and is incremented with am*dt* for the number of samples.
If a *dt_override* is not provided it will try to find a *dt* in the file.
:type segyfile: segyio.SegyFile
:type t0: float
:type dt_override: float or None
:rtype: list[float]
"""
if dt_override is None:
dt_override = dt(segyfile)
return [t0 + t * dt_override for t in range(len(segyfile.samples))]
def create_text_header(lines):
"""
Will create a "correct" SEG-Y textual header.
Every line will be prefixed with C## and there are 40 lines.
The input must be a dictionary with the line number[1-40] as a key.
The value for each key should be up to 76 character long string.
:type lines: dict[int, str]
:rtype: str
"""
rows = []
for line_no in range(1, 41):
line = ""
if line_no in lines:
line = lines[line_no]
row = "C{0:>2} {1:76}".format(line_no, line)
rows.append(row)
rows = ''.join(rows)
return rows
def wrap(s, width=80):
"""Since v1.1
Formats the text input with newlines given the user specified width for each line
:type s: str
:type width: int
:rtype: str
"""
return '\n'.join(textwrap.wrap(str(s), width=width))
def native(data,
format = segyio.SegySampleFormat.IBM_FLOAT_4_BYTE,
copy = True):
""" Convert numpy array to native float
Since v1.1
:type data: numpy.ndarray
:type format: int|segyio.SegySampleFormat
:type copy: bool
:rtype: numpy.ndarray
Converts a numpy array from raw segy trace data to native floats. Works for numpy ndarrays.
Examples:
Convert mmap'd trace to native float:
>>> d = np.memmap('file.sgy', offset = 3600, dtype = np.uintc)
>>> samples = 1500
>>> trace = segyio.tools.native(d[240:240+samples])
"""
data = data.view( dtype = np.single )
if copy:
data = np.copy( data )
format = int(segyio.SegySampleFormat(format))
return segyio._segyio.native(data, format)
def collect(itr):
""" Collect traces or lines into one ndarray
Since v1.1
Eagerly copy a series of traces, lines or depths into one numpy ndarray. If
collecting traces or fast-direction over a post-stacked file, reshaping the
resulting array is equivalent to calling `tools.cube`.
Examples:
collect-cube identity::
>>> f = segyio.open('post-stack.sgy')
>>> x = segyio.tools.collect(f.traces[:])
>>> x = x.reshape((len(f.ilines), len(f.xlines), f.samples))
>>> numpy.all(x == segyio.tools.cube(f))
:type itr: iterable[numpy.ndarray]
:rtype: numpy.ndarray
"""
return np.stack([np.copy(x) for x in itr])
def cube(f):
""" Read a full cube from a file
Since v1.1
Takes an open segy file (created with segyio.open) or a file name.
If the file is a prestack file, the cube returned has the dimensions
(fast,slow,offset,sample). If it is post-stack (i.e. only the one offset),
the dimensions are normalised to (fast,slow,sample)
:type f: SegyFile|str
:rtype numpy.ndarray
"""
if not isinstance(f, segyio.SegyFile):
with segyio.open(f) as fl:
return cube(fl)
ilsort = f.sorting == segyio.TraceSortingFormat.INLINE_SORTING
fast = f.ilines if ilsort else f.xlines
slow = f.xlines if ilsort else f.ilines
fast, slow, offs = len(fast), len(slow), len(f.offsets)
smps = len(f.samples)
dims = (fast, slow, smps) if offs == 1 else (fast, slow, offs, smps)
return f.trace.raw[:].reshape(dims)
def rotation(f, line = 'fast'):
""" Find rotation of the survey
Since v1.2
Find the clock-wise rotation and origin of `line` as (rot,cdp-x,cdp-y)
The clock-wise rotation is defined as the angle in radians between line
given by the first and last trace of the first line and the axis that gives
increasing CDP-Y, in the direction that gives increasing CDP-X.
By default, the first line is the 'fast' direction, which is inlines if the
file is inline sorted, and crossline if it's crossline sorted. `line`
should be any of 'fast', 'slow', 'iline', and 'xline'.
:type f: SegyFile
:type line: str
:rtype (float, int, int)
"""
if f.unstructured:
raise ValueError("Rotation requires a structured file")
lines = { 'fast': f.fast,
'slow': f.slow,
'iline': f.iline,
'xline': f.xline,
}
if line not in lines:
error = "Unknown line {}".format(line)
solution = "Must be any of: {}".format(' '.join(lines.keys()))
raise ValueError('{} {}'.format(error, solution))
l = lines[line]
origin = f.header[0][segyio.su.cdpx, segyio.su.cdpy]
cdpx, cdpy = origin[segyio.su.cdpx], origin[segyio.su.cdpy]
rot = f.xfd.rotation( l.len,
l.stride,
len(f.offsets),
np.asarray(l.lines, order = 'C', dtype = np.intc) )
return rot, cdpx, cdpy
def metadata(f):
""" Get survey structural properties and metadata
Since v1.4
Create a descriptor object that, when passed to `segyio.create()`, would
create a new file with the same structure, dimensions and metadata as `f`.
Takes an open segy file (created with segyio.open) or a file name.
:type f: SegyFile|str
:rtype segyio.spec
"""
if not isinstance(f, segyio.SegyFile):
with segyio.open(f) as fl:
return metadata(fl)
spec = segyio.spec()
spec.iline = f._il
spec.xline = f._xl
spec.samples = f.samples
spec.format = f.format
spec.ilines = f.ilines
spec.xlines = f.xlines
spec.offsets = f.offsets
spec.sorting = f.sorting
spec.tracecount = f.tracecount
spec.ext_headers = f.ext_headers
return spec
def resample(f, rate = None, delay = None, micro = False,
trace = True,
binary = True):
""" Resample a file
Since v1.4
Resample all data traces, and update the file handle to reflect the new
sample rate. No actual samples (data traces) are modified, only the header
fields and interpretation.
By default, the rate and the delay are in millseconds - if you need higher
resolution, passing micro=True interprets rate as microseconds (as it is
represented in the file). Delay is always milliseconds.
By default, both the global binary header and the trace headers are updated
to reflect this. If preserving either the trace header interval field or
the binary header interval field is important, pass trace=False and
binary=False respectively, to not have that field updated. This only apply
to sample rates - the recording delay is only found in trace headers and
will be written unconditionally, if delay is not None.
This function requires an open file handle and is DESTRUCTIVE. It will
modify the file, and if an exception is raised then partial writes might
have happened and the file might be corrupted.
This function assumes all traces have uniform delays and frequencies.
:type f: SegyFile
:type rate: int
:type delay: int
:type micro: bool
:type trace: bool
:type binary: bool
"""
if rate is not None:
if not micro: rate *= 1000
if binary: f.bin[segyio.su.hdt] = rate
if trace: f.header = { segyio.su.dt: rate}
if delay is not None:
f.header = { segyio.su.delrt: delay }
t0 = delay if delay is not None else f.samples[0]
rate = rate / 1000 if rate is not None else f.samples[1] - f.samples[0]
f._samples = (np.arange(len(f.samples), dtype = np.single) * rate) + t0
return f
|