This commit is contained in:
parent
32f148bfef
commit
f566f0959b
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -1629,9 +1629,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "servicepoint"
|
||||
version = "0.13.0"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8bc9e40503ece07e3f12232f648484191323b8126e74abce3d1644ba04dbd0"
|
||||
checksum = "33abd53582a995aaf5d387be4a1f7eb294a084185f88f8cf61652b6272041660"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"log",
|
||||
|
|
|
@ -13,7 +13,7 @@ homepage = "https://crates.io/crates/servicepoint-cli"
|
|||
keywords = ["cccb", "cccb-servicepoint", "cli"]
|
||||
|
||||
[dependencies]
|
||||
servicepoint = { version = "0.13.0", features = ["protocol_websocket"] }
|
||||
servicepoint = { version = "0.13.2", features = ["protocol_websocket"] }
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
env_logger = "0.11"
|
||||
log = "0.4"
|
||||
|
|
|
@ -114,14 +114,6 @@ pub enum StreamCommand {
|
|||
|
||||
#[derive(clap::Parser, std::fmt::Debug, Clone)]
|
||||
pub struct StreamScreenOptions {
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
default_value_t = false,
|
||||
help = "Disable dithering - improves performance"
|
||||
)]
|
||||
pub no_dither: bool,
|
||||
|
||||
#[arg(
|
||||
long,
|
||||
short,
|
||||
|
|
|
@ -1,17 +1,7 @@
|
|||
//! Based on https://github.com/WarkerAnhaltRanger/CCCB_Ledwand
|
||||
|
||||
use image::{GenericImage, GrayImage};
|
||||
use servicepoint::{PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
|
||||
pub struct LedwandDither {
|
||||
options: LedwandDitherOptions,
|
||||
tmpbuf: GrayImage,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct LedwandDitherOptions {
|
||||
pub size: Option<(u32, u32)>,
|
||||
}
|
||||
use image::GrayImage;
|
||||
use servicepoint::{BitVec, Bitmap, PIXEL_HEIGHT};
|
||||
|
||||
type GrayHistogram = [usize; 256];
|
||||
|
||||
|
@ -21,164 +11,495 @@ struct HistogramCorrection {
|
|||
factor: f32,
|
||||
}
|
||||
|
||||
impl LedwandDither {
|
||||
pub fn new(options: LedwandDitherOptions) -> Self {
|
||||
let (width, height) = options
|
||||
.size
|
||||
.unwrap_or((PIXEL_WIDTH as u32, PIXEL_HEIGHT as u32));
|
||||
Self {
|
||||
tmpbuf: GrayImage::new(width, height),
|
||||
options,
|
||||
pub fn histogram_correction(image: &mut GrayImage) {
|
||||
let histogram = make_histogram(image);
|
||||
let correction = determine_histogram_correction(image, histogram);
|
||||
apply_histogram_correction(image, correction)
|
||||
}
|
||||
|
||||
fn make_histogram(image: &GrayImage) -> GrayHistogram {
|
||||
let mut histogram = [0; 256];
|
||||
for pixel in image.pixels() {
|
||||
histogram[pixel.0[0] as usize] += 1;
|
||||
}
|
||||
histogram
|
||||
}
|
||||
|
||||
fn determine_histogram_correction(
|
||||
image: &GrayImage,
|
||||
histogram: GrayHistogram,
|
||||
) -> HistogramCorrection {
|
||||
let adjustment_pixels = image.len() / PIXEL_HEIGHT;
|
||||
|
||||
let mut num_pixels = 0;
|
||||
let mut brightness = 0;
|
||||
|
||||
let mincut = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness += 1;
|
||||
if num_pixels >= adjustment_pixels {
|
||||
break u8::min(brightness, 20);
|
||||
}
|
||||
};
|
||||
|
||||
let minshift = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness += 1;
|
||||
if num_pixels >= 2 * adjustment_pixels {
|
||||
break u8::min(brightness, 64);
|
||||
}
|
||||
};
|
||||
|
||||
brightness = u8::MAX;
|
||||
num_pixels = 0;
|
||||
let maxshift = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness -= 1;
|
||||
if num_pixels >= 2 * adjustment_pixels {
|
||||
break u8::max(brightness, 192);
|
||||
}
|
||||
};
|
||||
|
||||
let pre_offset = -(mincut as f32 / 2.);
|
||||
let post_offset = -(minshift as f32);
|
||||
let factor = (255.0 - post_offset) / maxshift as f32;
|
||||
HistogramCorrection {
|
||||
pre_offset,
|
||||
post_offset,
|
||||
factor,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) {
|
||||
for pixel in image.pixels_mut() {
|
||||
let pixel = &mut pixel.0[0];
|
||||
let value =
|
||||
(*pixel as f32 + correction.pre_offset) * correction.factor + correction.post_offset;
|
||||
*pixel = value.clamp(0f32, u8::MAX as f32) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn median_brightness(image: &GrayImage) -> u8 {
|
||||
let histogram = make_histogram(image);
|
||||
let midpoint = image.len() / 2;
|
||||
|
||||
debug_assert_eq!(
|
||||
image.len(),
|
||||
histogram.iter().copied().map(usize::from).sum()
|
||||
);
|
||||
|
||||
let mut num_pixels = 0;
|
||||
for brightness in u8::MIN..=u8::MAX {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
if num_pixels >= midpoint {
|
||||
return brightness;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn histogram_correction(image: &mut GrayImage) {
|
||||
let histogram = Self::make_histogram(image);
|
||||
let correction = Self::determine_histogram_correction(image, histogram);
|
||||
Self::apply_histogram_correction(image, correction)
|
||||
unreachable!("Somehow less pixels where counted in the histogram than exist in the image")
|
||||
}
|
||||
|
||||
pub fn blur(source: &GrayImage, destination: &mut GrayImage) {
|
||||
assert_eq!(source.len(), destination.len());
|
||||
|
||||
copy_border(source, destination);
|
||||
blur_inner_pixels(source, destination);
|
||||
}
|
||||
|
||||
pub fn sharpen(source: &GrayImage, destination: &mut GrayImage) {
|
||||
assert_eq!(source.len(), destination.len());
|
||||
|
||||
copy_border(source, destination);
|
||||
sharpen_inner_pixels(source, destination);
|
||||
}
|
||||
|
||||
fn copy_border(source: &GrayImage, destination: &mut GrayImage) {
|
||||
let last_row = source.height() - 1;
|
||||
for x in 0..source.width() {
|
||||
destination[(x, 0)] = source[(x, 0)];
|
||||
destination[(x, last_row)] = source[(x, last_row)];
|
||||
}
|
||||
|
||||
fn make_histogram(image: &GrayImage) -> GrayHistogram {
|
||||
let mut histogram = [0; 256];
|
||||
for pixel in image.pixels() {
|
||||
histogram[pixel.0[0] as usize] += 1;
|
||||
}
|
||||
histogram
|
||||
let last_col = source.width() - 1;
|
||||
for y in 0..source.height() {
|
||||
destination[(0, y)] = source[(0, y)];
|
||||
destination[(last_col, y)] = source[(last_col, y)];
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_histogram_correction(
|
||||
image: &GrayImage,
|
||||
histogram: GrayHistogram,
|
||||
) -> HistogramCorrection {
|
||||
let adjustment_pixels = image.len() / PIXEL_HEIGHT;
|
||||
|
||||
let mut num_pixels = 0;
|
||||
let mut brightness = 0;
|
||||
|
||||
let mincut = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness += 1;
|
||||
if num_pixels >= adjustment_pixels {
|
||||
break u8::min(brightness, 20);
|
||||
}
|
||||
};
|
||||
|
||||
let minshift = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness += 1;
|
||||
if num_pixels >= 2 * adjustment_pixels {
|
||||
break u8::min(brightness, 64);
|
||||
}
|
||||
};
|
||||
|
||||
brightness = u8::MAX;
|
||||
num_pixels = 0;
|
||||
let maxshift = loop {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
brightness -= 1;
|
||||
if num_pixels >= 2 * adjustment_pixels {
|
||||
break u8::max(brightness, 192);
|
||||
}
|
||||
};
|
||||
|
||||
let pre_offset = -(mincut as f32 / 2.);
|
||||
let post_offset = -(minshift as f32);
|
||||
let factor = (255.0 - post_offset) / maxshift as f32;
|
||||
HistogramCorrection {
|
||||
pre_offset,
|
||||
post_offset,
|
||||
factor,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_histogram_correction(image: &mut GrayImage, correction: HistogramCorrection) {
|
||||
for pixel in image.pixels_mut() {
|
||||
let pixel = &mut pixel.0[0];
|
||||
let value = (*pixel as f32 + correction.pre_offset) * correction.factor
|
||||
+ correction.post_offset;
|
||||
*pixel = value.clamp(0f32, u8::MAX as f32) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn median_brightness(image: &GrayImage) -> u8 {
|
||||
let histogram = Self::make_histogram(image);
|
||||
let midpoint = image.len() / 2;
|
||||
|
||||
debug_assert_eq!(
|
||||
image.len(),
|
||||
histogram.iter().copied().map(usize::from).sum()
|
||||
);
|
||||
|
||||
let mut num_pixels = 0;
|
||||
for brightness in u8::MIN..=u8::MAX {
|
||||
num_pixels += histogram[brightness as usize];
|
||||
if num_pixels >= midpoint {
|
||||
return brightness;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!("Somehow less pixels where counted in the histogram than exist in the image")
|
||||
}
|
||||
|
||||
pub fn blur(source: &GrayImage, destination: &mut GrayImage) {
|
||||
assert_eq!(source.len(), destination.len());
|
||||
|
||||
Self::copy_border(source, destination);
|
||||
Self::blur_inner_pixels(source, destination);
|
||||
}
|
||||
|
||||
pub fn sharpen(source: &GrayImage, destination: &mut GrayImage) {
|
||||
assert_eq!(source.len(), destination.len());
|
||||
|
||||
Self::copy_border(source, destination);
|
||||
Self::sharpen_inner_pixels(source, destination);
|
||||
}
|
||||
|
||||
fn copy_border(source: &GrayImage, destination: &mut GrayImage) {
|
||||
let last_row = source.height() -1;
|
||||
for x in 0..source.width() {
|
||||
destination[(x, 0)] = source[(x, 0)];
|
||||
destination[(x, last_row)] = source[(x, last_row)];
|
||||
}
|
||||
let last_col = source.width() - 1;
|
||||
for y in 0..source.height() {
|
||||
destination[(0, y)] = source[(0, y)];
|
||||
destination[(last_col, y)] = source[(last_col, y)];
|
||||
}
|
||||
}
|
||||
|
||||
fn blur_inner_pixels(source: &GrayImage, destination: &mut GrayImage) {
|
||||
for y in 1..source.height() - 2 {
|
||||
for x in 1..source.width() - 2 {
|
||||
let weighted_sum = source.get_pixel(x - 1, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x - 1, y).0[0] as u32
|
||||
+ 8 * source.get_pixel(x, y).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y).0[0] as u32
|
||||
+ source.get_pixel(x - 1, y + 1).0[0] as u32
|
||||
+ source.get_pixel(x, y + 1).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y + 1).0[0] as u32;
|
||||
let blurred = weighted_sum / 16;
|
||||
destination.get_pixel_mut(x, y).0[0] = blurred.clamp(u8::MIN as u32, u8::MAX as u32) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sharpen_inner_pixels(source: &GrayImage, destination: &mut GrayImage) {
|
||||
for y in 1..source.height() - 2 {
|
||||
for x in 1..source.width() - 2 {
|
||||
let weighted_sum = -(source.get_pixel(x - 1, y - 1).0[0] as i32)
|
||||
- source.get_pixel(x, y - 1).0[0] as i32
|
||||
- source.get_pixel(x + 1, y - 1).0[0] as i32
|
||||
- source.get_pixel(x - 1, y).0[0] as i32
|
||||
+ 9 * source.get_pixel(x, y).0[0] as i32
|
||||
- source.get_pixel(x + 1, y).0[0] as i32
|
||||
- source.get_pixel(x - 1, y + 1).0[0] as i32
|
||||
- source.get_pixel(x, y + 1).0[0] as i32
|
||||
- source.get_pixel(x + 1, y + 1).0[0] as i32;
|
||||
destination.get_pixel_mut(x, y).0[0] = weighted_sum.clamp(u8::MIN as i32, u8::MAX as i32) as u8;
|
||||
}
|
||||
fn blur_inner_pixels(source: &GrayImage, destination: &mut GrayImage) {
|
||||
for y in 1..source.height() - 2 {
|
||||
for x in 1..source.width() - 2 {
|
||||
let weighted_sum = source.get_pixel(x - 1, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y - 1).0[0] as u32
|
||||
+ source.get_pixel(x - 1, y).0[0] as u32
|
||||
+ 8 * source.get_pixel(x, y).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y).0[0] as u32
|
||||
+ source.get_pixel(x - 1, y + 1).0[0] as u32
|
||||
+ source.get_pixel(x, y + 1).0[0] as u32
|
||||
+ source.get_pixel(x + 1, y + 1).0[0] as u32;
|
||||
let blurred = weighted_sum / 16;
|
||||
destination.get_pixel_mut(x, y).0[0] =
|
||||
blurred.clamp(u8::MIN as u32, u8::MAX as u32) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sharpen_inner_pixels(source: &GrayImage, destination: &mut GrayImage) {
|
||||
for y in 1..source.height() - 2 {
|
||||
for x in 1..source.width() - 2 {
|
||||
let weighted_sum = -(source.get_pixel(x - 1, y - 1).0[0] as i32)
|
||||
- source.get_pixel(x, y - 1).0[0] as i32
|
||||
- source.get_pixel(x + 1, y - 1).0[0] as i32
|
||||
- source.get_pixel(x - 1, y).0[0] as i32
|
||||
+ 9 * source.get_pixel(x, y).0[0] as i32
|
||||
- source.get_pixel(x + 1, y).0[0] as i32
|
||||
- source.get_pixel(x - 1, y + 1).0[0] as i32
|
||||
- source.get_pixel(x, y + 1).0[0] as i32
|
||||
- source.get_pixel(x + 1, y + 1).0[0] as i32;
|
||||
destination.get_pixel_mut(x, y).0[0] =
|
||||
weighted_sum.clamp(u8::MIN as i32, u8::MAX as i32) as u8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn ostromoukhov_dither(source: GrayImage, bias: u8) -> Bitmap {
|
||||
let width = source.width();
|
||||
let height = source.height();
|
||||
assert_eq!(width % 8, 0);
|
||||
|
||||
let mut source = source.into_raw();
|
||||
let mut destination = BitVec::repeat(false, source.len());
|
||||
|
||||
for y in 0..height as usize {
|
||||
let start = y * width as usize;
|
||||
if y % 2 == 0 {
|
||||
for x in 0..width as usize {
|
||||
ostromoukhov_dither_pixel(
|
||||
&mut source,
|
||||
&mut destination,
|
||||
start + x,
|
||||
width as usize,
|
||||
y == (height - 1) as usize,
|
||||
1,
|
||||
bias,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
for x in (0..width as usize).rev() {
|
||||
ostromoukhov_dither_pixel(
|
||||
&mut source,
|
||||
&mut destination,
|
||||
start + x,
|
||||
width as usize,
|
||||
y == (height - 1) as usize,
|
||||
-1,
|
||||
bias,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap::from_bitvec(width as usize, destination)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn ostromoukhov_dither_pixel(
|
||||
source: &mut [u8],
|
||||
destination: &mut BitVec,
|
||||
position: usize,
|
||||
width: usize,
|
||||
last_row: bool,
|
||||
direction: isize,
|
||||
bias: u8,
|
||||
) {
|
||||
let old_pixel = source[position];
|
||||
|
||||
let destination_value = old_pixel > bias;
|
||||
destination.set(position, destination_value);
|
||||
|
||||
let error = if destination_value {
|
||||
255 - old_pixel
|
||||
} else {
|
||||
old_pixel
|
||||
};
|
||||
|
||||
let mut diffuse = |to: usize, mat: i16| {
|
||||
let diffuse_value = source[to] as i16 + mat;
|
||||
source[to] = diffuse_value.clamp(u8::MIN.into(), u8::MAX.into()) as u8;
|
||||
};
|
||||
|
||||
let lookup = if destination_value {
|
||||
ERROR_DIFFUSION_MATRIX[error as usize].map(move |i| -i)
|
||||
} else {
|
||||
ERROR_DIFFUSION_MATRIX[error as usize]
|
||||
};
|
||||
diffuse((position as isize + direction) as usize, lookup[0]);
|
||||
|
||||
if !last_row {
|
||||
diffuse(
|
||||
((position + width) as isize - direction) as usize,
|
||||
lookup[1],
|
||||
);
|
||||
diffuse(((position + width) as isize) as usize, lookup[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const ERROR_DIFFUSION_MATRIX: [[i16; 3]; 256] = [
|
||||
[0, 1, 0],
|
||||
[1, 0, 0],
|
||||
[1, 0, 1],
|
||||
[2, 0, 1],
|
||||
[2, 0, 2],
|
||||
[3, 0, 2],
|
||||
[4, 0, 2],
|
||||
[4, 1, 2],
|
||||
[5, 1, 2],
|
||||
[5, 2, 2],
|
||||
[5, 3, 2],
|
||||
[6, 3, 2],
|
||||
[6, 3, 3],
|
||||
[7, 3, 3],
|
||||
[7, 4, 3],
|
||||
[8, 4, 3],
|
||||
[8, 5, 3],
|
||||
[9, 5, 3],
|
||||
[9, 5, 4],
|
||||
[10, 6, 3],
|
||||
[10, 6, 4],
|
||||
[11, 7, 3],
|
||||
[11, 7, 4],
|
||||
[11, 8, 4],
|
||||
[12, 7, 5],
|
||||
[12, 7, 6],
|
||||
[12, 7, 7],
|
||||
[12, 7, 8],
|
||||
[12, 7, 9],
|
||||
[13, 7, 9],
|
||||
[13, 7, 10],
|
||||
[13, 7, 11],
|
||||
[13, 7, 12],
|
||||
[14, 7, 12],
|
||||
[14, 8, 12],
|
||||
[15, 8, 12],
|
||||
[15, 9, 12],
|
||||
[16, 9, 12],
|
||||
[16, 10, 12],
|
||||
[17, 10, 12],
|
||||
[17, 11, 12],
|
||||
[18, 12, 11],
|
||||
[19, 12, 11],
|
||||
[19, 13, 11],
|
||||
[20, 13, 11],
|
||||
[20, 14, 11],
|
||||
[21, 15, 10],
|
||||
[22, 15, 10],
|
||||
[22, 17, 9],
|
||||
[23, 17, 9],
|
||||
[24, 18, 8],
|
||||
[24, 19, 8],
|
||||
[25, 19, 8],
|
||||
[26, 20, 7],
|
||||
[26, 21, 7],
|
||||
[27, 22, 6],
|
||||
[28, 23, 5],
|
||||
[28, 24, 5],
|
||||
[29, 25, 4],
|
||||
[30, 26, 3],
|
||||
[31, 26, 3],
|
||||
[31, 28, 2],
|
||||
[32, 28, 2],
|
||||
[33, 29, 1],
|
||||
[34, 30, 0],
|
||||
[33, 31, 1],
|
||||
[32, 33, 1],
|
||||
[32, 33, 2],
|
||||
[31, 34, 3],
|
||||
[30, 36, 3],
|
||||
[29, 37, 4],
|
||||
[29, 37, 5],
|
||||
[28, 39, 5],
|
||||
[32, 34, 7],
|
||||
[37, 29, 8],
|
||||
[42, 23, 10],
|
||||
[46, 19, 11],
|
||||
[51, 13, 12],
|
||||
[52, 14, 13],
|
||||
[53, 13, 12],
|
||||
[53, 14, 13],
|
||||
[54, 14, 13],
|
||||
[55, 14, 13],
|
||||
[55, 14, 13],
|
||||
[56, 15, 14],
|
||||
[57, 14, 13],
|
||||
[56, 15, 15],
|
||||
[55, 17, 15],
|
||||
[54, 18, 16],
|
||||
[53, 20, 16],
|
||||
[52, 21, 17],
|
||||
[52, 22, 17],
|
||||
[51, 24, 17],
|
||||
[50, 25, 18],
|
||||
[49, 27, 18],
|
||||
[47, 29, 19],
|
||||
[48, 29, 19],
|
||||
[48, 29, 20],
|
||||
[49, 29, 20],
|
||||
[49, 30, 20],
|
||||
[50, 31, 20],
|
||||
[50, 31, 20],
|
||||
[51, 31, 20],
|
||||
[51, 31, 21],
|
||||
[52, 31, 21],
|
||||
[52, 32, 21],
|
||||
[53, 32, 21],
|
||||
[53, 32, 22],
|
||||
[55, 32, 21],
|
||||
[56, 31, 22],
|
||||
[58, 31, 21],
|
||||
[59, 30, 22],
|
||||
[61, 30, 21],
|
||||
[62, 29, 22],
|
||||
[64, 29, 21],
|
||||
[65, 28, 22],
|
||||
[67, 28, 21],
|
||||
[68, 27, 22],
|
||||
[70, 27, 21],
|
||||
[71, 26, 22],
|
||||
[73, 26, 21],
|
||||
[75, 25, 21],
|
||||
[76, 25, 21],
|
||||
[78, 24, 21],
|
||||
[80, 23, 21],
|
||||
[81, 23, 21],
|
||||
[83, 22, 21],
|
||||
[85, 21, 20],
|
||||
[85, 22, 21],
|
||||
[85, 22, 22],
|
||||
[84, 24, 22],
|
||||
[84, 24, 23],
|
||||
[84, 25, 23],
|
||||
[83, 27, 23],
|
||||
[83, 28, 23],
|
||||
[82, 29, 24],
|
||||
[82, 30, 24],
|
||||
[81, 31, 25],
|
||||
[80, 32, 26],
|
||||
[80, 33, 26],
|
||||
[79, 35, 26],
|
||||
[79, 36, 26],
|
||||
[78, 37, 27],
|
||||
[77, 38, 28],
|
||||
[77, 39, 28],
|
||||
[76, 41, 28],
|
||||
[75, 42, 29],
|
||||
[75, 43, 29],
|
||||
[74, 44, 30],
|
||||
[74, 45, 30],
|
||||
[75, 46, 30],
|
||||
[75, 46, 30],
|
||||
[76, 46, 30],
|
||||
[76, 46, 31],
|
||||
[77, 46, 31],
|
||||
[77, 47, 31],
|
||||
[78, 47, 31],
|
||||
[78, 47, 32],
|
||||
[79, 47, 32],
|
||||
[79, 48, 32],
|
||||
[80, 49, 32],
|
||||
[83, 46, 32],
|
||||
[86, 44, 32],
|
||||
[90, 42, 31],
|
||||
[93, 40, 31],
|
||||
[96, 39, 30],
|
||||
[100, 36, 30],
|
||||
[103, 35, 29],
|
||||
[106, 33, 29],
|
||||
[110, 30, 29],
|
||||
[113, 29, 28],
|
||||
[114, 29, 28],
|
||||
[115, 29, 28],
|
||||
[115, 29, 28],
|
||||
[116, 30, 29],
|
||||
[117, 29, 28],
|
||||
[117, 30, 29],
|
||||
[118, 30, 29],
|
||||
[119, 30, 29],
|
||||
[109, 43, 27],
|
||||
[100, 57, 23],
|
||||
[90, 71, 20],
|
||||
[80, 85, 17],
|
||||
[70, 99, 14],
|
||||
[74, 98, 12],
|
||||
[78, 97, 10],
|
||||
[81, 96, 9],
|
||||
[85, 95, 7],
|
||||
[89, 94, 5],
|
||||
[92, 93, 4],
|
||||
[96, 92, 2],
|
||||
[100, 91, 0],
|
||||
[100, 90, 2],
|
||||
[100, 88, 5],
|
||||
[100, 87, 7],
|
||||
[99, 86, 10],
|
||||
[99, 85, 12],
|
||||
[99, 84, 14],
|
||||
[99, 82, 17],
|
||||
[98, 81, 20],
|
||||
[98, 80, 22],
|
||||
[98, 79, 24],
|
||||
[98, 77, 27],
|
||||
[98, 76, 29],
|
||||
[97, 75, 32],
|
||||
[97, 73, 35],
|
||||
[97, 72, 37],
|
||||
[96, 71, 40],
|
||||
[96, 69, 43],
|
||||
[96, 67, 46],
|
||||
[96, 66, 48],
|
||||
[95, 65, 51],
|
||||
[95, 63, 54],
|
||||
[95, 61, 57],
|
||||
[94, 60, 60],
|
||||
[94, 58, 63],
|
||||
[94, 57, 65],
|
||||
[93, 55, 69],
|
||||
[93, 54, 71],
|
||||
[93, 52, 74],
|
||||
[92, 51, 77],
|
||||
[92, 49, 80],
|
||||
[91, 47, 84],
|
||||
[91, 46, 86],
|
||||
[93, 49, 82],
|
||||
[96, 52, 77],
|
||||
[98, 55, 73],
|
||||
[101, 58, 68],
|
||||
[104, 61, 63],
|
||||
[106, 65, 58],
|
||||
[109, 68, 53],
|
||||
[111, 71, 49],
|
||||
[114, 74, 44],
|
||||
[116, 78, 39],
|
||||
[118, 76, 40],
|
||||
[119, 74, 42],
|
||||
[120, 73, 43],
|
||||
[122, 71, 44],
|
||||
[123, 69, 46],
|
||||
[124, 67, 48],
|
||||
[125, 66, 49],
|
||||
[127, 64, 50],
|
||||
[128, 62, 52],
|
||||
[129, 60, 54],
|
||||
[131, 58, 55],
|
||||
[132, 57, 56],
|
||||
[136, 47, 63],
|
||||
[139, 38, 70],
|
||||
[143, 29, 76],
|
||||
[147, 19, 83],
|
||||
[151, 9, 90],
|
||||
[154, 0, 97],
|
||||
[160, 0, 92],
|
||||
[171, 0, 82],
|
||||
[183, 0, 71],
|
||||
[184, 0, 71],
|
||||
];
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::cli::StreamScreenOptions;
|
||||
use crate::ledwand_dither::{LedwandDither, LedwandDitherOptions};
|
||||
use crate::ledwand_dither::*;
|
||||
use image::{
|
||||
imageops::{dither, resize, BiLevel, FilterType},
|
||||
imageops::{resize, FilterType},
|
||||
DynamicImage, ImageBuffer, Luma, Rgb, Rgba,
|
||||
};
|
||||
use log::{error, info, warn};
|
||||
|
@ -11,42 +11,31 @@ use scap::{
|
|||
frame::Frame,
|
||||
};
|
||||
use servicepoint::{
|
||||
Bitmap, Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH,
|
||||
Command, CompressionCode, Connection, Origin, FRAME_PACING, PIXEL_HEIGHT, PIXEL_WIDTH,
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
pub fn stream_window(connection: &Connection, options: StreamScreenOptions) {
|
||||
info!("Starting capture with options: {:?}", options);
|
||||
warn!("this implementation does not drop any frames - set a lower fps or disable dithering if your computer cannot keep up.");
|
||||
|
||||
let capturer = match start_capture(&options) {
|
||||
Some(value) => value,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let mut bitmap = Bitmap::new(PIXEL_WIDTH, PIXEL_HEIGHT);
|
||||
info!("now starting to stream images");
|
||||
loop {
|
||||
let mut frame = get_next_frame(&capturer);
|
||||
|
||||
LedwandDither::histogram_correction(&mut frame);
|
||||
histogram_correction(&mut frame);
|
||||
|
||||
let mut orig = frame.clone();
|
||||
LedwandDither::blur(&orig, &mut frame);
|
||||
blur(&orig, &mut frame);
|
||||
|
||||
std::mem::swap(&mut frame, &mut orig);
|
||||
LedwandDither::sharpen(&orig, &mut frame);
|
||||
sharpen(&orig, &mut frame);
|
||||
|
||||
let cutoff = if options.no_dither {
|
||||
LedwandDither::median_brightness(&frame)
|
||||
} else {
|
||||
dither(&mut frame, &BiLevel);
|
||||
u8::MAX / 2
|
||||
};
|
||||
|
||||
for (mut dest, src) in bitmap.iter_mut().zip(frame.pixels()) {
|
||||
*dest = src.0[0] > cutoff;
|
||||
}
|
||||
let bitmap = ostromoukhov_dither(frame, u8::MAX / 2);
|
||||
|
||||
connection
|
||||
.send(Command::BitmapLinearWin(
|
||||
|
|
Loading…
Reference in a new issue