You will need libcamera. And current 0.4 is quite "raw". Following is the code I am using:
First, make sure of the camera id.
pub async fn camera_scan() -> Result<()> {
let cam_mgr = CameraManager::new()?;
let cams = cam_mgr.cameras();
for i in 0..cams.len() {
let cam = cams
.get(i)
.ok_or(eyre!("Cam {i} is gone when fetching from list"))?;
println!("{}\t{}", i, cam.id());
println!("{:?}", cam.properties());
let cfgs = cam
.generate_configuration(&[StreamRole::StillCapture])
.ok_or(eyre!("Cannot get configuration from camera {}", cam.id()))?;
for j in 0..cfgs.len() {
println!("{i}.{j} available formats:");
println!(
"{:?}",
cfgs.get(j)
.ok_or(eyre!(
"Cam {i} configration {j} is gone when fetching from list"
))?
.formats()
);
}
}
Ok(())
}
Next taking the pics is quite tricky. I need an async env, hence following may contain "seems unnecessary" stuff:
let shutter_button = Arc::new((Mutex::new(false), Condvar::new()));
let (photo_tx, mut photo_rx) = mpsc::channel(4);
let trigger = shutter_button.clone();
let x = camera_id.to_owned();
spawn_blocking(move || {
if let Err(e) = camera_thread(&x, trigger, photo_tx) {
tracing::error!("{e:?}");
}
})
.await?;
let (_, v) = &*shutter_button;
v.notify_one();
let photo_request_time = Utc::now();
let image = photo_rx
.recv()
.await
.ok_or(eyre!("Response channel closed"))?;
The image above is a DynamicImage from image crate. You can directly save it as some format or in my case, handed to OpenCV.
fn camera_thread(
camera_id: &str,
shutter_button: Arc<(Mutex<bool>, Condvar)>,
photo_tx: Sender<DynamicImage>,
) -> Result<()> {
let cam_mgr = CameraManager::new()?;
let cams = cam_mgr.cameras();
let mut chosen = None;
for i in 0..cams.len() {
let cam = cams
.get(i)
.ok_or(eyre!("Cam {i} is gone when fetching from list"))?;
if cam.id() == camera_id {
chosen = Some(cam);
break;
}
}
let cam = chosen.ok_or(eyre!("Could not find specified camera"))?;
let mut cam = cam.acquire()?;
let (tx, rx) = channel::<Request>();
cam.on_request_completed(move |req| {
if let Err(e) = tx.send(req) {
tracing::warn!("Camera thread internal channel error: {e:?}");
}
});
let mut cfgs = cam
.generate_configuration(&[StreamRole::StillCapture])
.ok_or(eyre!("Cannot get configuration from camera {}", cam.id()))?;
let mut cfg = cfgs.get_mut(0).unwrap();
cfg.set_pixel_format(PIXEL_FORMAT_RGB888);
let max_size = cfg.formats().range(PIXEL_FORMAT_RGB888).max;
cfg.set_size(max_size);
match cfgs.validate() {
CameraConfigurationStatus::Valid => (),
CameraConfigurationStatus::Adjusted => {
// What should I do about this?
tracing::warn!("Configuration was adjusted.")
}
CameraConfigurationStatus::Invalid => Err(eyre!("Invalid camera configuration."))?,
}
cam.configure(&mut cfgs)?;
let x: f64 = <f32 as Into<f64>>::into(EATING_CAT_THRESHOLD)
* <u32 as Into<f64>>::into(max_size.height)
* <u32 as Into<f64>>::into(max_size.width);
MIN_CAT_SIZE
.set(x.round() as u64)
.map_err(|_| eyre!("Could not init MIN_CAT_SIZE"))?;
let mut alloc = FrameBufferAllocator::new(&cam);
let cfg = cfgs.get(0).unwrap();
let stream = cfg.stream().unwrap();
// The buffers len alloced is actually 1. Maybe related to `role`?
let fb = alloc
.alloc(&stream)?
.pop()
.ok_or(eyre!("Could not allocate even one framebuffer"))?;
let fb = MemoryMappedFrameBuffer::new(fb)?;
let mut req = cam
.create_request(None)
.ok_or(eyre!("Cannot create request"))?;
req.add_buffer(&stream, fb)?;
// The first photo status is FrameStartup, sleeping 1 sec did not help.
// So take a photo and drop it.
cam.start(None)?;
cam.queue_request(req)?;
req = rx.recv_timeout(Duration::from_secs(2))?;
cam.stop()?;
loop {
// Wait until the trigger
let (l, v) = &*shutter_button;
let _not_care = l
.lock()
.and_then(|l| v.wait(l))
.map_err(|e| eyre!("{e:?}"))?;
req.reuse(ReuseFlag::REUSE_BUFFERS);
cam.start(None)?;
cam.queue_request(req)?;
req = rx.recv_timeout(Duration::from_secs(2))?;
cam.stop()?;
let fb: &MemoryMappedFrameBuffer<FrameBuffer> =
req.buffer(&stream).expect("Unmatched buffer types");
let plane = fb.data().pop().ok_or(eyre!("No photo got"))?; // only 1 plane.
let metadata = fb.metadata().ok_or(eyre!("No metadata got"))?;
if metadata.status() == FrameMetadataStatus::Success {
// let data_length = metadata
// .planes()
// .get(0)
// .ok_or(eyre!("No metadata planes"))?
// .bytes_used;
// tracing::debug!("{} x {} = {data_length}", max_size.height, max_size.width);
let image = ImageBuffer::from_vec(max_size.width, max_size.height, plane.to_vec())
.ok_or(eyre!("Cannot create image, input not big enough"))?;
let image = DynamicImage::ImageRgb8(image);
photo_tx.blocking_send(image)?;
}
}
}