add das
This commit is contained in:
parent
b277145104
commit
4337b10a87
139
src/beamformer/das.py
Normal file
139
src/beamformer/das.py
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
from threading import Thread
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
import cupy as cp
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from ds.Config import DeviceConfig
|
||||||
|
from imaging.dist import refraction_dist, direct_dist
|
||||||
|
from imaging.kernels import dist_mat_to_yids, dist_mat_to_yids_pwi
|
||||||
|
|
||||||
|
|
||||||
|
def repeat_range_in_axis(shape: tuple, axis: int,p=cp):
|
||||||
|
idx = [None for _ in shape]
|
||||||
|
idx[axis] = slice(None)
|
||||||
|
idx = tuple(idx)
|
||||||
|
return p.zeros(shape, dtype=p.uint8) + p.arange(shape[axis], dtype=p.uint8)[idx]
|
||||||
|
|
||||||
|
|
||||||
|
def gen_pwi(dist_mat: cp.ndarray, dev_cfg=DeviceConfig()):
|
||||||
|
col_idx = repeat_range_in_axis((dev_cfg.rows, dev_cfg.cols, dev_cfg.cols), 2)
|
||||||
|
row_idx = dist_mat_to_yids_pwi(dist_mat, dev_cfg)()
|
||||||
|
last_available_index = cp.where(row_idx[:, 128, 128] == (dev_cfg.rows))[0][0]
|
||||||
|
|
||||||
|
def pwi_(mat_in):
|
||||||
|
return mat_in[col_idx, row_idx].sum(axis=2).T[:, :last_available_index]
|
||||||
|
|
||||||
|
return pwi_, row_idx
|
||||||
|
|
||||||
|
|
||||||
|
class TFM:
|
||||||
|
def __init__(self, device_cfg=DeviceConfig()):
|
||||||
|
self.device_cfg = device_cfg
|
||||||
|
self.yks: list[cp.ndarray] = []
|
||||||
|
self.xks: list[cp.ndarray] = []
|
||||||
|
self.eks: list[cp.ndarray] = []
|
||||||
|
self.canvas: list[cp.ndarray] = []
|
||||||
|
self.inputs: list[cp.ndarray] = []
|
||||||
|
self.s2 = 8
|
||||||
|
for gpu_id in range(4):
|
||||||
|
with cp.cuda.Device(gpu_id):
|
||||||
|
self.canvas.append(cp.zeros((self.device_cfg.rows, 64), dtype=cp.int32))
|
||||||
|
self.eks.append(repeat_range_in_axis((device_cfg.rows, self.s2, device_cfg.cols, device_cfg.cols), 2))
|
||||||
|
self.xks.append(repeat_range_in_axis((device_cfg.rows, self.s2, device_cfg.cols, device_cfg.cols), 3))
|
||||||
|
|
||||||
|
def load_yids(self, gkx: Callable[[int], cp.ndarray]):
|
||||||
|
self.yks.clear()
|
||||||
|
for gpu_id in range(4):
|
||||||
|
with cp.cuda.Device(gpu_id):
|
||||||
|
self.yks.append(gkx(gpu_id * 64))
|
||||||
|
|
||||||
|
def load_bscan(self, bscan_mat_cpu):
|
||||||
|
self.inputs.clear()
|
||||||
|
for gpu_id in range(4):
|
||||||
|
with cp.cuda.Device(gpu_id):
|
||||||
|
self.inputs.append(cp.asarray(bscan_mat_cpu))
|
||||||
|
|
||||||
|
def get_idx_mat(self):
|
||||||
|
t = np.zeros((
|
||||||
|
self.device_cfg.rows,
|
||||||
|
self.device_cfg.cols,
|
||||||
|
self.device_cfg.cols,
|
||||||
|
self.device_cfg.cols
|
||||||
|
), dtype=np.uint16)
|
||||||
|
|
||||||
|
def send_thread(target, arr, idx):
|
||||||
|
target[:, idx * 64:(idx + 1) * 64, :, :] = arr[i].get()
|
||||||
|
|
||||||
|
ts = []
|
||||||
|
for i in range(4):
|
||||||
|
th = Thread(target=send_thread, args=(t, self.yks, i))
|
||||||
|
th.start()
|
||||||
|
ts.append(th)
|
||||||
|
for th in ts:
|
||||||
|
th.join()
|
||||||
|
return t
|
||||||
|
|
||||||
|
def __call__(self, bscan_mat_cpu=None):
|
||||||
|
if bscan_mat_cpu is not None:
|
||||||
|
self.load_bscan(bscan_mat_cpu)
|
||||||
|
assert self.inputs.__len__() > 0
|
||||||
|
ts = []
|
||||||
|
canvas_cpu = np.zeros((self.device_cfg.rows, self.device_cfg.cols))
|
||||||
|
for gpu_id in range(4):
|
||||||
|
with cp.cuda.Device(gpu_id):
|
||||||
|
for i in range(int(64 / self.s2)):
|
||||||
|
i_start = i * self.s2
|
||||||
|
i_end = (i + 1) * self.s2
|
||||||
|
a1 = self.eks[gpu_id]
|
||||||
|
a2 = self.xks[gpu_id]
|
||||||
|
a3 = self.yks[gpu_id][:, i_start:i_end, :, :]
|
||||||
|
if self.inputs[gpu_id].shape.__len__() == 2:
|
||||||
|
res = self.inputs[gpu_id][a2, a3]
|
||||||
|
else:
|
||||||
|
res = self.inputs[gpu_id][a1, a2, a3]
|
||||||
|
res = res.sum(axis=2)
|
||||||
|
res = res.sum(axis=2)
|
||||||
|
self.canvas[gpu_id][:, i_start:i_end] = res
|
||||||
|
|
||||||
|
def send_thread(canvas_, gpu_id_):
|
||||||
|
canvas_cpu[:, gpu_id_ * 64:(gpu_id_ + 1) * 64] = canvas_[gpu_id_].get()
|
||||||
|
|
||||||
|
t = Thread(target=send_thread, args=(self.canvas, gpu_id))
|
||||||
|
ts.append(t)
|
||||||
|
t.start()
|
||||||
|
for t in ts:
|
||||||
|
t.join()
|
||||||
|
return canvas_cpu.T
|
||||||
|
|
||||||
|
|
||||||
|
def test1():
|
||||||
|
device_cfg = DeviceConfig()
|
||||||
|
das = TFM()
|
||||||
|
das.load_yids()
|
||||||
|
das(np.ones((device_cfg.cols, device_cfg.cols, device_cfg.rows), dtype=np.int16))
|
||||||
|
|
||||||
|
|
||||||
|
def test2():
|
||||||
|
device_cfg = DeviceConfig()
|
||||||
|
tfm = TFM()
|
||||||
|
tfm.load_yids(dist_mat_to_yids(direct_dist()))
|
||||||
|
r = tfm(np.ones((device_cfg.cols, device_cfg.cols, device_cfg.rows), dtype=np.int16))
|
||||||
|
print(r.shape)
|
||||||
|
|
||||||
|
|
||||||
|
def test3():
|
||||||
|
device_cfg = DeviceConfig()
|
||||||
|
pwi, pwi_row_idx = gen_pwi(refraction_dist())
|
||||||
|
pwi(cp.ones((device_cfg.cols, device_cfg.rows), dtype=np.int16))
|
||||||
|
|
||||||
|
def test4():
|
||||||
|
device_cfg = DeviceConfig()
|
||||||
|
pwi, pwi_row_idx = gen_pwi(refraction_dist())
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
# test1()
|
||||||
|
# test2()
|
||||||
|
# test3()
|
||||||
|
test4()
|
||||||
69
src/beamformer/dist.py
Normal file
69
src/beamformer/dist.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
"""
|
||||||
|
dist function return mat f(y,x),
|
||||||
|
where f(y,x) is the pixel distance in echo pulse RF signal between point(0,0) and point(x,y).
|
||||||
|
"""
|
||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import fsolve
|
||||||
|
|
||||||
|
from ds.Config import DeviceConfig
|
||||||
|
from utils.filename import DS
|
||||||
|
|
||||||
|
|
||||||
|
def refraction_dist(dev_cfg=DeviceConfig()):
|
||||||
|
v1 = dev_cfg.v1
|
||||||
|
v1x = dev_cfg.v1x
|
||||||
|
v2 = dev_cfg.v2
|
||||||
|
y1 = dev_cfg.y1
|
||||||
|
SECONDS_PER_Y_PIX = dev_cfg.second_per_y_pix
|
||||||
|
METER_PER_X_PIX = dev_cfg.meter_per_x_pix
|
||||||
|
|
||||||
|
save_path = DS / f'{y1}_{v1x}_{v2}_fslove.npy'
|
||||||
|
if save_path.exists():
|
||||||
|
return np.load(str(save_path))
|
||||||
|
|
||||||
|
def gf(x, y2):
|
||||||
|
def f(arg):
|
||||||
|
θ1 = arg[0]
|
||||||
|
return [
|
||||||
|
y1 * np.tan(θ1) + v2 * SECONDS_PER_Y_PIX * y2 * np.tan(
|
||||||
|
np.arcsin(v2 / v1x * np.sin(θ1))) - METER_PER_X_PIX * x]
|
||||||
|
|
||||||
|
return f
|
||||||
|
|
||||||
|
iter_start = np.arcsin(v1x / v2 * np.sin(np.deg2rad(90)))
|
||||||
|
iter_start = iter_start // 1e-7 / 1e+7
|
||||||
|
m = np.zeros((dev_cfg.rows, dev_cfg.cols - 1))
|
||||||
|
for y2_ in range(m.shape[0]):
|
||||||
|
for x_ in range(m.shape[1]):
|
||||||
|
m[y2_, x_] = fsolve(gf(x_ + 1, y2_ + 1), np.array([iter_start]))[0]
|
||||||
|
|
||||||
|
dmat = np.zeros((dev_cfg.rows, dev_cfg.cols))
|
||||||
|
t1 = (np.arange(dev_cfg.rows)) * SECONDS_PER_Y_PIX
|
||||||
|
y2_mat = (t1 * v2)[:, np.newaxis]
|
||||||
|
x1_mat = y1 * np.tan(m)
|
||||||
|
x2_mat = y2_mat * np.tan(np.arcsin((v2 / v1x) * np.sin(m)))
|
||||||
|
d1 = np.hypot(x1_mat, y1)
|
||||||
|
d2 = np.hypot(x2_mat, y2_mat)
|
||||||
|
dmat[:, 1:] = (d1 / v1 + d2 / v2)
|
||||||
|
dmat[:, 0] = y1 / v1 + t1
|
||||||
|
dmat /= SECONDS_PER_Y_PIX
|
||||||
|
np.save(save_path.__str__(), dmat)
|
||||||
|
return dmat
|
||||||
|
|
||||||
|
|
||||||
|
def direct_dist(dev_cfg=DeviceConfig(), p=np):
|
||||||
|
y, x = p.ogrid[:dev_cfg.rows, :dev_cfg.cols]
|
||||||
|
y1_meter = dev_cfg.y1
|
||||||
|
y2_meter = y * dev_cfg.second_per_y_pix * dev_cfg.v2
|
||||||
|
y_meter = y1_meter + y2_meter
|
||||||
|
x_meter = x * dev_cfg.meter_per_x_pix
|
||||||
|
xy_meter = p.hypot(y_meter, x_meter)
|
||||||
|
xy1_meter = xy_meter * (y1_meter / y_meter)
|
||||||
|
xy2_meter = xy_meter * (y2_meter / y_meter)
|
||||||
|
m = xy1_meter / dev_cfg.v1 + xy2_meter / dev_cfg.v2
|
||||||
|
return m / dev_cfg.second_per_y_pix
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
print(refraction_dist())
|
||||||
|
print(direct_dist())
|
||||||
101
src/beamformer/kernels.py
Normal file
101
src/beamformer/kernels.py
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
"""
|
||||||
|
yfi: y fire index
|
||||||
|
yfs: y fire shape
|
||||||
|
xfi: x fire index
|
||||||
|
xfs: x fire shape
|
||||||
|
xri: x receive index
|
||||||
|
xrs: x receive shape
|
||||||
|
"""
|
||||||
|
import cupy as cp
|
||||||
|
|
||||||
|
from ds.Config import DeviceConfig
|
||||||
|
from imaging.dist import direct_dist
|
||||||
|
|
||||||
|
|
||||||
|
def dist_mat_to_yids(dist_mat: cp.ndarray, dev_cfg=DeviceConfig()):
|
||||||
|
def yids(offset: int) -> cp.ndarray:
|
||||||
|
yfs = dev_cfg.rows
|
||||||
|
xfs = 64
|
||||||
|
xss = dev_cfg.cols
|
||||||
|
xrs = dev_cfg.cols
|
||||||
|
kernel = cp.RawKernel(f"""
|
||||||
|
extern "C" __global__ void k(unsigned short int *result, float * dist_mat, int offset)
|
||||||
|
{{
|
||||||
|
const long long cols = {dev_cfg.cols};
|
||||||
|
|
||||||
|
const long long yfi = blockIdx.x;
|
||||||
|
const long long yfs = gridDim.x;
|
||||||
|
|
||||||
|
const long long xfi = blockIdx.y;
|
||||||
|
const long long xfs = gridDim.y;
|
||||||
|
|
||||||
|
const long long xsi = blockIdx.z;
|
||||||
|
const long long xss = gridDim.z;
|
||||||
|
|
||||||
|
const long long xri = threadIdx.x;
|
||||||
|
const long long xrs = blockDim.x;
|
||||||
|
|
||||||
|
long long idx = xri + xsi*xrs + xfi*xrs*xss + yfi*xrs*xss*xfs;
|
||||||
|
float r = dist_mat[yfi*cols+abs(xsi-(xfi + offset))] + dist_mat[yfi*cols+abs(xri-(xfi + offset))];
|
||||||
|
result[idx] = min(__float2int_rd(r), (int)yfs);
|
||||||
|
}}
|
||||||
|
""", 'k')
|
||||||
|
result = cp.zeros(yfs * xfs * xss * xrs, dtype=cp.uint16)
|
||||||
|
kernel((yfs, xfs, xss), (xrs, 1, 1), (
|
||||||
|
result,
|
||||||
|
cp.asarray(dist_mat.astype(cp.float32)),
|
||||||
|
cp.int32(offset)
|
||||||
|
))
|
||||||
|
result = result.reshape((yfs, xfs, xss, xrs))
|
||||||
|
return result
|
||||||
|
|
||||||
|
return yids
|
||||||
|
|
||||||
|
|
||||||
|
def dist_mat_to_yids_pwi(dist_mat: cp.ndarray, dev_cfg=DeviceConfig()):
|
||||||
|
"""
|
||||||
|
min in cuda is for __/\\__
|
||||||
|
:param dist_mat: cp.ndarray from dist.py
|
||||||
|
:param dev_cfg: DeviceConfig
|
||||||
|
:return: pixel distance (yf,xf,xr)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def yids() -> cp.ndarray:
|
||||||
|
yfs = dev_cfg.rows
|
||||||
|
xfs = dev_cfg.cols
|
||||||
|
xrs = dev_cfg.cols
|
||||||
|
kernel = cp.RawKernel(f"""
|
||||||
|
extern "C" __global__ void k(unsigned short int *result, float * dist_mat)
|
||||||
|
{{
|
||||||
|
const long long cols = {dev_cfg.cols};
|
||||||
|
|
||||||
|
const long long yfi = blockIdx.x;
|
||||||
|
const long long yfs = gridDim.x;
|
||||||
|
|
||||||
|
const long long xfi = blockIdx.y;
|
||||||
|
const long long xfs = gridDim.y;
|
||||||
|
|
||||||
|
const long long xri = threadIdx.x;
|
||||||
|
const long long xrs = blockDim.x;
|
||||||
|
|
||||||
|
long long idx = xri + xfi*xrs + yfi*xrs*xfs; //(yf,xf,xr)
|
||||||
|
float r = dist_mat[yfi*cols] + dist_mat[yfi*cols+abs(xri-xfi)];
|
||||||
|
result[idx] = min(__float2int_rd(r), (int)yfs);
|
||||||
|
}}
|
||||||
|
""", 'k')
|
||||||
|
result = cp.zeros(yfs * xfs * xrs, dtype=cp.uint16)
|
||||||
|
kernel((yfs, xfs), (xrs, 1, 1), (result, cp.asarray(dist_mat.astype(cp.float32))))
|
||||||
|
result = result.reshape((yfs, xfs, xrs))
|
||||||
|
return result
|
||||||
|
|
||||||
|
return yids
|
||||||
|
|
||||||
|
|
||||||
|
def test1():
|
||||||
|
f = dist_mat_to_yids(direct_dist())
|
||||||
|
r = f(0)
|
||||||
|
print(r.flags['C_CONTIGUOUS'], r.flags['F_CONTIGUOUS'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test1()
|
||||||
44
src/beamformer/process.py
Normal file
44
src/beamformer/process.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
from ds.Config import ImagingConfig
|
||||||
|
from imaging.ScanData import ScanData
|
||||||
|
import cupy as cp
|
||||||
|
|
||||||
|
from imaging.das import TFM
|
||||||
|
|
||||||
|
tfm_cache = [None]
|
||||||
|
|
||||||
|
|
||||||
|
def pwi_process(s: ScanData, icfg: ImagingConfig, pwi):
|
||||||
|
return (s
|
||||||
|
.filter_max_persent(icfg.bscan_max/1000, bid=True)
|
||||||
|
.sum(cond=s.d == 3)
|
||||||
|
.dct(icfg.dct_start, icfg.dct_end)
|
||||||
|
.call(lambda m: m.astype(cp.int16))
|
||||||
|
.call(pwi)
|
||||||
|
.call(cp.asarray, order='C')
|
||||||
|
.argrelextrema()
|
||||||
|
.conv_guass(b=icfg.beta * 0.01)
|
||||||
|
.clip(icfg.focus_start, icfg.focus_end)
|
||||||
|
.filter_max_persent(icfg.focus_max/100, mmax=icfg.focus_mmax if icfg.uafm else None)
|
||||||
|
.time_gain_compensation_linear_max(icfg.tgcl, mmax=icfg.focus_mmax if icfg.uafm else None)
|
||||||
|
.cpu()
|
||||||
|
.get()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def tfm_process(s: ScanData, icfg: ImagingConfig, disable_cache: bool, tfm: TFM):
|
||||||
|
# print(icfg.changed_field, icfg.changed_field in ['dct_start', 'dct_end', 'bscan_max'], disable_cache)
|
||||||
|
if icfg.changed_field in ['dct_start', 'dct_end', 'bscan_max'] or tfm_cache[0] is None or disable_cache:
|
||||||
|
tfm_cache[0] = (s.dct(icfg.dct_start, icfg.dct_end)
|
||||||
|
.filter_max_persent(icfg.bscan_max/1000, bid=True)
|
||||||
|
.call(lambda m: m.astype(cp.int16))
|
||||||
|
.call(tfm))
|
||||||
|
return (tfm_cache[0]
|
||||||
|
.call(cp.asarray, order='C')
|
||||||
|
.argrelextrema(axis=1)
|
||||||
|
.conv_guass(b=icfg.beta * 0.01, axis=1)
|
||||||
|
.clip(icfg.focus_start, icfg.focus_end)
|
||||||
|
.filter_max_persent(icfg.focus_max, mmax=icfg.focus_mmax if icfg.uafm else None)
|
||||||
|
.time_gain_compensation_linear_max(icfg.tgcl, mmax=icfg.focus_mmax if icfg.uafm else None)
|
||||||
|
.cpu()
|
||||||
|
.get()
|
||||||
|
)
|
||||||
54
src/utils/Config.py
Normal file
54
src/utils/Config.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
|
|
||||||
|
from utils.filename import LAST_CONFIG, CONFIG_FOLDER
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ImagingConfig:
|
||||||
|
focus_start: int = 0
|
||||||
|
focus_end: int = 1500
|
||||||
|
focus_max: int = 100
|
||||||
|
focus_min: int = 0
|
||||||
|
focus_mmax: int = 8192
|
||||||
|
uafm: bool = False
|
||||||
|
|
||||||
|
bscan_start: int = 0
|
||||||
|
bscan_end: int = 1500
|
||||||
|
bscan_max: int = 100
|
||||||
|
bscan_min: int = 0
|
||||||
|
bscan_mmax: int = 8192
|
||||||
|
|
||||||
|
dct_start: int = 300
|
||||||
|
dct_end: int = 550
|
||||||
|
|
||||||
|
eid: int = 0
|
||||||
|
beta: int = 1
|
||||||
|
|
||||||
|
tgcl: int = 0
|
||||||
|
|
||||||
|
changed_field: str = None
|
||||||
|
changed_field_orig_value: str | int = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_file(input_format, focus_mode, folder=CONFIG_FOLDER):
|
||||||
|
try:
|
||||||
|
return ImagingConfig(**json.load((folder / f'icfg_{input_format}_{focus_mode}.json').open()))
|
||||||
|
except Exception as e:
|
||||||
|
return ImagingConfig()
|
||||||
|
|
||||||
|
def save(self, input_format: str, focus_mode: str, folder=CONFIG_FOLDER):
|
||||||
|
json.dump(self.__dict__, (folder / f'icfg_{input_format}_{focus_mode}.json').open('w'))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DeviceConfig:
|
||||||
|
second_per_y_pix: float = 2e-8
|
||||||
|
meter_per_x_pix: float = 2e-4
|
||||||
|
|
||||||
|
rows: int = 1500
|
||||||
|
cols: int = 256
|
||||||
|
v1: int = 1915
|
||||||
|
v1x: int = 1915
|
||||||
|
v2: int = 5900
|
||||||
|
y1: float = 1e-3
|
||||||
348
src/utils/ScanData.py
Normal file
348
src/utils/ScanData.py
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
import cupy as cp
|
||||||
|
import cupyx
|
||||||
|
import cv2
|
||||||
|
import matplotlib
|
||||||
|
import scipy
|
||||||
|
import cupyx.scipy.signal
|
||||||
|
import scipy.signal
|
||||||
|
import cupyx.scipy.fft
|
||||||
|
import cupyx.scipy.ndimage
|
||||||
|
from cupyx.scipy.fft import dctn, idctn
|
||||||
|
from scipy.stats import norm as norms
|
||||||
|
|
||||||
|
|
||||||
|
def skip(f):
|
||||||
|
def wrapper(self, **kwargs):
|
||||||
|
if 'cond' not in kwargs:
|
||||||
|
return f(self, **kwargs)
|
||||||
|
if kwargs['cond']:
|
||||||
|
del kwargs['cond']
|
||||||
|
return f(self, **kwargs)
|
||||||
|
else:
|
||||||
|
return self
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
class ScanData:
|
||||||
|
@staticmethod
|
||||||
|
def from_buffer(buf: bytes | memoryview, shape, p=np):
|
||||||
|
return ScanData(p.frombuffer(buf, dtype=p.int16, count=np.prod(shape)).reshape(shape))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_file(filename: Path | str, shape, p=np):
|
||||||
|
filename = Path(filename)
|
||||||
|
return ScanData.from_buffer(filename.read_bytes(), shape, p)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_npy(filename: Path, p=np):
|
||||||
|
return ScanData(p.load(filename))
|
||||||
|
|
||||||
|
def __init__(self, mat: cp.ndarray):
|
||||||
|
if isinstance(mat, np.ndarray):
|
||||||
|
self.p = np
|
||||||
|
elif isinstance(mat, cp.ndarray):
|
||||||
|
self.p = cp
|
||||||
|
else:
|
||||||
|
raise Exception('wrong type mat!!')
|
||||||
|
self.m = mat
|
||||||
|
self.d = mat.shape.__len__()
|
||||||
|
|
||||||
|
def save(self, path: Path):
|
||||||
|
self.p.save(path.__str__(), self.m)
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# def p(self) -> np:
|
||||||
|
# if self._p == 'np':
|
||||||
|
# return np
|
||||||
|
# if self._p == 'cp':
|
||||||
|
# return cp
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
return self.m
|
||||||
|
|
||||||
|
def apply(self, f):
|
||||||
|
f(self.m)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def call(self, f, *args, **kwargs):
|
||||||
|
return ScanData(f(self.m, *args, **kwargs))
|
||||||
|
|
||||||
|
def copy(self):
|
||||||
|
return ScanData(self.m.copy())
|
||||||
|
|
||||||
|
def cpu(self):
|
||||||
|
if self.p == cp:
|
||||||
|
return ScanData(self.m.get())
|
||||||
|
if self.p == np:
|
||||||
|
return self.copy()
|
||||||
|
|
||||||
|
def gpu(self):
|
||||||
|
if self.p == np:
|
||||||
|
return ScanData(cp.asarray(self.m))
|
||||||
|
if self.p == cp:
|
||||||
|
return self.copy()
|
||||||
|
|
||||||
|
def norm(self):
|
||||||
|
m = self.m.astype(self.p.float32)
|
||||||
|
m -= m.min()
|
||||||
|
mmax = m.max()
|
||||||
|
if mmax == 0:
|
||||||
|
return ScanData(self.p.zeros_like(m))
|
||||||
|
m /= mmax
|
||||||
|
return ScanData(m)
|
||||||
|
|
||||||
|
@skip
|
||||||
|
def sum(self, axis=0):
|
||||||
|
return ScanData(self.m.sum(axis=axis))
|
||||||
|
|
||||||
|
def rotate90(self):
|
||||||
|
return ScanData(cv2.rotate(self.m, cv2.ROTATE_90_CLOCKWISE))
|
||||||
|
|
||||||
|
def filter_min(self, t, v=None, bid=False):
|
||||||
|
m = self.m
|
||||||
|
if v is None:
|
||||||
|
v = t
|
||||||
|
if bid:
|
||||||
|
m[(0 <= m) & (m <= t)] = v
|
||||||
|
m[(-t <= m) & (m <= 0)] = -v
|
||||||
|
else:
|
||||||
|
m[(m <= t)] = v
|
||||||
|
return self
|
||||||
|
|
||||||
|
def filter_max(self, t, v=None, bid=False):
|
||||||
|
m = self.m.copy()
|
||||||
|
if v is None:
|
||||||
|
v = t
|
||||||
|
if bid:
|
||||||
|
m[m >= t] = v
|
||||||
|
m[-m >= t] = -v
|
||||||
|
else:
|
||||||
|
m[m >= t] = v
|
||||||
|
return ScanData(m)
|
||||||
|
|
||||||
|
def filter_max_persent(self, t, v=None, bid=False, mmax=None):
|
||||||
|
if mmax is None:
|
||||||
|
mmax = self.m.max()
|
||||||
|
else:
|
||||||
|
self.m[0, 0] = mmax
|
||||||
|
return self.filter_max(mmax * t, v, bid)
|
||||||
|
|
||||||
|
def astype(self, t):
|
||||||
|
return ScanData(self.m.astype(t))
|
||||||
|
|
||||||
|
def dctn_cp(self):
|
||||||
|
return ScanData(dctn(self.m))
|
||||||
|
|
||||||
|
def dctn_cp_(self):
|
||||||
|
self.m = dctn(self.m)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def idctn_cp_(self):
|
||||||
|
self.m = idctn(self.m)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def dct(self, mmin, mmax):
|
||||||
|
dct_ = scipy.fft.dct
|
||||||
|
idct = scipy.fft.idct
|
||||||
|
if self.p == cp:
|
||||||
|
dct_ = cupyx.scipy.fft.dct
|
||||||
|
idct = cupyx.scipy.fft.idct
|
||||||
|
m_dct = dct_(self.m)
|
||||||
|
if self.d == 2:
|
||||||
|
m_dct[:, mmax:] = 0
|
||||||
|
m_dct[:, :mmin] = 0
|
||||||
|
elif self.d == 3:
|
||||||
|
m_dct[:, :, mmax:] = 0
|
||||||
|
m_dct[:, :, :mmin] = 0
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
return ScanData(idct(m_dct))
|
||||||
|
|
||||||
|
def argrelextrema(self, axis=1):
|
||||||
|
arg = scipy.signal.argrelextrema
|
||||||
|
m = self.m
|
||||||
|
p = self.p
|
||||||
|
if p == cp:
|
||||||
|
arg = cupyx.scipy.signal.argrelextrema
|
||||||
|
rm = p.zeros_like(m)
|
||||||
|
indies1 = arg(m, p.greater, axis=axis)
|
||||||
|
bl = p.zeros_like(m, dtype=p.bool_)
|
||||||
|
indies2 = arg(m, p.less, axis=axis)
|
||||||
|
bl2 = p.zeros_like(m, dtype=p.bool_)
|
||||||
|
bl[indies1] = True
|
||||||
|
bl2[indies2] = True
|
||||||
|
i1 = bl & (m > 0)
|
||||||
|
i2 = bl2 & (m < 0)
|
||||||
|
idx = i1 | i2
|
||||||
|
rm[idx] = m[idx].__abs__()
|
||||||
|
return ScanData(rm)
|
||||||
|
|
||||||
|
def conv_guass(self, b=0.01, axis=1):
|
||||||
|
cv = scipy.ndimage.convolve1d
|
||||||
|
m = self.m
|
||||||
|
p = self.p
|
||||||
|
if p == cp:
|
||||||
|
cv = cupyx.scipy.ndimage.convolve1d
|
||||||
|
rv = norms(loc=0, scale=b)
|
||||||
|
x2 = np.arange(-1, 1.1, 0.1)
|
||||||
|
w = rv.pdf(x2)
|
||||||
|
if p == cp:
|
||||||
|
w = cp.asarray(w)
|
||||||
|
rm = cv(m, w, axis=axis)
|
||||||
|
return ScanData(rm)
|
||||||
|
|
||||||
|
def clip(self, start, end):
|
||||||
|
return ScanData(self.m[:, start:end])
|
||||||
|
|
||||||
|
def print_contiguous(self):
|
||||||
|
print(self.m.flags['C_CONTIGUOUS'], self.m.flags['F_CONTIGUOUS'])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def rgb(self):
|
||||||
|
return ScanData((self.norm().m * 255).astype(np.uint8)[:, :, None].repeat(3, axis=2))
|
||||||
|
|
||||||
|
def pseudo_color(self):
|
||||||
|
m = self.m * 0.7
|
||||||
|
p = self.p
|
||||||
|
h = m
|
||||||
|
s = p.zeros_like(h) + 1
|
||||||
|
v = p.zeros_like(h) + 1
|
||||||
|
hsv = p.stack((h, s, v), axis=2)
|
||||||
|
if p == cp:
|
||||||
|
rgb = matplotlib.colors.hsv_to_rgb(hsv.get())
|
||||||
|
else:
|
||||||
|
rgb = matplotlib.colors.hsv_to_rgb(hsv)
|
||||||
|
return ScanData((rgb * 255).astype(np.uint8))
|
||||||
|
|
||||||
|
def pseudo_color_rgb(self):
|
||||||
|
m = (1 - self.m) * 0.7
|
||||||
|
p = self.p
|
||||||
|
h = m
|
||||||
|
s = p.zeros_like(h) + 1
|
||||||
|
v = p.zeros_like(h) + 1
|
||||||
|
hsv = p.stack((h, s, v), axis=2)
|
||||||
|
if p == cp:
|
||||||
|
rgb = matplotlib.colors.hsv_to_rgb(hsv.get())
|
||||||
|
else:
|
||||||
|
rgb = matplotlib.colors.hsv_to_rgb(hsv)
|
||||||
|
return ScanData((rgb * 255).astype(np.uint8))
|
||||||
|
|
||||||
|
def grey_color(self):
|
||||||
|
m = self.norm().m
|
||||||
|
return ScanData((m * 255).astype(np.uint8))
|
||||||
|
|
||||||
|
def time_gain_compensation_linear(self, scale: float, start: int = 0):
|
||||||
|
h = self.m.shape[-1]
|
||||||
|
addend = self.p.zeros((1, h), dtype=np.int64)
|
||||||
|
addend[:, start:] = self.p.arange(h - start) * scale
|
||||||
|
self.m += addend
|
||||||
|
return self
|
||||||
|
|
||||||
|
def time_gain_compensation_linear_float(self, scale: float, start: int = 0):
|
||||||
|
h = self.m.shape[-1]
|
||||||
|
addend = self.p.zeros((1, h), dtype=self.p.float32)
|
||||||
|
addend[:, start:] = (self.p.arange(h - start) * scale) + 1
|
||||||
|
print(addend)
|
||||||
|
return ScanData(self.m * addend)
|
||||||
|
|
||||||
|
def time_gain_compensation_linear_max(self, scale: float, mmax: int | None = None, start: int = 0):
|
||||||
|
if scale == 0:
|
||||||
|
return self
|
||||||
|
if mmax is None:
|
||||||
|
mmax = self.m.max()
|
||||||
|
h = self.m.shape[-1]
|
||||||
|
self.m = self.m.astype(np.float64)
|
||||||
|
mmax_arr = self.p.zeros(h) + mmax
|
||||||
|
mmax_arr[start:] -= self.p.arange(h - start) * scale
|
||||||
|
for i in range(h):
|
||||||
|
# a[1, a[1, :] > 99] = 99
|
||||||
|
self.m[self.m[:, i] > mmax_arr[i], i] = mmax_arr[i]
|
||||||
|
self.m[:, i] *= (mmax / mmax_arr[i])
|
||||||
|
self.m[self.m > mmax] = mmax
|
||||||
|
self.m = self.m.astype(np.int64)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def hib(self):
|
||||||
|
if self.p == cp:
|
||||||
|
hilbert = cupyx.scipy.signal.hilbert
|
||||||
|
else:
|
||||||
|
hilbert = scipy.signal.hilbert
|
||||||
|
return ScanData(hilbert(self.m))
|
||||||
|
|
||||||
|
def abs(self):
|
||||||
|
return ScanData(self.p.abs(self.m))
|
||||||
|
|
||||||
|
def log(self):
|
||||||
|
return ScanData(self.p.log(self.m))
|
||||||
|
|
||||||
|
def show(self, cmap='grey', w=40, h=20, no_axis=False, hsv=False):
|
||||||
|
from matplotlib import pyplot as plt
|
||||||
|
# fig = plt.figure(figsize=(w, h))
|
||||||
|
fig, ax = plt.subplots(figsize=(w, h))
|
||||||
|
if no_axis:
|
||||||
|
ax.get_xaxis().set_ticks([])
|
||||||
|
ax.get_yaxis().set_ticks([])
|
||||||
|
image = self.m if self.p == np else self.m.get()
|
||||||
|
if hsv:
|
||||||
|
image = self.norm().pseudo_color_rgb().m
|
||||||
|
plt.imshow(image, cmap=cmap)
|
||||||
|
return self
|
||||||
|
|
||||||
|
def plot_center_line(self):
|
||||||
|
from matplotlib import pyplot as plt
|
||||||
|
plt.figure(figsize=(40, 5))
|
||||||
|
m = self.m if self.p == np else self.m.get()
|
||||||
|
plt.plot(m[128, :])
|
||||||
|
return self
|
||||||
|
|
||||||
|
def windows(self, fn, center_f, width_f, sps_f):
|
||||||
|
fft = scipy.fft.fft if self.p == np else cupyx.scipy.fft.fft
|
||||||
|
ifft = scipy.fft.ifft if self.p == np else cupyx.scipy.fft.ifft
|
||||||
|
|
||||||
|
seq_len = self.m.shape[-1]
|
||||||
|
per_f = sps_f / seq_len
|
||||||
|
|
||||||
|
center = int(center_f / per_f)
|
||||||
|
width = int(width_f / 2 / per_f)
|
||||||
|
|
||||||
|
start = center - width
|
||||||
|
end = center + width
|
||||||
|
|
||||||
|
fft_seq = fft(self.m)
|
||||||
|
window = fn(end - start)
|
||||||
|
filter = np.zeros(seq_len) + window[0]
|
||||||
|
filter[start:end] = window
|
||||||
|
filter[seq_len - end:seq_len - start] = window
|
||||||
|
fft_seq *= filter
|
||||||
|
return ScanData(self.p.real(ifft(fft_seq)))
|
||||||
|
|
||||||
|
def windows_plot(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def dctn(self, xmin, xmax, ymin, ymax):
|
||||||
|
dctn = scipy.fft.dctn if self.p == np else cupyx.scipy.fft.dctn
|
||||||
|
idctn = scipy.fft.idctn if self.p == np else cupyx.scipy.fft.idctn
|
||||||
|
|
||||||
|
dctm = dctn(self.m)
|
||||||
|
dctm[xmin:xmax, ymin:ymax] = 0
|
||||||
|
return ScanData(idctn(dctm))
|
||||||
|
|
||||||
|
def resize_XY(self, xdyd):
|
||||||
|
print(self.m.shape[0])
|
||||||
|
if self.p != np:
|
||||||
|
raise NotImplementedError()
|
||||||
|
return ScanData(cv2.resize(self.m, (int(self.m.shape[1] * xdyd), self.m.shape[0])))
|
||||||
|
|
||||||
|
|
||||||
|
def test1():
|
||||||
|
s = ScanData(np.array([1, 1, 2])).norm().call(np.add, 1)
|
||||||
|
print(s.m)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test1()
|
||||||
37
test/main.ipynb
Normal file
37
test/main.ipynb
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "initial_id",
|
||||||
|
"metadata": {
|
||||||
|
"collapsed": true
|
||||||
|
},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 2
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython2",
|
||||||
|
"version": "2.7.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 5
|
||||||
|
}
|
||||||
0
test/test.py
Normal file
0
test/test.py
Normal file
Loading…
Reference in New Issue
Block a user