Skip to content

Commit

Permalink
Clean up global shutter capture.
Browse files Browse the repository at this point in the history
  • Loading branch information
ebezzam committed Oct 3, 2023
1 parent 4dbeaf7 commit 30a5287
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# python scripts/measure/remote_capture.py -cn remote_capture_rpi_gs
# python scripts/measure/remote_capture.py -cn capture_rpi_gs
defaults:
- demo
- _self_
Expand Down
13 changes: 10 additions & 3 deletions configs/digicam.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# -- setting mask-to-sensor distance and mask pattern
# python scripts/hardware/config_digicam.py
# -- just setting mask-to-sensor distance
# python scripts/hardware/set_digicam_mask_distance.py
rpi:
username: null
hostname: null
Expand All @@ -17,7 +21,10 @@ center: [0, 0]


aperture:
center: [59,76]
shape: [19,26]
center: null
shape: null
# aperture:
# center: [59,76]
# shape: [19,26]

z: 4 # mask to sensor distance
z: 4 # mask to sensor distance (mm)
11 changes: 5 additions & 6 deletions lensless/hardware/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ def get_distro():
return f"{RELEASE_DATA['NAME']} {RELEASE_DATA['VERSION']}"


def set_mask_sensor_distance(distance, rpi_username, rpi_hostname, motor=1):
def set_mask_sensor_distance(
distance, rpi_username, rpi_hostname, motor=1, max_distance=16, timeout=5
):
"""
Set the distance between the mask and sensor.
Expand All @@ -115,13 +117,10 @@ def set_mask_sensor_distance(distance, rpi_username, rpi_hostname, motor=1):
Hostname of Raspberry Pi.
"""

MAX_DISTANCE = 16 # mm
timeout = 5

client = check_username_hostname(rpi_username, rpi_hostname)
assert motor in [0, 1]
assert distance >= 0, "Distance must be non-negative"
assert distance < MAX_DISTANCE, f"Distance must be less than {MAX_DISTANCE} mm"
assert distance <= max_distance, f"Distance must be less than {max_distance} mm"

