VideoCodec/Modules/Quantization/DCTCoefficientQuantizer.cpp
#include "DCTCoefficientQuantizer.h"
namespace VideoCodec
{
/*
* From JPEG for Luma:
*
* {16, 11, 10, 16, 24, 40, 51, 61,
* 12, 12, 14, 19, 26, 58, 60, 55,
* 14, 13, 16, 24, 40, 57, 69, 56,
* 14, 17, 22, 29, 51, 87, 80, 62,
* 18, 22, 37, 56, 68, 109, 103, 77,
* 24, 35, 55, 64, 81, 104, 113, 92,
* 49, 64, 78, 87, 103, 121, 120, 101,
* 72, 92, 95, 98, 112, 100, 103, 99};
*
* For chroma:
*
* {17, 18, 24, 47, 99, 99, 99, 99,
* 18, 21, 26, 66, 99, 99, 99, 99,
* 24, 26, 56, 99, 99, 99, 99, 99,
* 47, 66, 99, 99, 99, 99, 99, 99,
* 99, 99, 99, 99, 99, 99, 99, 99,
* 99, 99, 99, 99, 99, 99, 99, 99,
* 99, 99, 99, 99, 99, 99, 99, 99,
* 99, 99, 99, 99, 99, 99, 99, 99};
*
* The tables below come from the MPEG-4 standard.
*/
// Standardized quantization tables.
static int dctStandardQuantizationLumaInteger[64] =
{8, 17, 18, 19, 21, 23, 25, 27,
17, 18, 19, 21, 23, 25, 27, 28,
20, 21, 22, 23, 24, 26, 28, 30,
21, 22, 23, 24, 26, 28, 30, 32,
22, 23, 24, 26, 28, 30, 32, 35,
23, 24, 26, 28, 30, 32, 35, 38,
25, 26, 28, 30, 32, 35, 38, 41,
27, 28, 30, 32, 35, 38, 41, 45};
static int dctStandardQuantizationChromaInteger[64] =
{8, 17, 18, 19, 21, 23, 25, 27,
17, 18, 19, 21, 23, 25, 27, 28,
20, 21, 22, 23, 24, 26, 28, 30,
21, 22, 23, 24, 26, 28, 30, 32,
22, 23, 24, 26, 28, 30, 32, 35,
23, 24, 26, 28, 30, 32, 35, 38,
25, 26, 28, 30, 32, 35, 38, 41,
27, 28, 30, 32, 35, 38, 41, 45};
static int dctInterQuantizationInteger[64] =
{16, 17, 18, 19, 20, 21, 22, 23,
17, 18, 19, 20, 21, 22, 23, 24,
18, 19, 20, 21, 22, 23, 24, 25,
19, 20, 21, 22, 23, 24, 26, 27,
20, 21, 22, 23, 25, 26, 27, 28,
21, 22, 23, 24, 26, 27, 28, 30,
22, 23, 24, 26, 27, 28, 30, 31,
23, 24, 25, 27, 28, 30, 31, 33};
DCTCoefficientQuantizer::DCTCoefficientQuantizer() : Module(DCT_COEFFICIENT_QUANTIZER_MODULE_ID)
{
}
DCTCoefficientQuantizer::~DCTCoefficientQuantizer()
{
}
void DCTCoefficientQuantizer::SetQuantizationMethod(DCTCoefficientQuantizationMethod intraQuantizationMethod, DCTCoefficientQuantizationMethod interQuantizationMethod)
{
this->intraQuantizationMethod = intraQuantizationMethod;
this->interQuantizationMethod = interQuantizationMethod;
}
void DCTCoefficientQuantizer::SetIntraQuantizationMethod(DCTCoefficientQuantizationMethod intraQuantizationMethod)
{
this->intraQuantizationMethod = intraQuantizationMethod;
}
void DCTCoefficientQuantizer::SetInterQuantizationMethod(DCTCoefficientQuantizationMethod interQuantizationMethod)
{
this->interQuantizationMethod = interQuantizationMethod;
}
UnsignedByte DCTCoefficientQuantizer::GetInterQuantizationScale()
{
return this->interQuantizationScale;
}
UnsignedByte DCTCoefficientQuantizer::GetIntraQuantizationScale()
{
return this->intraQuantizationScale;
}
void DCTCoefficientQuantizer::SetInterQuantizationScale(UnsignedByte quantizationScale)
{
this->interQuantizationScale = quantizationScale;
}
void DCTCoefficientQuantizer::SetIntraQuantizationScale(UnsignedByte quantizationScale)
{
this->intraQuantizationScale = quantizationScale;
}
DCTCoefficientQuantizationMethod DCTCoefficientQuantizer::GetIntraQuantizationMethod()
{
return this->intraQuantizationMethod;
}
DCTCoefficientQuantizationMethod DCTCoefficientQuantizer::GetInterQuantizationMethod()
{
return this->interQuantizationMethod;
}
ConfigurationStatus DCTCoefficientQuantizer::Configure(ConfigurationElement* configuration)
{
// Quantization type selection is currently off -- use quantizationScale to set the scaling value for MPEG-4 quantization.
/*
if (configuration == NULL || configuration->GetValue("type") == NULL || *configuration->GetValue("type")->GetString() == string("quality"))
{
// Default quantization.
this->intraQuantizationMethod = DCT_QUANTIZATION_JPEG_STANDARD_QUALITY;
this->interQuantizationMethod = DCT_QUANTIZATION_INTER_STANDARD;
this->quantizerScale = 1;
}
*/
// Explicitly specified quantization type overrides scaling.
/*
if (configuration != NULL && configuration->GetValue("type") != NULL && configuration->GetValue("type")->GetType() == CONFIGURATION_STRING && *configuration->GetValue("type")->GetString() == string("normal"))
{
this->intraQuantizationMethod = DCT_QUANTIZATION_JPEG_STANDARD_QUALITY;
this->interQuantizationMethod = DCT_QUANTIZATION_INTER_STANDARD;
this->quantizerScale = 2;
}
*/
this->intraQuantizationMethod = DCT_QUANTIZATION_JPEG_STANDARD_QUALITY;
this->interQuantizationMethod = DCT_QUANTIZATION_INTER_STANDARD;
// User defined quantization scaling values for inter and intra frames.
if (configuration != NULL && configuration->GetValue("interQuantizationScale") != NULL && configuration->GetValue("interQuantizationScale")->GetType() == CONFIGURATION_INTEGER)
this->interQuantizationScale = (UnsignedByte)*configuration->GetValue("interQuantizationScale")->GetInteger();
else
this->interQuantizationScale = 1;
if (configuration != NULL && configuration->GetValue("intraQuantizationScale") != NULL && configuration->GetValue("intraQuantizationScale")->GetType() == CONFIGURATION_INTEGER)
this->intraQuantizationScale = (UnsignedByte)*configuration->GetValue("intraQuantizationScale")->GetInteger();
else
this->intraQuantizationScale = 1;
return MODULE_CONFIGURATION_OK;
}
void DCTCoefficientQuantizer::DequantizeDCTCoefficients(PictureInfo* picture, bool isInter)
{
if (picture->GetPixelLayout() != PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
Logger::LogMessage("Warning: Unexpected macroblock type in DCT coefficient dequantizer.");
if ((isInter && this->interQuantizationMethod == DCT_QUANTIZATION_NONE) || (!isInter && this->intraQuantizationMethod == DCT_QUANTIZATION_NONE))
{
Logger::LogMessage("Warning: No quantization selected.");
return;
}
SignedShort* transformed = picture->GetSignedShortData();
int blockRows = picture->GetBlockRows();
int blockColumns = picture->GetBlockColumns();
// Store the offset of the current macroblock.
if (isInter && this->interQuantizationMethod == DCT_QUANTIZATION_INTER_STANDARD)
{
unsigned char blockIndex = 0;
for (int offset = 0; offset < (blockRows * blockColumns * 64 * 6); offset++)
{
if (transformed[offset] != 0)
{
transformed[offset] = (this->interQuantizationScale * dctInterQuantizationInteger[blockIndex] *
(2 * transformed[offset] + (transformed[offset] < 0 ? -1 : (transformed[offset] > 0 ? 1 : 0)))
) >> 4;
if (transformed[offset] > 2047)
transformed[offset] = 2047;
else if (transformed[offset] < -2048)
transformed[offset] = -2048;
}
blockIndex++;
if (blockIndex == 64)
blockIndex = 0;
}
}
else if (!isInter && this->intraQuantizationMethod == DCT_QUANTIZATION_JPEG_STANDARD_QUALITY)
{
for (int offset = 0; offset < (blockRows * blockColumns * 64 * 6); offset += 64 * 6)
{
// Dequantize DC coefficients.
for (int b = 0; b < 6; b++)
transformed[offset + (b << 6)] <<= 3;
// Dequantize AC coefficients for each block.
this->DequantizeIntraACValues(transformed + offset + (0 << 6), dctStandardQuantizationLumaInteger);
this->DequantizeIntraACValues(transformed + offset + (1 << 6), dctStandardQuantizationLumaInteger);
this->DequantizeIntraACValues(transformed + offset + (2 << 6), dctStandardQuantizationLumaInteger);
this->DequantizeIntraACValues(transformed + offset + (3 << 6), dctStandardQuantizationLumaInteger);
this->DequantizeIntraACValues(transformed + offset + (4 << 6), dctStandardQuantizationChromaInteger);
this->DequantizeIntraACValues(transformed + offset + (5 << 6), dctStandardQuantizationChromaInteger);
}
}
else
{
// Unrecognized quantization method.
Logger::LogMessage("Warning: No dequantizaton was performed.");
}
}
inline void DCTCoefficientQuantizer::DequantizeIntraACValues(SignedShort* block, int* quantizationMatrix)
{
// Skip the DC value.
for (int i = 1; i < 64; i++)
{
// Scale this value up and round to the correct range.
block[i] = ((block[i] << 1) * this->intraQuantizationScale * quantizationMatrix[i]) >> 4;
if (block[i] > 2047)
block[i] = 2047;
else if (block[i] < -2048)
block[i] = -2048;
}
}
inline void DCTCoefficientQuantizer::QuantizeIntraACValues(SignedShort* block, int* quantizationMatrix)
{
// Skip the DC value.
for (int i = 1; i < 64; i++)
{
SignedShort dct = block[i];
if (dct != 0)
block[i] =
((dct << 4) + (dct < 0 ? -1 : (dct > 0 ? 1 : 0)) * quantizationMatrix[i] * this->intraQuantizationScale)
/ (this->intraQuantizationScale * quantizationMatrix[i] << 1);
}
}
void DCTCoefficientQuantizer::QuantizeDCTCoefficients(PictureInfo* picture, bool isInter)
{
if (picture->GetPixelLayout() != PIXEL_LAYOUT_MB_FOUR_TWO_ZERO)
Logger::LogMessage("Warning: DCT coefficient quantizer required 4:2:0 MB pixel layout.");
SignedShort* transformed = picture->GetSignedShortData();
int blockRows = picture->GetBlockRows();
int blockColumns = picture->GetBlockColumns();
if (isInter && this->interQuantizationMethod == DCT_QUANTIZATION_INTER_STANDARD)
{
unsigned char blockIndex = 0;
for (int offset = 0; offset < blockColumns * blockRows * 64 * 6; offset++)
{
if (transformed[offset] != 0)
transformed[offset] = (transformed[offset] << 3) / (dctInterQuantizationInteger[blockIndex] * this->interQuantizationScale);
blockIndex++;
if (blockIndex == 64)
blockIndex = 0;
}
}
else if (!isInter && this->intraQuantizationMethod == DCT_QUANTIZATION_JPEG_STANDARD_QUALITY)
{
for (int offset = 0; offset < (blockRows * blockColumns * 64 * 6); offset += 64 * 6)
{
// Quantize DC coefficients.
for (int b = 0; b < 6; b++)
transformed[offset + (b << 6)] >>= 3;
// Quantize AC coefficients for each block.
this->QuantizeIntraACValues(transformed + offset + (0 << 6), dctStandardQuantizationLumaInteger);
this->QuantizeIntraACValues(transformed + offset + (1 << 6), dctStandardQuantizationLumaInteger);
this->QuantizeIntraACValues(transformed + offset + (2 << 6), dctStandardQuantizationLumaInteger);
this->QuantizeIntraACValues(transformed + offset + (3 << 6), dctStandardQuantizationLumaInteger);
this->QuantizeIntraACValues(transformed + offset + (4 << 6), dctStandardQuantizationChromaInteger);
this->QuantizeIntraACValues(transformed + offset + (5 << 6), dctStandardQuantizationChromaInteger);
}
}
else
{
Logger::LogMessage("Warning: No quantization was performed.");
}
}
}