Rustでカメラから取得した画像に対して画像処理をする
概要
- 最近勉強がてらRustを触り初めて、Rust * 画像処理はまだまだ日本語資料が少ないので防備録がてら残しておく
- pngとかの画像から読み込んで画像処理しているのは結構ヒットするんだけど、カメラ入力のものがあんまりヒットしないので
- やったことはopencvのhello world程度なので悪しからず
- カメラの入力画像を取ってきて
- Image crateにconvertして
- 各pixel操作の画像処理としてgray scale化
- 画像全体への操作として二値化
- https://github.com/scepter914/camera-image-processing-template 実装とかはここに置いた
- 得られた知見としては、画像サイズが小さくなるとimage::RgbImage::from_vec よりもimage::RgbImage::from_rawを使った方が高速
実装
使ったlibraryについて
カメラ入力
let device = "/dev/video0";
let mut camera = rscam::new(device).unwrap();
let width = 1920;
let height = 1080;
let fps = 50;
// let width = 640;
// let height = 360;
// let fps = 330;
camera
.start(&rscam::Config {
interval: (1, fps),
resolution: (width, height),
format: b"RGB3",
..Default::default()
})
.unwrap();
画像処理部分
- お馴染みのloopで回すところ
- captureでカメラのframeを取ってきて
- image::RgbImageに突っ込んで
- 画像処理する
- っていうだけ
loop {
let frame = camera.capture().unwrap();
let rgb_image = image::RgbImage::from_vec(width, height, (&frame[..]).to_vec()).unwrap();
let gray_image = rgb_to_gray(&rgb_image);
let otsu_level = imageproc::contrast::otsu_level(&gray_image);
let binarized_image = imageproc::contrast::threshold(&gray_image, otsu_level);
}
画像をとってくる
- パッとdocumentを眺めた感じ image::RgbImage::from_vec と image::RgbImage::from_rawが使えそう
let rgb_image_raw = image::RgbImage::from_raw(width, height, (&frame[..]).to_vec()).unwrap();
let rgb_image_vec = image::RgbImage::from_vec(width, height, (&frame[..]).to_vec()).unwrap();
- 気になったのでベンチマークを取ってみた(結果はまとめて下に)
println!("from_raw");
let bench = Benchmark::set_start_time();
let rgb_image_raw = image::RgbImage::from_raw(width, height, (&frame[..]).to_vec()).unwrap();
bench.print_bench_time();
rgb_image_raw.save("data/from_raw.png").unwrap();
println!("from_vec");
let bench = Benchmark::set_start_time();
let rgb_image_vec = image::RgbImage::from_vec(width, height, (&frame[..]).to_vec()).unwrap();
bench.print_bench_time();
rgb_image_vec.save("data/from_vec.png").unwrap();
グレースケール化
fn rgb_to_gray(rgb_image: &image::RgbImage) -> image::GrayImage {
let width = rgb_image.width();
let height = rgb_image.height();
let mut gray_image = image::GrayImage::new(width, height);
// gray scale
for i in 0..width {
for j in 0..height {
let pixel = rgb_image.get_pixel(i, j);
let gray_pixel = [((pixel[0] as f32 * 0.2126) as u32
+ (pixel[1] as f32 * 0.7152) as u32
+ (pixel[2] as f32 * 0.0722) as u32) as u8; 1];
gray_image.put_pixel(i, j, image::Luma(gray_pixel));
}
}
return gray_image;
}
二値化
let otsu_level = imageproc::contrast::otsu_level(&gray_image);
let binarized_image = imageproc::contrast::threshold(&gray_image, otsu_level);
結果
- 入力画像
- 因みにこういった簡単な試験の時に、100均とかで買える単色シールは色チェックに使えて便利です
- ベンチマークの結果
- 画像サイズが小さくなるとimage::RgbImage::from_vec よりもimage::RgbImage::from_rawを使った方が高速なことが分かる
Camera /dev/video0: 1920 * 1080, 50 FPS
capture
Process 5.379 msec
from_raw
Process 0.591 msec
from_vec
Process 1.562 msec
rgb to gray
Process 12.432 msec
otsu binarize
Process 4.265 msec
Camera /dev/video0: 640 * 360, 330 FPS
capture
Process 1.002 msec
from_raw
Process 0.130 msec
from_vec
Process 0.058 msec
rgb to gray
Process 0.775 msec
otsu binarize
Process 0.194 msec
感想
- rust初心者なので間違っているところ等あったらgithub, twitter等で教えていただければ幸いです
- 結構ひいこら言いながらrustを実装してましたが、割と楽しいので今後も少しずつやっていこうと思います(続くといいなぁ)