Skip to content

Commit

Permalink
Merge pull request #165 from braden6521/camera_io_update
Browse files Browse the repository at this point in the history
Camera IO update
  • Loading branch information
e10harvey authored Oct 9, 2024
2 parents 3fda296 + e6455e9 commit 90d7e13
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 15 deletions.
29 changes: 16 additions & 13 deletions example/camera_io/live_view_mono_Basler.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
"""
Example script that connects to and shows a live view from an
8 bit Basler monochrome camera.
"""Example script that connects to and shows a live view from a 12 bit Basler monochrome camera.
"""

import argparse
Expand All @@ -12,7 +9,7 @@

def main():
parser = argparse.ArgumentParser(
prog='run_and_save_images_Basler_color', description='Shows live view from Basler monochrome camera.'
prog='live_view_mono_Basler', description='Shows live view from Basler monochrome camera.'
)
parser.add_argument(
'camera_index',
Expand All @@ -21,24 +18,30 @@ def main():
help='Camera index (0-indexed in order of camera serial number) to run.',
)
parser.add_argument('--calibrate', action='store_true', help='calibrate camera exposure before capture')
parser.add_argument('-e', metavar='exposure', type=float, default=None, help='Camera exposure value')
parser.add_argument('-e', metavar='exposure', type=int, default=None, help='Camera exposure value')
parser.add_argument('-g', metavar='gain', type=int, default=None, help='Camera gain value')
args = parser.parse_args()

# Connect to camera
cam = ImageAcquisitionMono(args.camera_index, 'Mono12')

# Set exposure
if args.e is not None:
cam.exposure_time = args.e

# Set gain
if args.g is not None:
cam.gain = args.g

# Calibrate exposure and set frame rate
cam.frame_rate = 10
if args.calibrate:
cam.calibrate_exposure()

if args.e is not None:
cam.exposure_time = args.e
print('Exposure time range: ', cam.cap.ExposureTimeRaw.Min, '-', cam.cap.ExposureTimeRaw.Max)
print('Gain range: ', cam.cap.GainRaw.Min, '-', cam.cap.GainRaw.Max)

print('Exposure time:', cam.exposure_time)
print('Frame rate:', cam.frame_rate)
print('Gain:', cam.gain)
print('')
print('Exposure time set:', cam.exposure_time)
print('Gain set:', cam.gain)

# Show live view image
LiveView(cam, highlight_saturation=False)
Expand Down
52 changes: 52 additions & 0 deletions example/camera_io/run_and_save_images_Basler_mono.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Example program that captures images from a 12 bit monochrome Basler
camera and saves the images in TIFF format. Camera is run at minimum gain.
"""

import argparse
import imageio.v3 as imageio

from opencsp.common.lib.camera.ImageAcquisition_DCAM_mono import ImageAcquisition


def main():
parser = argparse.ArgumentParser(
prog='run_and_save_images_Basler_mono',
description='Captures N frames from a Basler camera. Saves images as 16 bit integers in TIFF format with filenames of the form: xx.tiff',
)
parser.add_argument(
'camera_index', type=int, help='Camera index (0-indexed in order of camera serial number) to run.'
)
parser.add_argument('num_images', type=int, help='Number of images to capture and save.')
parser.add_argument('--calibrate', action='store_true', help='Calibrate camera exposure before capture.')
parser.add_argument(
'-p',
'--prefix',
metavar='prefix',
default='',
type=str,
help='Image save prefix to be appended before the filename.',
)
args = parser.parse_args()

# Connect to camera
cam = ImageAcquisition(args.camera_index, 'Mono12')

# Calibrate exposure and set frame rate
if args.calibrate:
cam.calibrate_exposure()

print('Exposure time:', cam.exposure_time)
print('Gain:', cam.gain)

# Capture and save frames
for idx in range(args.num_images):
frame = cam.get_frame()
# Save by packing 12 bit image into 16 bit image
imageio.imwrite(f'{args.prefix}{idx:02d}.tiff', frame * 2**4)

# Close camera
cam.close()


if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions opencsp/common/lib/camera/ImageAcquisitionAbstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,14 @@ def _check_saturated(im):

# Checks that the minimum value is under-exposed
self.exposure_time = exposure_values[0]
lt.debug(f'Trying minimum exposure: {exposure_values[0]}')
im = self.get_frame()
if _check_saturated(im):
lt.error_and_raise(ValueError, 'Minimum exposure value is too high; image still saturated.')

# Checks that the maximum value is over-exposed
self.exposure_time = exposure_values[-1]
lt.debug(f'Trying maximum exposure: {exposure_values[-1]}')
im = self.get_frame()
if not _check_saturated(im):
lt.error_and_raise(ValueError, 'Maximum exposure value is too low; image not saturated.')
Expand Down
13 changes: 11 additions & 2 deletions opencsp/common/lib/camera/ImageAcquisition_DCAM_mono.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,13 @@ def __init__(self, instance: int = 0, pixel_format: str = 'Mono8'):
# Set exposure values to be stepped over when performing exposure calibration
shutter_min = self.cap.ExposureTimeRaw.Min
shutter_max = self.cap.ExposureTimeRaw.Max
self._shutter_cal_values = np.linspace(shutter_min, shutter_max, 2**13).astype(int)

self._shutter_cal_values: np.ndarray = np.linspace(shutter_min, shutter_max, 2**13).astype(int)

# Apply model-specific processing for Basler mono cameras
if devices[instance].GetModelName() == 'acA3088-16gm':
# If type acA3088-16gm, make sure shutter values are multiple of 25
self._shutter_cal_values = (self._shutter_cal_values.astype(float) / 25).astype(int) * 25

@classmethod
def _check_pypylon_version(cls):
Expand Down Expand Up @@ -104,7 +110,10 @@ def instance_matches(self, possible_matches: list[ImageAcquisitionAbstract]) ->
def get_frame(self) -> np.ndarray:
# Start frame capture
self.cap.StartGrabbingMax(1)
grabResult = self.cap.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
exposure_time_ms = self.cap.ExposureTimeRaw.Max / 1000 # exposure time, ms
# Grab the frame
# After 1.5 times the expected image acquisition time, throw a pylon.TimeoutHandling_ThrowException
grabResult = self.cap.RetrieveResult(int(exposure_time_ms * 1.5), pylon.TimeoutHandling_ThrowException)

# Access image data
if grabResult.GrabSucceeded():
Expand Down

0 comments on commit 90d7e13

Please sign in to comment.