Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 66 additions & 42 deletions benchmarking/script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
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 <method>"
exit 1
fi

mkdir -p $RESULTS_DIR
mkdir -p $RESULTS_DIR/$method/compressed
mkdir -p $RESULTS_DIR/$method/reconstructed

$method
Comment on lines +84 to +94
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance input validation and error handling in main script

The main script logic could be improved with:

  1. Validation of the method argument against available functions
  2. Error handling for directory creation
  3. Option to clean previous results

Here's a suggested improvement:

# List of supported methods
SUPPORTED_METHODS="lossless mycodec kmeans stable_diffusion hific"

# Validate method
method=$1
if [ -z "$method" ]; then
    echo "Usage: ./script.sh <method>" >&2
    echo "Supported methods: $SUPPORTED_METHODS" >&2
    exit 1
fi

if [[ ! " $SUPPORTED_METHODS " =~ " $method " ]]; then
    echo "Error: Unknown method '$method'" >&2
    echo "Supported methods: $SUPPORTED_METHODS" >&2
    exit 1
fi

# Create directories with error handling
create_dirs() {
    local dirs=("$@")
    for dir in "${dirs[@]}"; do
        if ! mkdir -p "$dir"; then
            echo "Error: Failed to create directory: $dir" >&2
            exit 1
        fi
    done
}

# Optional cleanup of previous results
if [ "${CLEAN_PREVIOUS:-0}" = "1" ]; then
    rm -rf "$RESULTS_DIR/$method"
fi

create_dirs "$RESULTS_DIR" "$RESULTS_DIR/$method/compressed" "$RESULTS_DIR/$method/reconstructed"

# Execute method
$method

2 changes: 1 addition & 1 deletion benchmarking/summarize_results.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions png/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ kodak/
reconstructed/
compressed/
mycodec
lossless
image_data/
images/
26 changes: 19 additions & 7 deletions png/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines +5 to +7
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Typo in linking flags variable impacting build.
The variable defined as LDLFAGS is used where LDFLAGS is referenced in the compilation rule (line 21). This mismatch could lead to linking errors. Renaming LDLFAGS to LDFLAGS consistently can resolve potential build issues.

-LDLFAGS =-lpng -lz -lfftw3 -lfftw3_omp
+LDFLAGS = -lpng -lz -lfftw3 -lfftw3_omp

 $(JPEG): $(COMMON_OBJS) $(JPEG_OBJS)
-	$(CC) $(FLAGS) -o $(JPEG) $(COMMON_OBJS) $(JPEG_OBJS) $(LDLFAGS)
+	$(CC) $(FLAGS) -o $(JPEG) $(COMMON_OBJS) $(JPEG_OBJS) $(LDFLAGS)

 $(LOSSLESS): $(COMMON_OBJS) $(PNG_OBJS)
-	$(CC) $(FLAGS) -o $(LOSSLESS) $(COMMON_OBJS) $(PNG_OBJS) $(LDLFAGS)
+	$(CC) $(FLAGS) -o $(LOSSLESS) $(COMMON_OBJS) $(PNG_OBJS) $(LDFLAGS)

Also applies to: 9-11, 13-18, 23-24, 26-27, 30-30


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)
108 changes: 108 additions & 0 deletions png/driver_lossless.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#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<uint8_t> read_png_image(string input_filepath) {
read_png_file(input_filepath.c_str());
string filename = get_filename(input_filepath);

vector<uint8_t> image;
image.clear();
copy_row_pointers_to_vector(image, row_pointers, width, height);
return image;
}

void write_png_image(vector<uint8_t>& 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<uint8_t> 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<uint8_t> image(num_pixels);
infile.read((char*)&image[0], num_pixels * sizeof(uint8_t));
infile.close();

vector<vector<uint8_t>> 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] << " <input_file> <compressed_file> <output_file>" << 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<uint8_t> image = read_png_image(input_filepath);
compress_image(image, width, height, compressed_filepath);

// Decompress and write
vector<uint8_t> restored_image = decompress_image(compressed_filepath);
write_png_image(restored_image, compressed_filepath, output_filepath);
} else if(file_type(input_filepath) == "ppm") {
vector<uint8_t> image = read_ppm_image(input_filepath);
compress_image(image, width, height, compressed_filepath);

// Decompress and write
vector<uint8_t> 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;
}
28 changes: 28 additions & 0 deletions png/lossless.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "mycodec.h"
#include "compress.h"

using namespace std;

/*
Ideas:
1. RLE encoding: <intensity[0,255], count>: 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<uint8_t>& image, int width, int height, string filepath) {
image = delta_encode<uint8_t> (image);
vector<char> buffer = write_vec_to_buffer<uint8_t>(image, width, height, filepath);
compress(buffer, filepath);
return filepath;
}

vector<uint8_t> decompress_image(string filename) {
vector<char> buffer = decompress1(filename);
vector<uint8_t> image = read_vec_from_buffer<uint8_t>(buffer);
image = delta_decode<uint8_t>(image);
return image;
}