This is the final project of Video Signal Processing in NTHU, 2015, Spring, and the document is just a set of supplementary materials. It simply demonstrates how to use these source files to encode/decode an image, and how to integrate/compile the project. Because it is complex, I can only show how to use as clearly as possible. If you have any problem, please send me an e-mail. ([email protected])
-
Mac OSX [recommended] or UNIX (terminal and shell script used)
-
Xcode-build-tools [recommended] or g++ compiler
-
Matlab [recommended] or YUV viewer
Note that Windows has not been tested yet, so it might take lots of time encoding an image. Please wait for it, thanks.
If you are trying to run this project on the platform other than Mac OSX, please make sure your executable (.out, .exe) is in the directory/ImageCodec/
-
Root:
/ImageCodec/
-
Code:
/ImageCodec/code/
,/ImageCodec/libs/
-
Data:
/ImageCodec/data/
-
Generated:
/ImageCodec/results/
-
Report:
/ImageCodec/report/
Note that if user does not arrange files followed the structure mentioned above, it might cause some unknown errors.
-
run
cd ~/{YOUR_DIR}/ImageCodec/
-
Configuration in the sample code
-
check the file
/ImageCodec/code/ImageCodec_Demo.cpp
#define CONSTRAINT_BY_PSNR 0 #define CONSTRAINT_BY_BITRATE 1 #define SKIP_ENCODING -1 using namespace std; int main(int argc, const char * argv[]) { int constWay = {Config}; // <-- can be CONSTRAINT_BY_PSNR : Use PSNR as constraint CONSTRAINT_BY_BITRATE : Use bitrate as constraint SKIP_ENCODING : Skip encoding int opt = {Config}; // <-- can be 0: 1_1536x1024.yuv 1: 2_1024x768.yuv 2: 3_1000x728.yuv 3: 4_1000x1504.yuv
and
if (constWay == CONSTRAINT_BY_PSNR) { ss << PATHS[opt+4]; ss << "bitstream1.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMinPSNR({Config})->run(); ss.str(""); ss.clear(); ss << PATHS[opt+4]; ss << "bitstream2.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMinPSNR({Config})->run(); ss.str(""); ss.clear(); ss << PATHS[opt+4]; ss << "bitstream3.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMinPSNR({Config})->run(); } else if (constWay == CONSTRAINT_BY_BITRATE) { ss << PATHS[opt+4]; ss << "bitstream1.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMaxBitrate({Config})->run(); ss.str(""); ss.clear(); ss << PATHS[opt+4]; ss << "bitstream2.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMaxBitrate({Config})->run(); ss.str(""); ss.clear(); ss << PATHS[opt+4]; ss << "bitstream3.bin"; ImageCodec::encodeTo(ss.str())->withImage(img)->withMaxBitrate({Config})->run(); }
-
Replace the annotation
{Config}
with your specific configurations. -
run
./run.sh
under/ImageCodec/
-
-
Do the following commands on the terminal
- run
chmod +x ./run.sh
- key in your pwd
- run
./run.sh
- run
-
Use Matlab code to convert YUV to PNG
- run
genImage
under/ImageCodec/
on Matlab terminal
- run
-
Check the output files in
/ImageCodec/results/
- This API gives 2 ways to encode an image.
-
Specific upper bound of bitrate
YUVImage* img = YUVImage::import({image_path}) ->withFormat({image_format}) ->withSize({image_width}, {image_height}); PerformancePackage::load(img->getName()); ImageCodec::encodeTo({target_path})->withImage(img)->withMaxBitrate({max_bitrate})->run();
e.g.
YUVImage* img = YUVImage::import("./1_1536x1024.yuv") ->withFormat(YUVImage::FORMAT_4_2_0) ->withSize(1536, 1024); PerformancePackage::load(img->getName()); ImageCodec::encodeTo("./bitstream.bin")->withImage(img)->withMaxBitrate(0.75)->run(); PerformancePackage::save();
-
Specific lower bound of PSNR
YUVImage* img = YUVImage::import({image_path}) ->withFormat({image_format}) ->withSize({image_width}, {image_height}); PerformancePackage::load(img->getName()); ImageCodec::encodeTo({target_path})->withImage(img)->withMinPSNR({min_PSNR})->run(); PerformancePackage::save();
e.g.
YUVImage* img = YUVImage::import("./1_1536x1024.yuv") ->withFormat(YUVImage::FORMAT_4_2_0) ->withSize(1536, 1024); PerformancePackage::load(img->getName()); ImageCodec::encodeTo("./bitstream.bin")->withImage(img)->withMinPSNR(28)->run(); PerformancePackage::save();
-
Decoding a compressed file is easy.
ImageCodec::decode({bitstream_path})->saveTo({target_path})->run();
e.g.
ImageCodec::decode("./bitstream.bin")->saveTo("./decompressed.yuv")->run();
-
Importing 2 images and then call the following API.
double PSNR = img1->calPSNR(img2);
e.g.
YUVImage* img1 = YUVImage::import("./1_1536x1024.yuv") ->withFormat(YUVImage::FORMAT_4_2_0) ->withSize(1536, 1024); YUVImage* img2 = YUVImage::import("./decompressed.yuv") ->withFormat(YUVImage::FORMAT_4_2_0) ->withSize(1536, 1024); double PSNR = img1->calPSNR(img2);
-
Importing the original image, and then give the path of compressed file.
double bitrate = img->calBitrate({bitstream_path});
e.g.
YUVImage* img = YUVImage::import("./1_1536x1024.yuv") ->withFormat(YUVImage::FORMAT_4_2_0) ->withSize(1536, 1024); double bitrate = img->calBitrate("./bitstream.bin");
- Level 1: main coding system.
- Level 2: optionally applied, for compensating PSNR.
- Define the searching space. ( O(dsr x mk x pid x qres), very large! )
- Use all parameters to encode the file.
- Evaluate the bitrate and PSNR.
- Submit the result to
ParameterPackage
- Find the best parameters set under specific constraint.
To save the time re-searching same parameters at same image, an database/controller has been programmed for saving these parameters. It is /ImageCodec/libs/PerformancePackage.h
:
-
the parameters will be saved in
/ImageCodec/libs/PerformancePackageCache/
, that's why this project should be runned at the appropriate directory. (to avoid some potential problems) -
the database is programmed as singleton, which can be identified with 'name'. In this project, the parameters are stored in the database named from
YUVImage::getName()
i.e.
YUVImage* image = YUVImage::import(mImgPath) ->withSize(mWidth, mHeight) ->withFormat(YUVImage::FORMAT_4_2_0); PerformancePackage::load(image->getName());
-
Main component of this project: to encode/decode a file.
-
Encoding
ImageCodec::encodeTo({target_path}) ->withImage({your_image}) ->withMaxBitrate({max_bitrate}) ->run();
ImageCodec::encodeTo({target_path}) ->withImage({your_image}) ->withMinPSNR({min_PSNR}) ->run();
-
Decoding
ImageCodec::decode({bitstream_path}) ->saveTo({decompressed_path}) ->run();
-
-
An object that contains lots of functions/properties that an image has.
-
Importing an image
YUVImage* image = YUVImage::import({image_path}) ->withSize({image_width}, {image_height}) ->withFormat(YUVImage::FORMAT_4_2_0);
-
Initiate an empty image
YUVImage* image = YUVImage::emptyImage({assigned_path}, {image_width}, {image_height}, {image_format});
-
Save the image into a file
image->exportTo({tartget_path});
-
Properties
image->getWidth(DataLayer::Y); image->getHeight(DataLayer::Cr); image->getDataSize(DataLayer::Cb); image->getDataSize(); // get total size of image image->getFormat(); // FORMAT_4_2_0 | FORMAT_4_2_2 | FORMAT_4_4_4 image->getPath(); image->getName();
-
Data getters & setters
image->getYDataAt<int>(x, y); image->getCbDataAt<short>(x, y); image->getCrDataAt<long>(x, y); image->setYDataAt(x, y, (short)v); image->setCbDataAt(x, y, (short)v); image->setCrDataAt(x, y, (short)v);
-
Circular Addition/Subtraction
YUVImage* image3 = image1->add(image2); YUVImage* image4 = image1->diff(image2);
-
Evaluate difference between two images
double PSNR = image1->calPSNR(image2);
-
Evaluate the compressed bitrate
double bitrate = image1->calBitrate({bitstream_path});
-
-
A toolbox to process
YUVImage
: to apply k-means clustering.-
Clustering the image
YUVImageFactory* factory = YUVImageFactory::initWithImage(image) ->useQuantization() ->withYUVLevels({Y_clusters}, {U_clusters}, {V_clusters}) ->compress();
-
After clustering, all data in the image refer to cluster ID rather than magnitude. The clustered image with cluster ID can be obtained with
YUVImage* symbolImg = factory->getSymbolImage()
, and the clustered image with magnitude can be obtained withYUVImage* clusteredImg = factory->getImage()
-
Information after clustering
factory->getYLevel(); // get # of Y_clusters factory->getYSymbolAt(n); // get the n^th center of Y factory->getULevel(); factory->getUSymbolAt(n); factory->getVLevel(); factory->getVSymbolAt(n);
-
-
A toolbox to process
YUVImage
: to generate predicted residual image with a specific predictor ID.-
Directly getting a residual image
YUVImage* resImg = ImagePredictor::predictResidual(image, predictorID);
-
Use circular subtraction (e.g. 1 cirSubtr_5 7 = (1-7) % 5 = 4)
int circularN[3] = {64, 16, 8}; YUVImage* resImg = ImagePredictor::predictResidual(image, predictorID, circularN);
-
Predictor ID: (only 1-8 used)
-
-
A toolbox to do discrete cosine transform: 4x4-block supported so far. [not used]
-
Transformation of a vector
int v1[4] = {1, -1, 2, -2}; Transform::dct4<int>(v1); short v2[4] = {11, 1, -2, 1}; Transform::idct4<short>(v2);
-
Transformation of multiple vectors
int v1[12] = {1, 2, 1, 2, -1, 3, 15, 10, 0, 2, -3, -5}; Transform::dct4<int>(v1, 3); long v2[8] = {1254, -4, 11, 19, 28, 3, 2, 0}; Transform::idct4<long>(v2, 2);
-
-
A toolbox to do k-means clustering.
KmeansFactory<T>
: all data are stored in T type.KmeansInfo<T, centerT>
: all centers are stored in centerT type.
int data[65536] = {1, 4, 2, 8, 10, 16, ... }; KmeansFactory<int>* factory = KmeansFactory<int>::getInstance(); KmeansInfo<int, int>* info = factory->withRawData(data, 65536) ->clusterInto(4096) ->run<int>();
-
An object able to construct a canonical Huffman table with specific contents data.
HuffmanTable<T>
: all data are stored in T type.
char data[1024] = {0, 5, 123, ... }; HuffmanTable<short>* table = HuffmanTable::initWithSize(256) ->withData(data, 1024);
-
Get maximal word length and entries value
int WL_max = table->getMaxWordLength(); for (int i = 0; i < WL_max; i++) int nEntries = table->getNumEntriesWithWordLength(i+1);
-
Get symbol/probability at specific index
Symbol<T>* symb = table->getSymbolAt(n); double p = table->getProbabilityAt(n);
-
An object able to construct a Golomb-Rice table. [not used]
GolombRiceTable<T>
: all data are stored in T type
GolombRiceTable<int>* table = GolombRiceTable<int>::initWithSize({size}) ->withGroupSize({group_size}) ->init();
- Other getters are similar to HuffmanTable's.
-
A central controller/database to manage the efficiency and its corresponded parameters set.
-
Getting database with a specific name
PerformancePackage* pkg = PerformancePackage::getInstance(name);
-
Submitting a 'tried parameters set' to database
CompressionParameters* cp = Builder()->withArea(imageArea) ->withDownSampleScale(wscale, hscale) ->withMk_YUV(mk_y, mk_u, mk_v) ->withPredictorID(predictorID) ->withKmeansTableSize(N_k) ->withMk_TABLE(mk_table) ->withMaxWordLength(WL_max) ->withKmeansTableBitrate(kmeansTableBitrate) ->withAdjustBitrate(adjustBitrate) ->withResidualQuantizationConst(Q_res) ->withPSNR(PSNR) ->withAdjust(isWithAdjust); pkg->submit(cp);
-
After searching all parameters, the database should eliminate some higher-bitrate and lower-PSNR (higher cost, lower efficiency) parameters set.
pkg->anneal();
-
Save all data into the disk
PerformancePackage::save();
-
Load data with a specific name
PerformancePackage::load(name);
-
-
A toolbox to read a file in bits level.
-
Opening an input stream
BitReader* reader = BitReader::open({stream_path});
-
Reading data (big endian)
char v = reader->read(); // read a bit char* v = reader->read(size); long v = reader->read<long>(15); // read 15 bits and store in long short v = reader->read<short>(3);
-
Properties
int restBits = reader->remains(); int size = reader->getFileSize(); // in terms of bits not bytes
-
DO NOT FORGET TO CLOSE INPUT STREAM! (because it is singleton.)
reader->close();
-
-
A toolbox to write a file in bits level.
-
Opening the output stream
BitWriter* writer = BitWriter::open({stream_path});
-
Writing data
writer->write(0); char v[5] = {0, 1, 1, 1, 0}; writer->write(v, 5); int k = 18; writer->write<int>(k, 7); // write '0010010'
-
DO NOT FORGET TO CLOSE OUTPUT STREAM! (because it is singleton.)
writer->close();
-
-
An object that contains lots of functions/properties that a symbol has.
-
Symbol<T>
: the value of the symbol is stored in T type -
Getters
Symbol<short>* s = new Symbol<short>(v); short value = s->getValue(); std::vector<char> word = s->getWord(); string wordStr = s->getWordString(); int wordCount = s->getWordSize();
-