From 4f6f990285770135d60061d796876ea653ff8636 Mon Sep 17 00:00:00 2001 From: Omar Wael Date: Fri, 28 Apr 2017 17:59:17 +0200 Subject: [PATCH] Enhanced compression time --- Compressor/Compressor.cpp | 175 ++++++++++---------------------------- Compressor/Compressor.h | 42 ++------- Compressor/Directory.h | 2 +- Compressor/Source.cpp | 44 +++++++--- Compressor/Utility.h | 51 +++++++++-- 5 files changed, 128 insertions(+), 186 deletions(-) diff --git a/Compressor/Compressor.cpp b/Compressor/Compressor.cpp index c714fa7..6719dc8 100644 --- a/Compressor/Compressor.cpp +++ b/Compressor/Compressor.cpp @@ -1,62 +1,34 @@ #include "Compressor.h" -Compressor::Compressor(int threshold) { - this->threshold = threshold; +Compressor::Compressor() { + } Compressor::~Compressor() { - imgMatrix.deallocate(); + } // // Compression functions // -void Compressor::compress(const string& imagePath, const string& outputPath) { - cout << "Loading image..." << endl; - loadImage(imagePath); - cout << "Compressing..." << endl; - encodeData(); - cout << "Saving compressed file..." << endl; - saveCompressedFile(outputPath); -} - -void Compressor::loadImage(const string& path) { - // Load colored image from file - cv::Mat rgbMat = imread(path, CV_LOAD_IMAGE_COLOR); - - // Check for invalid input - if (rgbMat.empty() || !rgbMat.data) { - string errorMessage = "Could not load the image at: " + path; - throw exception(errorMessage.c_str()); - } - - // Get image size - rows = rgbMat.rows; - cols = rgbMat.cols; - - // Convert BGR to Gray - cv::cvtColor(rgbMat, imgMatrix, CV_BGR2GRAY); -} - -void Compressor::encodeData() { +void Compressor::compress(const cv::Mat& imageMat, vector& outputBytes) { + // Clear previous records compressedBytes.clear(); compressedSizes.clear(); // Store image rows count - compressedSizes.push_back(encodeToBase256(rows)); + compressedSizes.push_back(encodeToBase256(imageMat.rows)); // Store image cols count - compressedSizes.push_back(encodeToBase256(cols)); + compressedSizes.push_back(encodeToBase256(imageMat.cols)); // Store image pixels int cnt = 0; - bool pixel, prv = true; - for (int i = 0; i < rows; ++i) { - for (int j = 0; j < cols; ++j) { - pixel = ((int)imgMatrix.at(i, j) > this->threshold); - - if (prv == pixel) { + bool prv = true; + for (int i = 0; i < imageMat.rows; ++i) { + for (int j = 0; j < imageMat.cols; ++j) { + if (prv == imageMat.at(i, j)) { ++cnt; } else { @@ -70,9 +42,9 @@ void Compressor::encodeData() { // Store compression meta-data encodeMetaData(); - /*for (int i = 0; i < compressedSizes.size(); ++i) { - compressedImage.push_back(compressedSizes[i]); - }*/ + + // Pass compressed data to function caller + outputBytes.swap(compressedBytes); } void Compressor::encodeMetaData() { @@ -110,101 +82,38 @@ int Compressor::encodeToBase256(int number) { return cnt; } -void Compressor::saveCompressedFile(const string& path) { - ofstream fout(path, ofstream::binary); - - if (!fout.is_open()) { - string errorMessage = "Could not load the file at: " + path; - throw exception(errorMessage.c_str()); - } - - fout.write((char*)compressedBytes.data(), compressedBytes.size()); - - fout.close(); -} - // // Extraction functions // -void Compressor::extract(const string& compressedFilePath, const string& outputPath) { - cout << "Loading compressed file..." << endl; - loadCompressedFile(compressedFilePath); - cout << "Extracting..." << endl; - decodeData(); - cout << "Saving image..." << endl; - saveImage(outputPath); -} - -void Compressor::loadCompressedFile(const string& path) { - ifstream fin(path, ifstream::binary); - - if (!fin.is_open()) { - string errorMessage = "Could not load the file at: " + path; - throw exception(errorMessage.c_str()); - } - - // Get file size - fin.seekg(0, fin.end); - int fileSize = fin.tellg(); - fin.seekg(0, fin.beg); +void Compressor::extract(vector& compressedBytes, cv::Mat& outputImage) { + // Pass data to compressor object + this->compressedBytes.swap(compressedBytes); - // Read all data - compressedBytes.resize(fileSize); - fin.read((char*)compressedBytes.data(), fileSize); - - fin.close(); -} - -void Compressor::decodeData() { - // Retrieve image meta-data - compressedSizes.clear(); - int bytesCnt = 0; - int dataIdx = 0, sizeIdx = -1, idx = -1; - int n = compressedBytes.size(); - while (n >= 0 && n > bytesCnt) { - int cnt = (compressedBytes[--n] & 63); - int len = (compressedBytes[n] >> 6) + 1; - bytesCnt += cnt * len; - - for (int i = 0; i < cnt; ++i) { - compressedSizes.push_back(len); - } - } + // Retrieve compression meta-data + decodeMetaData(); - /*compressedSizes.clear(); - int bytesCnt = 0; int dataIdx = 0, sizeIdx = -1; - int n = compressedImage.size(); - while (n >= 0 && n > bytesCnt) { - bytesCnt += (int)compressedImage[--n]; - compressedSizes.push_back(compressedImage[n]); - } - reverse(compressedSizes.begin(), compressedSizes.end());*/ - - if (n != bytesCnt) { - throw exception("Could not extract the given file"); - } // Retrieve image rows count - rows = decodeFromBase256(dataIdx, compressedSizes[++sizeIdx]); + int rows = decodeFromBase256(dataIdx, compressedSizes[++sizeIdx]); dataIdx += compressedSizes[sizeIdx]; // Retrieve image cols count - cols = decodeFromBase256(dataIdx, compressedSizes[++sizeIdx]); + int cols = decodeFromBase256(dataIdx, compressedSizes[++sizeIdx]); dataIdx += compressedSizes[sizeIdx]; // Retrieve image pixels - imgMatrix = cv::Mat(rows, cols, CV_8U); - int i = 0, j = 0; - int cnt; + outputImage = cv::Mat(rows, cols, CV_8U); + int i = 0, j = 0, cnt; bool color = true; - while (dataIdx < n) { + + for (int k = 2; k < compressedSizes.size(); ++k) { cnt = decodeFromBase256(dataIdx, compressedSizes[++sizeIdx]); dataIdx += compressedSizes[sizeIdx]; while (cnt--) { - imgMatrix.at(i, j) = color ? 255 : 0; + outputImage.at(i, j) = (color ? 255 : 0); if (++j >= cols) { ++i; @@ -216,6 +125,27 @@ void Compressor::decodeData() { } } +void Compressor::decodeMetaData() { + compressedSizes.clear(); + + int bytesCnt = 0; + int n = compressedBytes.size(); + + while (n >= 0 && n > bytesCnt) { + int cnt = (compressedBytes[--n] & 63); + int len = (compressedBytes[n] >> 6) + 1; + bytesCnt += cnt * len; + + for (int i = 0; i < cnt; ++i) { + compressedSizes.push_back(len); + } + } + + if (n != bytesCnt) { + throw exception("Could not extract the given file"); + } +} + int Compressor::decodeFromBase256(int idx, int size) { idx += size; int num = 0; @@ -226,15 +156,4 @@ int Compressor::decodeFromBase256(int idx, int size) { } return num; -} - -void Compressor::saveImage(const string& path) { - //Mat image(rows, cols, CV_8UC3); - - /*int idx = -1; - for (int i = 0; i < rows; ++i) - for (int j = 0; j < cols; ++j) - image.at(i, j) = (binaryImage[++idx] ? Vec3b(255, 255, 255) : Vec3b(0, 0, 0));*/ - - imwrite(path, imgMatrix); } \ No newline at end of file diff --git a/Compressor/Compressor.h b/Compressor/Compressor.h index 6346ee5..e362679 100644 --- a/Compressor/Compressor.h +++ b/Compressor/Compressor.h @@ -11,25 +11,20 @@ #include // Custom libraries -//#include "Utility.h" using namespace cv; using namespace std; class Compressor { -public: - int rows; - int cols; - int threshold; +private: vector compressedSizes; vector compressedBytes; - cv::Mat imgMatrix; public: /** * Constructor */ - Compressor(int threshold); + Compressor(); /** * Destructor @@ -39,24 +34,14 @@ class Compressor /** * Compress the given black & white jpg image */ - void compress(const string& imagePath, const string& outputPath); + void compress(const cv::Mat& imageMat, vector& outputBytes); /** * Extract the given compressed file to a black & white jpg image */ - void extract(const string& compressedFilePath, const string& outputPath); + void extract(vector& compressedBytes, cv::Mat& outputImage); private: - /** - * Loads grayscaled image from the given path into a matrix - */ - void loadImage(const string& path); - - /** - * Encode the loaded image data and save it to compressedBytes vector - */ - void encodeData(); - /** * Encode meta-data needed in decompression process */ @@ -70,19 +55,9 @@ class Compressor int encodeToBase256(int number); /** - * Save the compress data to the given path + * Decode image compressed meta-data needed in decompression process */ - void saveCompressedFile(const string& path); - - /** - * Load the compress data from the given path - */ - void loadCompressedFile(const string& path); - - /** - * Decode the loaded file data into a binary image - */ - void decodeData(); + void decodeMetaData(); /** * Convert the given size of bytes from compressedBytes vector starting from idx @@ -90,9 +65,4 @@ class Compressor * Return the converted integer */ int decodeFromBase256(int idx, int size); - - /** - * Save the loaded image with the given url - */ - void saveImage(const string& path); }; \ No newline at end of file diff --git a/Compressor/Directory.h b/Compressor/Directory.h index 20e3141..42f1844 100644 --- a/Compressor/Directory.h +++ b/Compressor/Directory.h @@ -7,7 +7,7 @@ using namespace std; /** * Returns a list of files (name, extension) in the given directory */ -inline void GetFilesInDirectory(vector>& files, const string& directory) { +inline void getFilesInDirectory(const string& directory, vector>& files) { // Make sure to change the following command to the corresponding // one on your operating system when using Linux or MAC string s = "dir " + directory + "b > dirs.txt"; diff --git a/Compressor/Source.cpp b/Compressor/Source.cpp index 565af63..1ab79db 100644 --- a/Compressor/Source.cpp +++ b/Compressor/Source.cpp @@ -6,8 +6,6 @@ #include "Compressor.h" using namespace std; -#define BLACK_WHITE_THRESHOLD 180 - #define PATH_SAMPLE_DATA "DataSet/" #define PATH_COMPRESSED_DATA "Compressed/" #define PATH_UNCOMPRESSED_DATA "Uncompressed/" @@ -31,14 +29,13 @@ int main() { int startTime = clock(); int originalFilesSize = 0; int compressedFilesSize = 0; - Compressor compressor(BLACK_WHITE_THRESHOLD); vector> files; cout << fixed << setprecision(3); try { // Read files info - GetFilesInDirectory(files, PATH_SAMPLE_DATA); + getFilesInDirectory(PATH_SAMPLE_DATA, files); for (int i = 0; i < files.size(); ++i) { // If file is not of type .jpg then skip it @@ -51,23 +48,42 @@ int main() { string cpr = PATH_COMPRESSED_DATA + files[i].first + "." + EXT_COMPRESSED_FILE; string dst = PATH_UNCOMPRESSED_DATA + files[i].first + "." + EXT_SAMPLE_FILE; - cout << "Processing " << src << "..." << endl; - - // Compressing - compressor.compress(src, cpr); + // Compression variables + Compressor compressor; + cv::Mat originalImg, uncompressedImg; + vector compressedBytes; - // Get image size before and after compression - int orgSize = compressor.rows * compressor.cols; - int comSize = compressor.compressedBytes.size(); + // Loading image + cout << "Loading " << src << "..." << endl; + originalImg = loadBinaryImage(src); + int orgSize = originalImg.rows * originalImg.cols; originalFilesSize += orgSize; + + // Compressing + cout << "Compressing..." << endl; + compressor.compress(originalImg, compressedBytes); + int comSize = compressedBytes.size(); compressedFilesSize += comSize; - // Decompressing - compressor.extract(cpr, dst); + // Saving compressed image + cout << "Saving compressed file..." << endl; + saveFile(cpr, compressedBytes); + + // Loading file + cout << "Loading compressed file..." << endl; + loadFile(cpr, compressedBytes); + + // Extracting + cout << "Extracting..." << endl; + compressor.extract(compressedBytes, uncompressedImg); + + // Saving extracted image + cout << "Saving image..." << endl; + imwrite(dst, uncompressedImg); // Stop if invalid compression is detected cout << "Comparing original and compressed images..." << endl; - if (!compareImages(src, dst, BLACK_WHITE_THRESHOLD)) { + if (!compareImages(originalImg, uncompressedImg)) { cout << "Lossy compression!" << endl; return 0; } diff --git a/Compressor/Utility.h b/Compressor/Utility.h index 6fccc61..0a2d509 100644 --- a/Compressor/Utility.h +++ b/Compressor/Utility.h @@ -9,10 +9,12 @@ using namespace cv; using namespace std; +#define BLACK_WHITE_THRESHOLD 180 + /** * Loads binary image from the given path into a matrix of pixels */ -inline cv::Mat loadBinaryImage(const string& path, int threshold) { +inline cv::Mat loadBinaryImage(const string& path) { // Load colored image from file cv::Mat rgbMat = imread(path, CV_LOAD_IMAGE_COLOR); @@ -32,20 +34,55 @@ inline cv::Mat loadBinaryImage(const string& path, int threshold) { cv::Mat binaryMat(grayMat.size(), grayMat.type()); // Apply thresholding - cv::threshold(grayMat, binaryMat, threshold, 1, cv::THRESH_BINARY); + cv::threshold(grayMat, binaryMat, BLACK_WHITE_THRESHOLD, 1, cv::THRESH_BINARY); return binaryMat; } +/** + * Save the given vector of bytes to the given directory + */ +inline void saveFile(const string& path, const vector& outBytes) { + ofstream fout(path, ofstream::binary); + + if (!fout.is_open()) { + string errorMessage = "Could not load the file at: " + path; + throw exception(errorMessage.c_str()); + } + + fout.write((char*)outBytes.data(), outBytes.size()); + + fout.close(); +} + +/** + * Load the data from the given directory into the given vector of bytes + */ +inline void loadFile(const string& path, vector& inBytes) { + ifstream fin(path, ifstream::binary); + + if (!fin.is_open()) { + string errorMessage = "Could not load the file at: " + path; + throw exception(errorMessage.c_str()); + } + + // Get file size + fin.seekg(0, fin.end); + int fileSize = fin.tellg(); + fin.seekg(0, fin.beg); + + // Read all data + inBytes.resize(fileSize); + fin.read((char*)inBytes.data(), fileSize); + + fin.close(); +} + /** * Compare the given two images and return true if they match, * false otherwise */ -inline bool compareImages(const string& image1, const string& image2, int threshold) { - // Load images - Mat img1 = loadBinaryImage(image1, threshold); - Mat img2 = loadBinaryImage(image2, threshold); - +inline bool compareImages(const cv::Mat& img1, const cv::Mat& img2) { // treat two empty mat as identical if (img1.empty() && img2.empty()) { return true;