const Array2D inverse_quantise_block(const Array2D& block, int q) { // Construct a new array with same size as block Array2D inverseQuantisedBlock(block.ranges()); const int blockHeight = block.shape()[0]; const int blockWidth = block.shape()[1]; for (int y=0; y<blockHeight; ++y) { for (int x=0; x<blockWidth; ++x) { inverseQuantisedBlock[y][x] = scale(block[y][x], q); } } return inverseQuantisedBlock; }
const Array2D clip(const Array2D& values, const int min_value, const int max_value) { Array2D result(values.ranges()); const Index height = values.shape()[0]; const Index width = values.shape()[1]; for (int y=0; y<height; ++y) { for (int x=0; x<width; ++x) { if (values[y][x]<min_value) result[y][x] = min_value; else if (values[y][x]>max_value) result[y][x] = max_value; else result[y][x] = values[y][x]; } } return result; }
// Inverse quantise a block of quantised coefficients using an array of quantisers. // The block to be inverse quantised may correspond either to the transform of the whole picture // or to a subband. In the former case this function will inverse quantise slices, in the // latter case this function will inverse quantise codeblocks const Array2D inverse_quantise_block(const ConstView2D& block, const Array2D& qIndices) { // Construct a new array with same size as block Array2D invQuantisedBlock(block.ranges()); const int blockHeight = block.shape()[0]; const int blockWidth = block.shape()[1]; const int yBlocks = qIndices.shape()[0]; const int xBlocks = qIndices.shape()[1]; // Loop through the slices or codeblocks // Note Range(left, right) defines the half open range [left, right), // i.e. the rightmost element is not included for (int y=0, top=0, bottom=blockHeight/yBlocks; y<yBlocks; ++y, top=bottom, bottom=((y+1)*blockHeight/yBlocks) ) { for (int x=0, left=0, right=blockWidth/xBlocks; x<xBlocks; ++x, left=right, right=((x+1)*blockWidth/xBlocks) ) { const ArrayIndices2D sliceIndices = // Define the samples within the curent slice/codeblock indices[Range(top,bottom)][Range(left,right)]; invQuantisedBlock[sliceIndices] = inverse_quantise_block(block[sliceIndices], qIndices[y][x]); } } return invQuantisedBlock; }
// Quantise a subband in in-place transform order // This version of quantise_subbands assumes multiple quantisers per subband. // It may be used for either quantising slices or for quantising subbands with codeblocks const Array2D quantise_subbands(const Array2D& coefficients, const BlockVector& qIndices) { const Index transformHeight = coefficients.shape()[0]; const Index transformWidth = coefficients.shape()[1]; // TO DO: Check numberOfSubbands=3n+1 ? const int numberOfSubbands = qIndices.size(); const int waveletDepth = (numberOfSubbands-1)/3; Index stride, offset; // stride is subsampling factor, offset is subsampling phase Array2D result(coefficients.ranges()); // Create a view of the coefficients, representing the LL subband, quantise it, // then assign the result a view of the results array. This puts the quantised // LL subband into the result array in in-place transform order. // ArrayIndices2D objects specify the subset of array elements within a view, // that is they specify the subsampling factor and subsampling phase. stride = pow(2, waveletDepth); const ArrayIndices2D LLindices = // LLindices specifies the samples in the LL subband indices[Range(0,transformHeight,stride)][Range(0,transformWidth,stride)]; result[LLindices] = quantise_LLSubband(coefficients[LLindices], qIndices[0]); // Next quantise the other subbands // Note: Level numbers go from zero for the lowest ("DC") frequencies to depth for // the high frequencies. This corresponds to the convention in the VC-2 specification. // Subands go from zero ("DC") to numberOfSubbands-1 for HH at the highest level for (char level=1, band=1; level<=waveletDepth; ++level) { stride = pow(2, waveletDepth+1-level); offset = stride/2; // Create a view of coefficients corresponding to a subband, then quantise it //Quantise HL subband const ArrayIndices2D HLindices = // HLindices specifies the samples in the HL subband indices[Range(0,transformHeight,stride)][Range(offset,transformWidth,stride)]; result[HLindices] = quantise_block(coefficients[HLindices], qIndices[band++]); //Quantise LH subband const ArrayIndices2D LHindices = // LHindices specifies the samples in the LH subband indices[Range(offset,transformHeight,stride)][Range(0,transformWidth,stride)]; result[LHindices] = quantise_block(coefficients[LHindices], qIndices[band++]); //Quantise HH subband const ArrayIndices2D HHindices = // HHindices specifies the samples in the HH subband indices[Range(offset,transformHeight,stride)][Range(offset,transformWidth,stride)]; result[HHindices] = quantise_block(coefficients[HHindices], qIndices[band++]); } return result; }
// Splits a large 2D array into an array of smaller 2D arrays (blocks) // Note that if the number of blocks is not a sub-multiple of the input array dimensions then // the blocks will have different sizes! // yBlocks and xBlocks are the number of blocks in the vertical and horizontal dimension respectively. // Splits a picture into slices or a subband into codeblocks. const BlockArray split_into_blocks(const Array2D& picture, int yBlocks, int xBlocks) { // Define array of yBlocks by xBlocks BlockArray blocks(extents[yBlocks][xBlocks]); const int pictureHeight = picture.shape()[0]; const int pictureWidth = picture.shape()[1]; // Note Range(left, right) defines the half open range [left, right), // i.e. the rightmost element is not included for (int y=0, top=0, bottom=pictureHeight/yBlocks; y<yBlocks; ++y, top=bottom, bottom=((y+1)*pictureHeight/yBlocks) ) { for (int x=0, left=0, right=pictureWidth/xBlocks; x<xBlocks; ++x, left=right, right=((x+1)*pictureWidth/xBlocks) ) { // Assign region of picture to block blocks[y][x] = picture[indices[Range(top,bottom)][Range(left,right)]]; } } return blocks; }
static void medianCut( const Array2D &luminance, const Array2D &summedLuminance, MedianCutSampler::Projection projection, const Box2i &area, vector<Box2i> &areas, vector<V2f> ¢roids, int depth, int maxDepth ) { float radiansPerPixel = M_PI / (luminance.shape()[1]); if( depth==maxDepth ) { float totalEnergy = 0.0f; V2f position( 0.0f ); for( int y=area.min.y; y<=area.max.y; y++ ) { for( int x=area.min.x; x<=area.max.x; x++ ) { float e = luminance[x][y]; position += V2f( x, y ) * e; totalEnergy += e; } } position /= totalEnergy; centroids.push_back( position ); areas.push_back( area ); } else { // find cut dimension V2f size = area.size(); if( projection==MedianCutSampler::LatLong ) { float centreY = (area.max.y + area.min.y) / 2.0f; float centreAngle = (M_PI - radiansPerPixel) / 2.0f - centreY * radiansPerPixel; size.x *= cosf( centreAngle ); } int cutAxis = size.x > size.y ? 0 : 1; float e = energy( summedLuminance, area ); float halfE = e / 2.0f; Box2i lowArea = area; while( e > halfE ) { lowArea.max[cutAxis] -= 1; e = energy( summedLuminance, lowArea ); } Box2i highArea = area; highArea.min[cutAxis] = lowArea.max[cutAxis] + 1; medianCut( luminance, summedLuminance, projection, lowArea, areas, centroids, depth + 1, maxDepth ); medianCut( luminance, summedLuminance, projection, highArea, areas, centroids, depth + 1, maxDepth ); } }
// Inverse quantise an LL (DC) subband, including prediction // This version either inverse quantises the LL subband for low delay mode or // inverse quantises the LL subband for core syntax using codeblocks const Array2D inverse_quantise_LLSubband(const ConstView2D& llSubband, const Array2D& qIndices) { const int LLHeight = llSubband.shape()[0]; // Height of the LL subband const int LLWidth = llSubband.shape()[1]; // Width of the LL subband const int yBlocks = qIndices.shape()[0]; // Number of vertical slices/codeblocks in the LL subband const int xBlocks = qIndices.shape()[1]; // Number of horizontal slices/codeblocks in the LL subband Array2D invQuantisedLL(llSubband.ranges()); for (int y=0; y<LLHeight; ++y) { for (int x=0; x<LLWidth; ++x) { // TO DO: Implement more efficient calculation of yb/xb. // Calculate (y+1)*yBlocks by incrementing previous version by yBlocks // Do division by shift (width is always a power of 2) const int yb = ((y+1)*yBlocks-1)/LLHeight; // vertical slice/codeblock number const int xb = ((x+1)*xBlocks-1)/LLWidth; // horizontal slice/codeblock number const int prediction = predictDC(invQuantisedLL, y, x); invQuantisedLL[y][x] = scale(llSubband[y][x], qIndices[yb][xb])+prediction; } } return invQuantisedLL; }
// Get the shape of a 2D array const Shape2D shape(const Array2D& arg) { const Shape2D result = {{static_cast<Index>(arg.shape()[0]), static_cast<Index>(arg.shape()[1])}}; return result; }