|
|
import gradio as gr |
|
|
import numpy as np |
|
|
import plotly.graph_objs as go |
|
|
from scipy.ndimage import convolve |
|
|
from gradio_imageslider import ImageSlider |
|
|
import cv2 |
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def mipi_raw10_to_raw8_scaled(raw10_data): |
|
|
raw10_data = np.frombuffer(raw10_data, dtype=np.uint8) |
|
|
n_blocks = len(raw10_data) // 5 |
|
|
raw10_data = raw10_data[:n_blocks * 5].reshape(-1, 5) |
|
|
|
|
|
p0 = (raw10_data[:, 0].astype(np.uint16) << 2) | ((raw10_data[:, 4] >> 0) & 0x03) |
|
|
p1 = (raw10_data[:, 1].astype(np.uint16) << 2) | ((raw10_data[:, 4] >> 2) & 0x03) |
|
|
p2 = (raw10_data[:, 2].astype(np.uint16) << 2) | ((raw10_data[:, 4] >> 4) & 0x03) |
|
|
p3 = (raw10_data[:, 3].astype(np.uint16) << 2) | ((raw10_data[:, 4] >> 6) & 0x03) |
|
|
|
|
|
raw8_data = np.empty((n_blocks * 4 * 2,), dtype=np.uint8) |
|
|
raw8_data[0::8] = p0 & 0xFF |
|
|
raw8_data[1::8] = p0 >> 8 |
|
|
raw8_data[2::8] = p1 & 0xFF |
|
|
raw8_data[3::8] = p1 >> 8 |
|
|
raw8_data[4::8] = p2 & 0xFF |
|
|
raw8_data[5::8] = p2 >> 8 |
|
|
raw8_data[6::8] = p3 & 0xFF |
|
|
raw8_data[7::8] = p3 >> 8 |
|
|
|
|
|
return raw8_data.tobytes() |
|
|
|
|
|
def readRAW(path): |
|
|
filesize = os.path.getsize(path) |
|
|
|
|
|
with open(path, "rb") as f: |
|
|
raw_data = f.read() |
|
|
|
|
|
|
|
|
if filesize == 7372800: |
|
|
raw_data = mipi_raw10_to_raw8_scaled(raw_data) |
|
|
|
|
|
|
|
|
arr = np.frombuffer(raw_data, dtype=np.int16).reshape(96, 240, 256) |
|
|
|
|
|
|
|
|
reshaped = arr.reshape(*arr.shape[:-1], -1, 2) |
|
|
swapped = reshaped[..., ::-1] |
|
|
histogram_data = swapped.reshape(arr.shape) |
|
|
|
|
|
|
|
|
mapping = [0, 4, 1, 5, 2, 6, 3, 7] |
|
|
group_size = 8 |
|
|
num_groups = 12 |
|
|
output = np.empty_like(histogram_data) |
|
|
|
|
|
for g in range(num_groups): |
|
|
start = g * group_size |
|
|
end = start + group_size |
|
|
output[start:end, :, :] = histogram_data[start:end, :, :][mapping, :, :] |
|
|
|
|
|
return output |
|
|
|
|
|
def load_bin(file, threshold=3): |
|
|
raw_hist = readRAW(file.name) |
|
|
|
|
|
multishot = (raw_hist[..., 254] * 1024 + raw_hist[..., 255]).astype(np.float32) |
|
|
normalize_data = 12000 / multishot |
|
|
nor_hist = raw_hist * normalize_data[..., np.newaxis] |
|
|
|
|
|
img = np.sum(nor_hist[:, :, :-2], axis=2) |
|
|
norm_img = (img - img.min()) / (img.max() + 1e-8) |
|
|
img_uint8 = (norm_img * 255).astype(np.uint8) |
|
|
img_zoomed = np.repeat(np.repeat(img_uint8, 4, axis=0), 4, axis=1) |
|
|
|
|
|
depth_slider_imgs = plot_depth(nor_hist,threshold) |
|
|
|
|
|
return img_zoomed, raw_hist, nor_hist, depth_slider_imgs |
|
|
|
|
|
|
|
|
def plot_pixel_histogram(evt: gr.SelectData, raw_hist, nor_hist): |
|
|
|
|
|
x, y = evt.index |
|
|
x = x // 4 |
|
|
y = y // 4 |
|
|
raw_values = raw_hist[y, x, :] |
|
|
nor_values = nor_hist[y, x, :] |
|
|
|
|
|
|
|
|
|
|
|
fig = go.Figure() |
|
|
fig.add_trace(go.Scatter(y=raw_values, mode="lines+markers")) |
|
|
fig.update_layout( |
|
|
title=f"Pixel ({x}, {y}) 在所有 {raw_values.shape[0]} 帧的强度变化", |
|
|
xaxis_title="帧索引 (T)", |
|
|
yaxis_title="强度值", |
|
|
) |
|
|
return fig |
|
|
|
|
|
def to_uint8_image(arr): |
|
|
norm = (arr) / (np.max(arr) + 1e-8) |
|
|
return (norm * 255).astype(np.uint8) |
|
|
|
|
|
def plot_depth(norm_hist, threshold): |
|
|
|
|
|
|
|
|
|
|
|
tof = np.argmax(norm_hist[...,:-5], axis=2) |
|
|
img_tof = to_uint8_image(tof) |
|
|
|
|
|
|
|
|
|
|
|
noise = np.median(norm_hist[...,:8],axis=2) |
|
|
noise_th = noise + 4 * np.sqrt(noise) |
|
|
|
|
|
norm_hist_sub_noise = norm_hist - noise_th[...,np.newaxis] |
|
|
|
|
|
norm_hist_sub_noise[norm_hist_sub_noise<0]=0 |
|
|
|
|
|
norm_hist_pool = norm_hist_sub_noise[::10,::10,:] |
|
|
print(norm_hist_pool.shape) |
|
|
lst_scatter_th = [] |
|
|
for idx in range(0,256): |
|
|
map = norm_hist_pool[...,idx] |
|
|
ratio = 1/np.max(map) * 255 |
|
|
map_ratio = map * ratio |
|
|
_, otsu_thresh = cv2.threshold(map.astype(np.uint8), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_TRIANGLE) |
|
|
|
|
|
lst_scatter_th.append(_/ratio) |
|
|
|
|
|
de_scatter = norm_hist_sub_noise - np.array(lst_scatter_th)[np.newaxis, np.newaxis, :] |
|
|
de_scatter[de_scatter<0]=0 |
|
|
|
|
|
tof = np.argmax(de_scatter[...,:-5], axis=2) |
|
|
peak = np.max(de_scatter[...,:-5], axis=2) |
|
|
r = 4 |
|
|
neighbor_score = 16 |
|
|
snr_th = 0.1 |
|
|
for y in range(norm_hist.shape[0]): |
|
|
for x in range(norm_hist.shape[1]): |
|
|
t = tof[y,x] |
|
|
|
|
|
shift = np.argmax(norm_hist[y,x,max(0,t-r):min(255,t+r+1)]) -r |
|
|
t = t + shift |
|
|
cubic_S = de_scatter[max(0,y-1):min(norm_hist.shape[0],y+2), max(0,x-1):min(norm_hist.shape[1],x+2), max(0,t-1):min(255,t+2)] |
|
|
cubic_SN = norm_hist[max(0,y-1):min(norm_hist.shape[0],y+2), max(0,x-1):min(norm_hist.shape[1],x+2), max(0,t-1):min(255,t+2)] |
|
|
snr = cubic_S/np.sqrt(cubic_SN + 1e-6) |
|
|
|
|
|
mask = snr> snr_th |
|
|
if abs(shift) <= r and np.sum(mask) > neighbor_score: |
|
|
tof[y,x] = t |
|
|
else: |
|
|
tof[y,x] = 0 |
|
|
|
|
|
|
|
|
def get_value_at_depth_index(array_3d, depth_index): |
|
|
return array_3d[np.arange(array_3d.shape[0])[:, None], np.arange(array_3d.shape[1]), depth_index] |
|
|
|
|
|
C3 = get_value_at_depth_index(norm_hist,tof-1) |
|
|
C4 = get_value_at_depth_index(norm_hist,tof) |
|
|
C5 = get_value_at_depth_index(norm_hist,tof+1) |
|
|
|
|
|
|
|
|
shift_mat = (C5-C3)/(4.0 * C4 -2.0 * C3 - 2.0 * C5 + 1e-6) |
|
|
mask = abs(shift_mat) < 1 |
|
|
shift_mat = shift_mat * mask |
|
|
tof[tof<0]=0 |
|
|
|
|
|
img_filter = to_uint8_image(tof) |
|
|
|
|
|
colored_tof = cv2.applyColorMap(img_tof, cv2.COLORMAP_VIRIDIS)[:, :, ::-1] |
|
|
colored_tof_filter = cv2.applyColorMap(img_filter, cv2.COLORMAP_VIRIDIS)[:, :, ::-1] |
|
|
|
|
|
return [colored_tof, colored_tof_filter] |
|
|
|
|
|
|
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
gr.Markdown("## 上传 96×240×256 int16 `.bin/.raw` 文件,点击图像像素查看该像素的 256 帧直方图") |
|
|
|
|
|
file_input = gr.File(label="上传 .raw/.bin 文件", file_types=[".raw", ".bin"]) |
|
|
image_display = gr.Image(interactive=True, label="点击像素显示强度曲线") |
|
|
histogram = gr.Plot(label="像素强度曲线") |
|
|
depth_image_slider = ImageSlider(label="Filter Depth Map with Slider View", elem_id='img-display-output', position=0.5) |
|
|
threshold_slider = gr.Slider(1, 30, value=3, step=1, label="Mask 阈值设定 (ref > x)") |
|
|
|
|
|
raw_hist = gr.State() |
|
|
nor_hist = gr.State() |
|
|
|
|
|
|
|
|
file_input.change( |
|
|
load_bin, |
|
|
inputs=[file_input,threshold_slider], |
|
|
outputs=[image_display, raw_hist, nor_hist, depth_image_slider] |
|
|
) |
|
|
|
|
|
image_display.select( |
|
|
plot_pixel_histogram, |
|
|
inputs=[raw_hist, nor_hist], |
|
|
outputs=histogram |
|
|
) |
|
|
threshold_slider.change( |
|
|
load_bin, |
|
|
inputs=[file_input,threshold_slider], |
|
|
outputs=[image_display, raw_hist, nor_hist, depth_image_slider]) |
|
|
|
|
|
|
|
|
demo.launch() |
|
|
|