Skip to content

Commit

Permalink
feat!: update image crate, use pixelutil-image, add thiserror crate, …
Browse files Browse the repository at this point in the history
…downscale image coordinate values from i64 to i32
  • Loading branch information
rostyq committed May 14, 2024
1 parent f172ebf commit 2fda7f1
Show file tree
Hide file tree
Showing 18 changed files with 116 additions and 209 deletions.
12 changes: 7 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,24 @@ bench = false

[dependencies]
nalgebra = { version = "0.32.1", default-features = false, features = [ "std" ] }
image = { version = "0.24.5", default-features = false }
imageproc = { version = "0.23.0", default-features = false }
image = { version = "0.25.1", default-features = false }
imageproc = { version = "0.24.0", default-features = false }
rand = "0.8.5"
similarity-least-squares = "0.2.0"
rand_xoshiro = "0.6.0"
derive_builder = "0.12.0"
pixelutil-image = "0.1"
thiserror = "1.0"

[dev-dependencies]
image = "0.24.5"
imageproc = "0.23.0"
image = "0.25.1"
imageproc = "0.24.0"
criterion = "0.4.0"
approx = "0.5.0"
rstest = "0.17.0"
clap = { version = "4.1.8", features = ["derive"] }
anyhow = "1.0"
rusttype = { version = "0.9.3", default-features = false }
ab_glyph = "0.2.23"

