extract image processing
This commit is contained in:
parent
117e6a8bf7
commit
19f24f9331
|
@ -122,6 +122,12 @@ pub struct StreamScreenOptions {
|
|||
)]
|
||||
pub pointer: bool,
|
||||
|
||||
#[transparent]
|
||||
pub image_processing: ImageProcessingOptions,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser, std::fmt::Debug, Clone)]
|
||||
pub struct ImageProcessingOptions {
|
||||
#[arg(long, help = "Disable histogram correction")]
|
||||
pub no_hist: bool,
|
||||
|
||||
|
|
66
src/image_processing.rs
Normal file
66
src/image_processing.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use crate::cli::ImageProcessingOptions;
|
||||
use crate::ledwand_dither::{
|
||||
blur, histogram_correction, median_brightness, ostromoukhov_dither, sharpen,
|
||||
};
|
||||
use image::imageops::{resize, FilterType};
|
||||
use image::{imageops, DynamicImage, ImageBuffer, Luma};
|
||||
use servicepoint::{Bitmap, PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
|
||||
pub struct ImageProcessingPipeline {
|
||||
options: ImageProcessingOptions,
|
||||
}
|
||||
|
||||
impl ImageProcessingPipeline {
|
||||
pub fn new(options: ImageProcessingOptions) -> Self {
|
||||
Self { options }
|
||||
}
|
||||
|
||||
pub fn process(&self, frame: DynamicImage) -> Bitmap {
|
||||
let frame = Self::resize_grayscale(&frame);
|
||||
let frame = self.grayscale_processing(frame);
|
||||
self.grayscale_to_bitmap(frame)
|
||||
}
|
||||
|
||||
fn resize_grayscale(frame: &DynamicImage) -> ImageBuffer<Luma<u8>, Vec<u8>> {
|
||||
let frame = imageops::grayscale(&frame);
|
||||
let frame = resize(
|
||||
&frame,
|
||||
PIXEL_WIDTH as u32,
|
||||
PIXEL_HEIGHT as u32,
|
||||
FilterType::Nearest,
|
||||
);
|
||||
frame
|
||||
}
|
||||
|
||||
fn grayscale_processing(
|
||||
&self,
|
||||
mut frame: ImageBuffer<Luma<u8>, Vec<u8>>,
|
||||
) -> ImageBuffer<Luma<u8>, Vec<u8>> {
|
||||
if !self.options.no_hist {
|
||||
histogram_correction(&mut frame);
|
||||
}
|
||||
|
||||
let mut orig = frame.clone();
|
||||
|
||||
if !self.options.no_blur {
|
||||
blur(&orig, &mut frame);
|
||||
std::mem::swap(&mut frame, &mut orig);
|
||||
}
|
||||
|
||||
if !self.options.no_sharp {
|
||||
sharpen(&orig, &mut frame);
|
||||
std::mem::swap(&mut frame, &mut orig);
|
||||
}
|
||||
orig
|
||||
}
|
||||
|
||||
fn grayscale_to_bitmap(&self, mut orig: ImageBuffer<Luma<u8>, Vec<u8>>) -> Bitmap {
|
||||
if self.options.no_dither {
|
||||
let cutoff = median_brightness(&orig);
|
||||
let bits = orig.iter().map(move |x| x > &cutoff).collect();
|
||||
Bitmap::from_bitvec(orig.width() as usize, bits)
|
||||
} else {
|
||||
ostromoukhov_dither(orig, u8::MAX / 2)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ use servicepoint::{Brightness, Connection};
|
|||
|
||||
mod brightness;
|
||||
mod cli;
|
||||
mod image_processing;
|
||||
mod ledwand_dither;
|
||||
mod pixels;
|
||||
mod stream_stdin;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use crate::cli::StreamScreenOptions;
|
||||
use crate::ledwand_dither::*;
|
||||
use crate::{
|
||||
cli::{ImageProcessingOptions, StreamScreenOptions},
|
||||
image_processing::ImageProcessingPipeline,
|
||||
ledwand_dither::*,
|
||||
};
|
||||
use image::{
|
||||
imageops::{resize, FilterType},
|
||||
DynamicImage, ImageBuffer, Luma, Rgb, Rgba,
|
||||
|
@ -10,45 +13,26 @@ use scap::{
|
|||
frame::convert_bgra_to_rgb,
|
||||
frame::Frame,
|
||||
};
|
||||
use servicepoint::{Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
use servicepoint::{
|
||||
Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH,
|
||||
TILE_HEIGHT, TILE_SIZE,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn stream_window(connection: &Connection, options: StreamScreenOptions) {
|
||||
info!("Starting capture with options: {:?}", options);
|
||||
|
||||
let capturer = match start_capture(&options) {
|
||||
Some(value) => value,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let pipeline = ImageProcessingPipeline::new(options.image_processing);
|
||||
|
||||
info!("now starting to stream images");
|
||||
loop {
|
||||
let mut frame = get_next_frame(&capturer);
|
||||
|
||||
if !options.no_hist {
|
||||
histogram_correction(&mut frame);
|
||||
}
|
||||
|
||||
let mut orig = frame.clone();
|
||||
|
||||
if !options.no_blur {
|
||||
blur(&orig, &mut frame);
|
||||
std::mem::swap(&mut frame, &mut orig);
|
||||
}
|
||||
|
||||
if !options.no_sharp {
|
||||
sharpen(&orig, &mut frame);
|
||||
std::mem::swap(&mut frame, &mut orig);
|
||||
}
|
||||
|
||||
let bitmap = if options.no_dither {
|
||||
let cutoff = median_brightness(&orig);
|
||||
let bits = orig.iter().map(move |x| x > &cutoff).collect();
|
||||
Bitmap::from_bitvec(orig.width() as usize, bits)
|
||||
} else {
|
||||
ostromoukhov_dither(orig, u8::MAX / 2)
|
||||
};
|
||||
|
||||
let frame = capturer.get_next_frame().expect("failed to capture frame");
|
||||
let frame = frame_to_image(frame);
|
||||
let bitmap = pipeline.process(frame);
|
||||
connection
|
||||
.send(Command::BitmapLinearWin(
|
||||
Origin::ZERO,
|
||||
|
@ -59,20 +43,6 @@ pub fn stream_window(connection: &Connection, options: StreamScreenOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
/// returns next frame from the capturer, resized and grayscale
|
||||
fn get_next_frame(capturer: &Capturer) -> ImageBuffer<Luma<u8>, Vec<u8>> {
|
||||
let frame = capturer.get_next_frame().expect("failed to capture frame");
|
||||
let frame = frame_to_image(frame);
|
||||
let frame = frame.grayscale().to_luma8();
|
||||
|
||||
resize(
|
||||
&frame,
|
||||
PIXEL_WIDTH as u32,
|
||||
PIXEL_HEIGHT as u32,
|
||||
FilterType::Nearest,
|
||||
)
|
||||
}
|
||||
|
||||
fn start_capture(options: &StreamScreenOptions) -> Option<Capturer> {
|
||||
if !scap::is_supported() {
|
||||
error!("platform not supported by scap");
|
||||
|
|
Loading…
Reference in a new issue