Improved DCT functions

This commit is contained in:
G2-Games 2024-07-26 02:30:30 -05:00
parent c97eae75df
commit 41158066ec
3 changed files with 55 additions and 40 deletions

View file

@ -133,13 +133,40 @@ pub fn dequantize(input: &[i16], quant_matrix: [u16; 64]) -> Vec<f32> {
input.iter().zip(quant_matrix).map(|(v, q)| (*v as i16 * q as i16) as f32).collect() input.iter().zip(quant_matrix).map(|(v, q)| (*v as i16 * q as i16) as f32).collect()
} }
pub fn dct_compress(input: &[u8], width: u32, height: u32, quality: u32) -> (Vec<Vec<i16>>, usize, usize) {
let new_width = width as usize + (8 - width % 8) as usize;
let new_height = height as usize + (8 - height % 8) as usize;
let mut img_2d: Vec<Vec<u8>> = input.windows(width as usize).step_by(width as usize).map(|r| r.to_vec()).collect();
img_2d.iter_mut().for_each(|r| r.resize(new_width, 0));
img_2d.resize(new_height, vec![0u8; new_width]);
let mut dct_image = Vec::new();
for h in 0..new_height / 8 {
for w in 0..new_width / 8 {
let mut chunk = Vec::new();
for i in 0..8 {
let row = &img_2d[(h * 8) + i][w * 8..(w * 8) + 8];
chunk.extend_from_slice(&row);
}
// Perform the DCT on the image section
let dct: Vec<f32> = dct(&chunk, 8, 8);
let quantzied_dct = quantize(&dct, quantization_matrix(quality));
dct_image.push(quantzied_dct);
}
}
(dct_image, new_width, new_height)
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
#[test] #[test]
fn quantization_matrix_q80() { fn quantization_matrix_q80() {
let result = gen_quantization_matrix(80); let result = quantization_matrix(80);
assert_eq!( assert_eq!(
result, result,
@ -158,20 +185,20 @@ mod tests {
#[test] #[test]
fn quantization_matrix_q100() { fn quantization_matrix_q100() {
let result = gen_quantization_matrix(100); let result = quantization_matrix(100);
assert_eq!( assert_eq!(
result, result,
[ [
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 0, 0, 0, 0, 0, 0 1, 1, 1, 1, 1, 1, 1, 1
] ]
); );
} }
} }

View file

@ -31,3 +31,11 @@ impl Header {
buf.into_inner().try_into().unwrap() buf.into_inner().try_into().unwrap()
} }
} }
pub enum ColorFormat {
/// RGBA, 8 bits per channel
Rgba32,
/// RGB, 8 bits per channel
Rgb24,
}

View file

@ -10,45 +10,25 @@ pub mod picture;
use picture::DangoPicture; use picture::DangoPicture;
use std::{ use std::{
fs::File, fs::File,
io::{BufReader, BufWriter}, io::{BufReader, BufWriter, Write},
time::Instant, time::Instant,
}; };
use compression::dct::{dct, dequantize, idct, quantization_matrix, quantize}; use compression::{dct::{dct, dct_compress, dequantize, idct, quantization_matrix, quantize}, lossless};
use image::{ColorType, DynamicImage, GenericImage, GrayImage, Luma, Rgba}; use image::{ColorType, DynamicImage, GenericImage, GrayImage, Luma, Rgba};
fn main() { fn main() {
let input = image::open("scaramouche.png").unwrap().to_luma8(); let input = image::open("transparent.png").unwrap().to_luma8();
input.save("original.png").unwrap(); input.save("original.png").unwrap();
let new_width = input.width() as usize + (8 - input.width() % 8) as usize; let (dct_image, new_width, new_height) = dct_compress(input.as_raw(), input.width(), input.height(), 100);
let new_height = input.height() as usize + (8 - input.height() % 8) as usize; let compressed_dct = lossless::compress(&dct_image.iter().flatten().flat_map(|b| b.to_le_bytes()).collect::<Vec<u8>>());
let mut img_2d: Vec<Vec<u8>> = input.windows(input.width() as usize).step_by(input.width() as usize).map(|r| r.to_vec()).collect(); let mut dct_save = File::create("dct_raw.dct").unwrap();
img_2d.iter_mut().for_each(|r| r.resize(new_width, 0)); dct_save.write_all(&compressed_dct.0).unwrap();
img_2d.resize(new_height, vec![0u8; new_width]);
let timer = Instant::now();
let mut dct_image = Vec::new();
for h in 0..new_height / 8 {
for w in 0..new_width / 8 {
let mut chunk = Vec::new();
for i in 0..8 {
let row = &img_2d[(h * 8) + i][w * 8..(w * 8) + 8];
chunk.extend_from_slice(&row);
}
// Perform the DCT on the image section
let dct: Vec<f32> = dct(&chunk, 8, 8);
let quantzied_dct = quantize(&dct, quantization_matrix(80));
dct_image.push(quantzied_dct);
}
}
println!("Encoding took {}ms", timer.elapsed().as_millis());
let mut decoded_image = GrayImage::new(new_width as u32, new_height as u32); let mut decoded_image = GrayImage::new(new_width as u32, new_height as u32);
for (i, chunk) in dct_image.iter().enumerate() { for (i, chunk) in dct_image.iter().enumerate() {
let dequantized_dct = dequantize(chunk, quantization_matrix(80)); let dequantized_dct = dequantize(chunk, quantization_matrix(100));
let original = idct(&dequantized_dct, 8, 8); let original = idct(&dequantized_dct, 8, 8);
// Write rows of blocks // Write rows of blocks