[dev-dependencies.cargo-husky]
version = "1.5.0"
Expand Down
6 changes: 1 addition & 5 deletions benches/multiscaler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ pub fn bench_run(c: &mut Criterion) {
group.sample_size(10000);

for size in SIZES.iter().map(|s| Size::from(*s)) {
let ms = Multiscaler::builder()
.min_size(100)
.max_size(size.height)
.build()
.unwrap();
let ms = Multiscaler::new(100, size.height, 0.1, 1.1).unwrap();

let id = BenchmarkId::from_parameter(size);

Expand Down
20 changes: 8 additions & 12 deletions examples/detect-faces/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,13 @@ impl Args {
pub fn init(&self, image: &DynamicImage) -> Result<(DetectMultiscale, LocalizePerturbate)> {
Ok((
DetectMultiscale::builder()
.multiscaler(
Multiscaler::builder()
.min_size(self.min_size)
.scale_factor(self.scale_factor)
.shift_factor(self.shift_factor)
.max_size(
self.max_size
.unwrap_or_else(|| image.height().min(image.width())),
)
.build()?,
)
.multiscaler(Multiscaler::new(
self.min_size,
self.max_size
.unwrap_or_else(|| image.height().min(image.width())),
self.scale_factor,
self.shift_factor,
)?)
.clusterizer(Clusterizer {
intersection_threshold: self.intersection_threshold,
score_threshold: self.score_threshold,
Expand All @@ -104,4 +100,4 @@ impl Args {

pub fn parse() -> Args {
Args::parse()
}
}
22 changes: 6 additions & 16 deletions examples/detect-faces/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ mod utils;
use rand::SeedableRng;
use rand_xoshiro::Xoroshiro128PlusPlus;

use anyhow::{anyhow, Context, Result};
use ab_glyph::FontRef;
use anyhow::{Context, Result};

use face::Face;
use shape::Shape5;
use utils::{draw_face, print_faces_data};

use rusttype::{Font, Scale};

fn main() -> Result<()> {
let args = args::parse();

let font = FontRef::try_from_slice(include_bytes!("../../assets/DejaVuSansDigits.ttf"))
.expect("Failed to load font.");

let image = image::open(&args.input).context("Failed to load image file.")?;

let (detector, localizer, shaper) = args.load_models()?;
Expand Down Expand Up @@ -59,23 +61,11 @@ fn main() -> Result<()> {

let mut rgb = image.into_rgb8();

let height = 12.0;
let scale = Scale {
x: height,
y: height,
};
let font = load_font()?;

for face in faces.iter() {
draw_face(&mut rgb, &face, &font, scale);
draw_face(&mut rgb, &face, &font, 12.0);
}

rgb.save(args.output).context("Cannot write output image")?;

Ok(())
}

fn load_font<'a>() -> Result<Font<'a>> {
Font::try_from_bytes(include_bytes!("../../assets/DejaVuSansDigits.ttf"))
.ok_or(anyhow!("Cannot load font."))
}
4 changes: 2 additions & 2 deletions examples/detect-faces/shape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ impl Shape5 {
let rh = rs / 2.0;

(
Square::new((l.x - lh) as i64, (l.y - lh) as i64, ls as u32),
Square::new((r.x - rh) as i64, (r.y - rh) as i64, rs as u32),
Square::new((l.x - lh) as i32, (l.y - lh) as i32, ls as u32),
Square::new((r.x - rh) as i32, (r.y - rh) as i32, rs as u32),
)
}
}
4 changes: 2 additions & 2 deletions examples/detect-faces/utils.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use ab_glyph::FontRef;
use image::{Rgb, RgbImage};
use imageproc::drawing;
use rusttype::{Font, Scale};

use crate::face::Face;

pub fn draw_face(image: &mut RgbImage, face: &Face, font: &Font, scale: Scale) {
pub fn draw_face(image: &mut RgbImage, face: &Face, font: &FontRef<'_>, scale: f32) {
drawing::draw_hollow_rect_mut(image, face.region.into(), Rgb([0, 0, 255]));

let color = Rgb([0, 255, 0]);
Expand Down
14 changes: 5 additions & 9 deletions examples/print-init-shape.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{fs::File, io::BufReader};
use std::path::PathBuf;

use anyhow::{anyhow, Context, Result};
use anyhow::{Context, Result};
use clap::Parser;
use image::{Rgb, RgbImage};
use imageproc::{
Expand All @@ -11,7 +11,8 @@ use imageproc::{
rect::Rect,
};
use pico_detect::Shaper;
use rusttype::{Font, Scale};
use ab_glyph::FontRef;


#[derive(Parser, Debug)]
#[command(author, version, about = "Print init points from shaper model.")]
Expand All @@ -25,6 +26,7 @@ struct Args {

fn main() -> Result<()> {
let args = Args::parse();
let font = FontRef::try_from_slice(include_bytes!("../assets/DejaVuSansDigits.ttf")).expect("Failed to load font.");
let file = BufReader::new(File::open(&args.model_path).context("Failed to open model file.")?);
let shaper = Shaper::load(file).context("Error during model loading.")?;

Expand All @@ -50,8 +52,7 @@ fn main() -> Result<()> {
let mut image = RgbImage::new(rect.width() + padding * 2, rect.height() + padding * 2);

let color = Rgb::from([0u8, 255u8, 0u8]);
let scale = Scale { x: 20.0, y: 20.0 };
let font = load_font()?;
let scale = 20.0;
let radius = 5;

for (i, point) in points.iter().enumerate() {
Expand All @@ -75,8 +76,3 @@ fn main() -> Result<()> {

Ok(())
}

fn load_font<'a>() -> Result<Font<'a>> {
Font::try_from_bytes(include_bytes!("../assets/DejaVuSansDigits.ttf"))
.ok_or(anyhow!("Cannot load font."))
}
8 changes: 4 additions & 4 deletions examples/run-pico-model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ struct Args {
image_path: PathBuf,

#[arg(long, default_value_t = 0)]
top: i64,
top: i32,

#[arg(long, default_value_t = 0)]
left: i64,
left: i32,

#[arg(long)]
size: Option<u32>,
Expand Down Expand Up @@ -60,14 +60,14 @@ fn main() -> Result<()> {
ModelType::Localizer => {
let localizer = Localizer::load(file)?;
let point = localizer.localize(&image, square.into());
println!("{},{}", point.x as i64, point.y as i64);
println!("{},{}", point.x as i32, point.y as i32);
}
ModelType::Shaper => {
let shaper = Shaper::load(file)?;
let shape = shaper.shape(&image, square.into());
println!("i,x,y");
for (i, point) in shape.iter().enumerate() {
println!("{},{},{}", i, point.x as i64, point.y as i64);
println!("{},{},{}", i, point.x as i32, point.y as i32);
}
}
}
Expand Down
74 changes: 37 additions & 37 deletions src/detect/multiscale.rs
Original file line number Diff line number Diff line change
@@ -1,51 +1,58 @@
use derive_builder::Builder;
use imageproc::rect::Rect;
use thiserror::Error;

use crate::geometry::Square;

#[derive(Copy, Clone, Debug, Builder, PartialEq)]
#[builder(build_fn(validate = "Multiscaler::validate"))]
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Multiscaler {
min_size: u32,
max_size: u32,
#[builder(default = "0.1")]
shift_factor: f32,
#[builder(default = "1.1")]
scale_factor: f32,
}

#[derive(Debug, Error)]
pub enum MultiscalerError {
#[error("`min_size` should be non zero")]
MinSizeIsZero,
#[error("`max_size` should be greater than `min_size`")]
MaxSizeLessThanMinSize,
#[error("`shift_factor` should be in `(0, 1]` range")]
ShiftFactorOutOfRange,
#[error("`scale_factor` should be greater than 1")]
ScaleFactorLessThanOne,
}

impl Multiscaler {
#[inline]
pub fn validate(builder: &MultiscalerBuilder) -> Result<(), String> {
if let Some(value) = builder.min_size {
if value == 0 {
return Err("`min_size` should be non zero".into());
}
pub fn new(
min_size: u32,
max_size: u32,
shift_factor: f32,
scale_factor: f32,
) -> Result<Self, MultiscalerError> {
if min_size == 0 {
return Err(MultiscalerError::MinSizeIsZero);
}

if let Some((min_size, max_size)) = builder.min_size.zip(builder.max_size) {
if min_size > max_size {
return Err("`max_size` should be greater than `min_size`".into());
}
if min_size > max_size {
return Err(MultiscalerError::MaxSizeLessThanMinSize);
}

if let Some(value) = builder.shift_factor {
if !(0.0..=1.0).contains(&value) {
return Err("`shift_factor` should be in `(0, 1]` range".into());
}
if !(0.0..=1.0).contains(&shift_factor) {
return Err(MultiscalerError::ShiftFactorOutOfRange);
}

if let Some(value) = builder.scale_factor {
if value < 1.0 {
return Err("`scale_factor` should be greater than `1.0`".into());
}
};

Ok(())
}
if scale_factor < 1.0 {
return Err(MultiscalerError::ScaleFactorLessThanOne);
}

pub fn builder() -> MultiscalerBuilder {
Default::default()
Ok(Self {
min_size,
max_size,
shift_factor,
scale_factor,
})
}

pub fn min_size(&self) -> u32 {
Expand Down Expand Up @@ -122,7 +129,7 @@ pub fn multiscale<F>(

for y in (start_y..=end_y).step_by(step) {
for x in (start_x..=end_x).step_by(step) {
f(Square::new(x as i64, y as i64, size))
f(Square::new(x, y, size))
}
}
size = (sizef * scale_factor) as u32;
Expand All @@ -135,14 +142,7 @@ mod tests {

#[test]
fn test_multiscale_run() {
let ms = Multiscaler::builder()
.min_size(1)
.max_size(4)
.scale_factor(2.0)
.shift_factor(1.0)
.build()
.unwrap();

let ms = Multiscaler::new(1, 4, 1.0, 2.0).unwrap();
ms.run(Rect::at(0, 0).of_size(4, 4), |s| println!("{:?}", s));
}
}
18 changes: 9 additions & 9 deletions src/geometry/square.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use crate::traits::region::Region;

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Square {
pub(crate) left: i64,
pub(crate) top: i64,
pub(crate) left: i32,
pub(crate) top: i32,
pub(crate) size: u32,
}

impl Square {
#[inline]
pub fn at(x: i64, y: i64) -> Self {
pub fn at(x: i32, y: i32) -> Self {
Self {
left: x,
top: y,
Expand Down Expand Up @@ -39,7 +39,7 @@ impl Square {
}

#[inline]
pub fn new(left: i64, top: i64, size: u32) -> Self {
pub fn new(left: i32, top: i32, size: u32) -> Self {
Self { left, top, size }
}

Expand All @@ -51,12 +51,12 @@ impl Square {

impl Region for Square {
#[inline]
fn left(&self) -> i64 {
fn left(&self) -> i32 {
self.left
}

#[inline]
fn top(&self) -> i64 {
fn top(&self) -> i32 {
self.top
}

Expand All @@ -76,14 +76,14 @@ impl Region for Square {
}
}

impl From<(i64, i64, u32)> for Square {
fn from(value: (i64, i64, u32)) -> Self {
impl From<(i32, i32, u32)> for Square {
fn from(value: (i32, i32, u32)) -> Self {
Self::new(value.0, value.1, value.2)
}
}

impl From<Square> for Rect {
fn from(value: Square) -> Self {
Self::at(value.left as i32, value.top as i32).of_size(value.size, value.size)
Self::at(value.left, value.top).of_size(value.size, value.size)
}
}
Loading

0 comments on commit 2fda7f1

Please sign in to comment.