# assumes that `StepperDriver` is in home directory
rpi_python = "python3"
Expand All @@ -130,7 +129,7 @@ def set_mask_sensor_distance(distance, rpi_username, rpi_hostname, motor=1):
# reset to zero
print("Resetting to zero distance...")
try:
command = f"{rpi_python} {script} {motor} REV {MAX_DISTANCE * 1000}"
command = f"{rpi_python} {script} {motor} REV {max_distance * 1000}"
_stdin, _stdout, _stderr = client.exec_command(command, timeout=timeout)
except socket.timeout: # socket.timeout
pass
Expand Down
22 changes: 21 additions & 1 deletion lensless/utils/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,27 @@ def load_image(
assert bayer
raw = rawpy.imread(fp)
img = raw.raw_image
# TODO : use raw.postprocess?
# # # TODO : use raw.postprocess?
# img = raw.postprocess(
# adjust_maximum_thr=0, # default 0.75
# no_auto_scale=False,
# # no_auto_scale=True,
# gamma=(1, 1),
# bright=1, # default 1
# exp_shift=1,
# no_auto_bright=True,
# # use_camera_wb=True,
# # use_auto_wb=False,
# # -- gives better balance for PSF measurement
# use_camera_wb=False,
# use_auto_wb=True, # default is False? f both use_camera_wb and use_auto_wb are True, then use_auto_wb has priority.
# )

# if red_gain is None or blue_gain is None:
# camera_wb = raw.camera_whitebalance
# red_gain = camera_wb[0]
# blue_gain = camera_wb[1]

ccm = raw.color_matrix[:, :3]
black_level = np.array(raw.black_level_per_channel[:3]).astype(np.float32)
elif "npy" in fp or "npz" in fp:
Expand Down
18 changes: 9 additions & 9 deletions scripts/hardware/config_digicam.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ def config_digicam(config):
else:
raise ValueError(f"Pattern {config.pattern} not supported")

# apply aperture
if config.aperture is not None:

aperture = np.zeros(shape, dtype=np.uint8)
top_left = np.array(config.aperture.center) - np.array(config.aperture.shape) // 2
bottom_right = top_left + np.array(config.aperture.shape)
aperture[:, top_left[0] : bottom_right[0], top_left[1] : bottom_right[1]] = 1
pattern = pattern * aperture

# save pattern
if not config.pattern.endswith(".npy") and config.save:
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
Expand All @@ -72,15 +81,6 @@ def config_digicam(config):
print("Pattern min : ", pattern.min())
print("Pattern max : ", pattern.max())

# apply aperture
if config.aperture is not None:

aperture = np.zeros(shape, dtype=np.uint8)
top_left = np.array(config.aperture.center) - np.array(config.aperture.shape) // 2
bottom_right = top_left + np.array(config.aperture.shape)
aperture[:, top_left[0] : bottom_right[0], top_left[1] : bottom_right[1]] = 1
pattern = pattern * aperture

assert pattern is not None

n_nonzero = np.count_nonzero(pattern)
Expand Down
16 changes: 16 additions & 0 deletions scripts/hardware/set_digicam_mask_distance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import hydra
from lensless.hardware.utils import set_mask_sensor_distance


@hydra.main(version_base=None, config_path="../../configs", config_name="digicam")
def config_digicam(config):

rpi_username = config.rpi.username
rpi_hostname = config.rpi.hostname

# set mask to sensor distance
set_mask_sensor_distance(config.z, rpi_username, rpi_hostname, max_distance=40)


if __name__ == "__main__":
config_digicam()
57 changes: 32 additions & 25 deletions scripts/measure/remote_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,33 +125,40 @@ def liveview(config):

# copy over DNG file
remotefile = f"~/{remote_fn}.dng"
localfile = f"{fn}.dng"
localfile = os.path.join(save, f"{fn}.dng")
print(f"\nCopying over picture as {localfile}...")
os.system('scp "%s@%s:%s" %s' % (username, hostname, remotefile, localfile))
raw = rawpy.imread(localfile)

# https://letmaik.github.io/rawpy/api/rawpy.Params.html#rawpy.Params
# https://www.libraw.org/docs/API-datastruct-eng.html
if nbits_out > 8:
# only 8 or 16 bit supported by postprocess
if nbits_out != 16:
print("casting to 16 bit...")
output_bps = 16
else:
if nbits_out != 8:
print("casting to 8 bit...")
output_bps = 8
img = raw.postprocess(
adjust_maximum_thr=0, # default 0.75
no_auto_scale=False,
gamma=(1, 1),
output_bps=output_bps,
bright=1, # default 1
exp_shift=1,
no_auto_bright=True,
use_camera_wb=True,
use_auto_wb=False, # default is False? f both use_camera_wb and use_auto_wb are True, then use_auto_wb has priority.
)

img = load_image(localfile, verbose=True, bayer=bayer, nbits_out=nbits_out)

# raw = rawpy.imread(localfile)

# # https://letmaik.github.io/rawpy/api/rawpy.Params.html#rawpy.Params
# # https://www.libraw.org/docs/API-datastruct-eng.html
# if nbits_out > 8:
# # only 8 or 16 bit supported by postprocess
# if nbits_out != 16:
# print("casting to 16 bit...")
# output_bps = 16
# else:
# if nbits_out != 8:
# print("casting to 8 bit...")
# output_bps = 8
# img = raw.postprocess(
# adjust_maximum_thr=0, # default 0.75
# no_auto_scale=False,
# # no_auto_scale=True,
# gamma=(1, 1),
# output_bps=output_bps,
# bright=1, # default 1
# exp_shift=1,
# no_auto_bright=True,
# # use_camera_wb=True,
# # use_auto_wb=False,
# # -- gives better balance for PSF measurement
# use_camera_wb=False,
# use_auto_wb=True, # default is False? f both use_camera_wb and use_auto_wb are True, then use_auto_wb has priority.
# )

# print image properties
print_image_info(img)
Expand Down
6 changes: 5 additions & 1 deletion scripts/recon/admm.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,13 @@ def admm(config):
else:
org_data = data
ax = plot_image(org_data, gamma=config["display"]["gamma"])
ax.set_title("Original measurement")
ax.set_title("Raw data")
plt.savefig(plib.Path(save) / "lensless.png")

# close axes
fig = plt.gcf()
plt.close(fig)

start_time = time.time()
if not config.admm.unrolled:
recon = ADMM(psf, **config.admm)
Expand Down

0 comments on commit 30a5287

Please sign in to comment.