From 6f4bbca97b671f3a8e73699b15b91c9b1a6f1358 Mon Sep 17 00:00:00 2001 From: Ajay Joshi Date: Wed, 7 Dec 2022 01:19:55 -0600 Subject: [PATCH] Add lossless codec(Delta + Zlib compression) and support for benchmarking --- benchmarking/script.sh | 108 ++++++++++++++++++------------ benchmarking/summarize_results.py | 2 +- png/.gitignore | 1 + png/Makefile | 26 +++++-- png/driver_lossless.cpp | 108 ++++++++++++++++++++++++++++++ png/lossless.cpp | 28 ++++++++ 6 files changed, 223 insertions(+), 50 deletions(-) create mode 100644 png/driver_lossless.cpp create mode 100644 png/lossless.cpp diff --git a/benchmarking/script.sh b/benchmarking/script.sh index d1bd817..bd3aa87 100644 --- a/benchmarking/script.sh +++ b/benchmarking/script.sh @@ -2,6 +2,7 @@ BASE_DIR=/mnt/Image-Compression/ DATA_DIR=$BASE_DIR/data RESULTS_DIR=$BASE_DIR/benchmarking/results +LOSSLESS_DIR=$BASE_DIR/png MYCODEC_DIR=$BASE_DIR/png CLUSTERING_DIR=$BASE_DIR/clustering DIFFUSION_DIR=$BASE_DIR/stable-diffusion-compressor @@ -10,61 +11,84 @@ HIFIC_DIR=$BASE_DIR/hific CLIC_DATA_DIR=$DATA_DIR/clic C10N_DATA_DIR=$DATA_DIR/c10n_benchmark -mkdir -p $RESULTS_DIR/mycodec/compressed -mkdir -p $RESULTS_DIR/mycodec/reconstructed -mkdir -p $RESULTS_DIR/clustering/compressed -mkdir -p $RESULTS_DIR/clustering/reconstructed -mkdir -p $RESULTS_DIR/stable_diffusion/compressed -mkdir -p $RESULTS_DIR/stable_diffusion/reconstructed -mkdir -p $RESULTS_DIR/hific/compressed -mkdir -p $RESULTS_DIR/hific/reconstructed +lossless() { + # Run custom lossless codec on C10N benchmark dataset + for i in $C10N_DATA_DIR/*.ppm; do + img_name=$(basename -s .ppm $i) + src_img_path=$i + compressed_img_path=$RESULTS_DIR/lossless/compressed/$img_name.z + reconstructed_img_path=$RESULTS_DIR/lossless/reconstructed/$img_name.png -# Run custom codec on C10N benchmark dataset -for i in $C10N_DATA_DIR/*.ppm; do - img_name=$(basename -s .ppm $i) - src_img_path=$i + $MYCODEC_DIR/lossless $src_img_path $compressed_img_path $reconstructed_img_path + done +} - compressed_img_path=$RESULTS_DIR/mycodec/compressed/$img_name.z - reconstructed_img_path=$RESULTS_DIR/mycodec/reconstructed/$img_name.png +mycodec() { + # Run custom codec on C10N benchmark dataset + for i in $C10N_DATA_DIR/*.ppm; do + img_name=$(basename -s .ppm $i) + src_img_path=$i - $MYCODEC_DIR/mycodec $src_img_path $compressed_img_path $reconstructed_img_path -done + compressed_img_path=$RESULTS_DIR/mycodec/compressed/$img_name.z + reconstructed_img_path=$RESULTS_DIR/mycodec/reconstructed/$img_name.png + + $MYCODEC_DIR/mycodec $src_img_path $compressed_img_path $reconstructed_img_path + done +} # Run k-means clustering codec on C10N benchmark dataset -K=200 -for i in $C10N_DATA_DIR/*.ppm; do - img_name=$(basename -s .ppm $i) - src_img_path=$i +kmeans() { + K=200 + for i in $C10N_DATA_DIR/*.ppm; do + img_name=$(basename -s .ppm $i) + src_img_path=$i - compressed_img_path=$RESULTS_DIR/clustering/compressed/$img_name.z - reconstructed_img_path=$RESULTS_DIR/clustering/reconstructed/$img_name.png + compressed_img_path=$RESULTS_DIR/clustering/compressed/$img_name.z + reconstructed_img_path=$RESULTS_DIR/clustering/reconstructed/$img_name.png - python3 $CLUSTERING_DIR/kmeans.py $src_img_path $compressed_img_path $reconstructed_img_path $K -done + python3 $CLUSTERING_DIR/kmeans.py $src_img_path $compressed_img_path $reconstructed_img_path $K + done +} # Run stable diffusion codec on C10N benchmark dataset -for i in $C10N_DATA_DIR/*.ppm; do - img_name=$(basename -s .ppm $i) - src_img_path=$i +stable_diffusion() { + for i in $C10N_DATA_DIR/*.ppm; do + img_name=$(basename -s .ppm $i) + src_img_path=$i - compressed_img_path=$RESULTS_DIR/stable_diffusion/compressed/$img_name.z - reconstructed_img_path=$RESULTS_DIR/stable_diffusion/reconstructed/$img_name.png + compressed_img_path=$RESULTS_DIR/stable_diffusion/compressed/$img_name.z + reconstructed_img_path=$RESULTS_DIR/stable_diffusion/reconstructed/$img_name.png - python3 $DIFFUSION_DIR/driver.py $src_img_path $compressed_img_path $reconstructed_img_path -done + python3 $DIFFUSION_DIR/driver.py $src_img_path $compressed_img_path $reconstructed_img_path + done +} # Run HiFiC codec on C10N benchmark dataset -model="hific-lo" -for i in $C10N_DATA_DIR/*.ppm; do - img_name=$(basename -s .ppm $i) - src_img_path=$i - - compressed_img_path=$RESULTS_DIR/hific/compressed/$img_name.tfci - reconstructed_img_path=$RESULTS_DIR/hific/reconstructed/$img_name.png - - python3 $HIFIC_DIR/tfci.py compress $model $src_img_path $compressed_img_path - python3 $HIFIC_DIR/tfci.py decompress $compressed_img_path $reconstructed_img_path -done \ No newline at end of file +hific() { + model="hific-lo" + for i in $C10N_DATA_DIR/*.ppm; do + img_name=$(basename -s .ppm $i) + src_img_path=$i + + compressed_img_path=$RESULTS_DIR/hific/compressed/$img_name.tfci + reconstructed_img_path=$RESULTS_DIR/hific/reconstructed/$img_name.png + + python3 $HIFIC_DIR/tfci.py compress $model $src_img_path $compressed_img_path + python3 $HIFIC_DIR/tfci.py decompress $compressed_img_path $reconstructed_img_path + done +} + +method=$1 +if [ -z "$method" ]; then + echo "Usage: ./script.sh " + exit 1 +fi + +mkdir -p $RESULTS_DIR +mkdir -p $RESULTS_DIR/$method/compressed +mkdir -p $RESULTS_DIR/$method/reconstructed + +$method \ No newline at end of file diff --git a/benchmarking/summarize_results.py b/benchmarking/summarize_results.py index 5a9b517..abe6c97 100644 --- a/benchmarking/summarize_results.py +++ b/benchmarking/summarize_results.py @@ -77,7 +77,7 @@ def summarize_results(method): if __name__ == "__main__": base_dir="/mnt/Image-Compression" - codecs = ["mycodec", "clustering", "hific", "stable_diffusion"] + codecs = ["lossless", "mycodec", "clustering", "hific", "stable_diffusion"] for codec in codecs: print(f"Summarizing results for {codec}") summarize_results(codec) diff --git a/png/.gitignore b/png/.gitignore index 03387e8..b825056 100644 --- a/png/.gitignore +++ b/png/.gitignore @@ -7,5 +7,6 @@ kodak/ reconstructed/ compressed/ mycodec +lossless image_data/ images/ \ No newline at end of file diff --git a/png/Makefile b/png/Makefile index 05a2c9c..7f89f1a 100644 --- a/png/Makefile +++ b/png/Makefile @@ -2,17 +2,29 @@ CC = g++ FLAGS = -std=c++11 -O3 -fpermissive -fPIC -fopenmp LDLFAGS =-lpng -lz -lfftw3 -lfftw3_omp -SRCS = driver.cpp refpng.cpp mycodec.cpp rle.cpp delta.cpp bucket.cpp color_transform.cpp compress.cpp dct.cpp utils.cpp -OBJS = $(SRCS:.cpp=.o) -TARGET = mycodec +SRCS_COMMON = refpng.cpp rle.cpp delta.cpp bucket.cpp color_transform.cpp compress.cpp dct.cpp utils.cpp +SRCS_JPEG = mycodec.cpp driver.cpp +SRCS_PNG = lossless.cpp driver_lossless.cpp -all: $(TARGET) +COMMON_OBJS = $(SRCS_COMMON:.cpp=.o) +JPEG_OBJS = $(SRCS_JPEG:.cpp=.o) +PNG_OBJS = $(SRCS_PNG:.cpp=.o) + +JPEG = mycodec +LOSSLESS = lossless + +all: $(JPEG) $(LOSSLESS) +jpeg: $(JPEG) +png: $(LOSSLESS) %.o: %.cpp $(CC) $(FLAGS) -c $< -o $@ $(LDFLAGS) -$(TARGET): $(OBJS) - $(CC) $(FLAGS) -o $(TARGET) $(OBJS) $(LDLFAGS) +$(JPEG): $(COMMON_OBJS) $(JPEG_OBJS) + $(CC) $(FLAGS) -o $(JPEG) $(COMMON_OBJS) $(JPEG_OBJS) $(LDLFAGS) + +$(LOSSLESS): $(COMMON_OBJS) $(PNG_OBJS) + $(CC) $(FLAGS) -o $(LOSSLESS) $(COMMON_OBJS) $(PNG_OBJS) $(LDLFAGS) clean: - rm -f $(OBJS) $(TARGET) + rm -f $(COMMON_OBJS) $(JPEG_OBJS) $(PNG_OBJS) $(JPEG) $(LOSSLESS) diff --git a/png/driver_lossless.cpp b/png/driver_lossless.cpp new file mode 100644 index 0000000..c468179 --- /dev/null +++ b/png/driver_lossless.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include "refpng.h" +#include "mycodec.h" +#include "compress.h" + +using namespace std; + +string file_type(string filename) { + int loc = filename.find_last_of("."); + string file_type = filename.substr(loc + 1); + return file_type; +} + +string get_filename(string filepath){ + stringstream ss(filepath); + string folder, filename; + getline(ss, folder, '/'); + getline(ss, filename, '.'); + return filename; +} + +vector read_png_image(string input_filepath) { + read_png_file(input_filepath.c_str()); + string filename = get_filename(input_filepath); + + vector image; + image.clear(); + copy_row_pointers_to_vector(image, row_pointers, width, height); + return image; +} + +void write_png_image(vector& image, string compressed_filepath, string output_filepath) { + row_pointers = copy_vector_to_row_pointers(image, width, height); + write_png_file(output_filepath.c_str()); +} + +vector read_ppm_image(string input_filepath) { + ifstream infile(input_filepath, ios::in | ios::binary); + string magic_number; + int max_color_value; + infile >> magic_number; + infile >> width >> height; // setting global values + infile >> max_color_value; + + #ifdef debug + cout << "Reading PPM image from file: with width: " << width << " and height: " << height << endl; + cout << "Max color value: " << max_color_value << endl; + #endif + + int num_pixels = width * height * 3; + vector image(num_pixels); + infile.read((char*)&image[0], num_pixels * sizeof(uint8_t)); + infile.close(); + + vector> channels = separate_channels(image, width * height); + + // channels --> 1 --> actual red, 2 --> actual green, 0 --> actual blue + + channels = {channels[1], channels[2], channels[0]}; + image = merge_channels(channels, width * height); + + cout << "Number of image pixels: " << image.size() << endl; + + return image; +} + + +int main(int argc, char* argv[]) { + if(argc != 4) { + cout << "Usage: " << argv[0] << " " << endl; + return 1; + } + string input_filepath = argv[1], compressed_filepath = argv[2], output_filepath = argv[3]; + cout << "Original file size: " << (file_size(argv[1]) / 1024) << " KB" << endl; + + omp_set_num_threads(40); + fftw_init_threads(); + fftw_plan_with_nthreads(omp_get_max_threads()); + + if(file_type(input_filepath) == "png"){ + // Read and compress + vector image = read_png_image(input_filepath); + compress_image(image, width, height, compressed_filepath); + + // Decompress and write + vector restored_image = decompress_image(compressed_filepath); + write_png_image(restored_image, compressed_filepath, output_filepath); + } else if(file_type(input_filepath) == "ppm") { + vector image = read_ppm_image(input_filepath); + compress_image(image, width, height, compressed_filepath); + + // Decompress and write + vector restored_image = decompress_image(compressed_filepath); + write_png_image(restored_image, compressed_filepath, output_filepath); + } else { + cout << "Input file is not a PNG or PPM image." << endl; + return 1; + } + + fftw_cleanup_threads(); + + + return 0; +} \ No newline at end of file diff --git a/png/lossless.cpp b/png/lossless.cpp new file mode 100644 index 0000000..0664b7a --- /dev/null +++ b/png/lossless.cpp @@ -0,0 +1,28 @@ +#include "mycodec.h" +#include "compress.h" + +using namespace std; + +/* +Ideas: +1. RLE encoding: : a) directly including all channels --> image blows up to 4MB, b) for each channel separately +2. RGB to YCbCr conversion +3. Bucketing CbCr values into 128 buckets and representing each intensity in 7 bits using bitsets --> lossy +4. ZLib compression +5. DCT + Quantization +*/ + +// Combination 1: Bucketing + Delta + ZLib (RLE worsens compression) +string compress_image(vector& image, int width, int height, string filepath) { + image = delta_encode (image); + vector buffer = write_vec_to_buffer(image, width, height, filepath); + compress(buffer, filepath); + return filepath; +} + +vector decompress_image(string filename) { + vector buffer = decompress1(filename); + vector image = read_vec_from_buffer(buffer); + image = delta_decode(image); + return image; +}