This commit is contained in:
parent
3006141cc0
commit
4f31b4d8fd
22
README.md
22
README.md
|
@ -71,11 +71,13 @@ Stream the default source to the display. On Linux Wayland, this pops up a scree
|
||||||
Usage: servicepoint-cli stream screen [OPTIONS]
|
Usage: servicepoint-cli stream screen [OPTIONS]
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-p, --pointer Show mouse pointer in video feed
|
-p, --pointer Show mouse pointer in video feed
|
||||||
--no-hist Disable histogram correction
|
--no-hist Disable histogram correction
|
||||||
--no-blur Disable blur
|
--no-blur Disable blur
|
||||||
--no-sharp Disable sharpening
|
--no-sharp Disable sharpening
|
||||||
--no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on.
|
--no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on.
|
||||||
|
--no-spacers Do not remove the spacers from the image.
|
||||||
|
--no-aspect Do not keep aspect ratio when resizing.
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Stdin
|
#### Stdin
|
||||||
|
@ -127,10 +129,12 @@ Arguments:
|
||||||
<FILE_NAME>
|
<FILE_NAME>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--no-hist Disable histogram correction
|
--no-hist Disable histogram correction
|
||||||
--no-blur Disable blur
|
--no-blur Disable blur
|
||||||
--no-sharp Disable sharpening
|
--no-sharp Disable sharpening
|
||||||
--no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on.
|
--no-dither Disable dithering. Brightness will be adjusted so that around half of the pixels are on.
|
||||||
|
--no-spacers Do not remove the spacers from the image.
|
||||||
|
--no-aspect Do not keep aspect ratio when resizing.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
|
@ -154,6 +154,9 @@ pub struct ImageProcessingOptions {
|
||||||
|
|
||||||
#[arg(long, help = "Do not remove the spacers from the image.")]
|
#[arg(long, help = "Do not remove the spacers from the image.")]
|
||||||
pub no_spacers: bool,
|
pub no_spacers: bool,
|
||||||
|
|
||||||
|
#[arg(long, help = "Do not keep aspect ratio when resizing.")]
|
||||||
|
pub no_aspect: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(clap::Parser, std::fmt::Debug, Clone)]
|
#[derive(clap::Parser, std::fmt::Debug, Clone)]
|
||||||
|
|
|
@ -12,7 +12,7 @@ use std::{default::Default, time::Instant};
|
||||||
pub struct ImageProcessingPipeline {
|
pub struct ImageProcessingPipeline {
|
||||||
options: ImageProcessingOptions,
|
options: ImageProcessingOptions,
|
||||||
resizer: Resizer,
|
resizer: Resizer,
|
||||||
render_size: (usize, usize),
|
render_size: (u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
const SPACER_HEIGHT: usize = TILE_SIZE / 2;
|
const SPACER_HEIGHT: usize = TILE_SIZE / 2;
|
||||||
|
@ -21,16 +21,17 @@ impl ImageProcessingPipeline {
|
||||||
pub fn new(options: ImageProcessingOptions) -> Self {
|
pub fn new(options: ImageProcessingOptions) -> Self {
|
||||||
debug!("Creating image pipeline: {:?}", options);
|
debug!("Creating image pipeline: {:?}", options);
|
||||||
|
|
||||||
let spacers_height = if options.no_spacers {
|
let height = PIXEL_HEIGHT
|
||||||
0
|
+ if options.no_spacers {
|
||||||
} else {
|
0
|
||||||
SPACER_HEIGHT * (TILE_HEIGHT - 1)
|
} else {
|
||||||
};
|
SPACER_HEIGHT * (TILE_HEIGHT - 1)
|
||||||
|
};
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
options,
|
options,
|
||||||
resizer: Resizer::new(),
|
resizer: Resizer::new(),
|
||||||
render_size: (PIXEL_WIDTH, PIXEL_HEIGHT + spacers_height),
|
render_size: (PIXEL_WIDTH as u32, height as u32),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +53,11 @@ impl ImageProcessingPipeline {
|
||||||
fn resize_grayscale(&mut self, frame: DynamicImage) -> GrayImage {
|
fn resize_grayscale(&mut self, frame: DynamicImage) -> GrayImage {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
let (scaled_width, scaled_height) = self.fit_size((frame.width(), frame.height()));
|
let (scaled_width, scaled_height) = if self.options.no_aspect {
|
||||||
|
self.render_size
|
||||||
|
} else {
|
||||||
|
self.calc_scaled_size_keep_aspect((frame.width(), frame.height()))
|
||||||
|
};
|
||||||
let mut dst_image = DynamicImage::new(scaled_width, scaled_height, frame.color());
|
let mut dst_image = DynamicImage::new(scaled_width, scaled_height, frame.color());
|
||||||
|
|
||||||
self.resizer
|
self.resizer
|
||||||
|
@ -106,20 +111,13 @@ impl ImageProcessingPipeline {
|
||||||
fn remove_spacers(source: Bitmap) -> Bitmap {
|
fn remove_spacers(source: Bitmap) -> Bitmap {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
let full_tile_rows_with_spacers = source.height() / (TILE_SIZE + SPACER_HEIGHT);
|
let width = source.width();
|
||||||
let remaining_pixel_rows = source.height() % (TILE_SIZE + SPACER_HEIGHT);
|
let result_height = Self::calc_height_without_spacers(source.height());
|
||||||
let total_spacer_height = full_tile_rows_with_spacers * SPACER_HEIGHT
|
let mut result = Bitmap::new(width, result_height);
|
||||||
+ remaining_pixel_rows.saturating_sub(TILE_SIZE);
|
|
||||||
let height_without_spacers = source.height() - total_spacer_height;
|
|
||||||
trace!(
|
|
||||||
"spacers take up {total_spacer_height}, resulting in height {height_without_spacers}"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut result = Bitmap::new(source.width(), height_without_spacers);
|
|
||||||
|
|
||||||
let mut source_y = 0;
|
let mut source_y = 0;
|
||||||
for result_y in 0..result.height() {
|
for result_y in 0..result_height {
|
||||||
for x in 0..result.width() {
|
for x in 0..width {
|
||||||
result.set(x, result_y, source.get(x, source_y));
|
result.set(x, result_y, source.get(x, source_y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,10 +131,22 @@ impl ImageProcessingPipeline {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fit_size(&self, source: (u32, u32)) -> (u32, u32) {
|
fn calc_height_without_spacers(height: usize) -> usize {
|
||||||
|
let full_tile_rows_with_spacers = height / (TILE_SIZE + SPACER_HEIGHT);
|
||||||
|
let remaining_pixel_rows = height % (TILE_SIZE + SPACER_HEIGHT);
|
||||||
|
let total_spacer_height = full_tile_rows_with_spacers * SPACER_HEIGHT
|
||||||
|
+ remaining_pixel_rows.saturating_sub(TILE_SIZE);
|
||||||
|
let height_without_spacers = height - total_spacer_height;
|
||||||
|
trace!(
|
||||||
|
"spacers take up {total_spacer_height}, resulting in final height {height_without_spacers}"
|
||||||
|
);
|
||||||
|
height_without_spacers
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calc_scaled_size_keep_aspect(&self, source: (u32, u32)) -> (u32, u32) {
|
||||||
let (source_width, source_height) = source;
|
let (source_width, source_height) = source;
|
||||||
let (target_width, target_height) = self.render_size;
|
let (target_width, target_height) = self.render_size;
|
||||||
debug_assert_eq!(target_width % TILE_SIZE, 0);
|
debug_assert_eq!(target_width % TILE_SIZE as u32, 0);
|
||||||
|
|
||||||
let width_scale = target_width as f32 / source_width as f32;
|
let width_scale = target_width as f32 / source_width as f32;
|
||||||
let height_scale = target_height as f32 / source_height as f32;
|
let height_scale = target_height as f32 / source_height as f32;
|
||||||
|
|
|
@ -215,7 +215,7 @@ fn ostromoukhov_dither_pixel(
|
||||||
) {
|
) {
|
||||||
let (destination_value, error) = gray_to_bit(source[position], bias);
|
let (destination_value, error) = gray_to_bit(source[position], bias);
|
||||||
destination.set(position, destination_value);
|
destination.set(position, destination_value);
|
||||||
|
|
||||||
let mut diffuse = |to: usize, mat: i16| {
|
let mut diffuse = |to: usize, mat: i16| {
|
||||||
let diffuse_value = source[to] as i16 + mat;
|
let diffuse_value = source[to] as i16 + mat;
|
||||||
source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8;
|
source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8;
|
||||||
|
|
Loading…
Reference in a new issue