Newer
Older

Karsten Suehring
committed
/* The copyright in this software is being made available under the BSD
* License, included below. This software may be subject to other third party
* and contributor rights, including patent rights, and no such rights are
* granted under this license.
*

Karsten Suehring
committed
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
\file EncSampleAdaptiveOffset.cpp
\brief estimation part of sample adaptive offset class
*/
#include "EncSampleAdaptiveOffset.h"
#include "CommonLib/UnitTools.h"
#include "CommonLib/dtrace_codingstruct.h"
#include "CommonLib/dtrace_buffer.h"
#include "CommonLib/CodingStructure.h"
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER

Karsten Suehring
committed
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
//! \ingroup EncoderLib
//! \{
#define SAOCtx(c) SubCtx( Ctx::Sao, c )
#if JVET_W0066_CCSAO
#include <algorithm>
struct SetIdxCount
{
uint8_t setIdx;
uint16_t count;
};
struct CtbCost
{
int16_t pos;
double cost;
};
bool compareSetIdxCount(SetIdxCount a, SetIdxCount b) { return a.count > b.count; }
bool compareCtbCost(CtbCost a, CtbCost b) { return a.cost < b.cost; }
#endif

Karsten Suehring
committed
//! rounding with IBDI
inline double xRoundIbdi2(int bitDepth, double x)
{
#if FULL_NBIT
return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5)));
#else
if (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) == 0)
return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5)));
else
return ((x) > 0) ? (int)(((int)(x) + (1 << (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) - 1)))
/ (1 << DISTORTION_PRECISION_ADJUSTMENT(bitDepth)))
: ((int)(((int)(x) - (1 << (DISTORTION_PRECISION_ADJUSTMENT(bitDepth) - 1)))
/ (1 << DISTORTION_PRECISION_ADJUSTMENT(bitDepth))));
#endif
}
inline double xRoundIbdi(int bitDepth, double x)
{
return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ;
}
EncSampleAdaptiveOffset::EncSampleAdaptiveOffset()
{
m_CABACEstimator = NULL;
::memset( m_saoDisabledRate, 0, sizeof( m_saoDisabledRate ) );

Karsten Suehring
committed
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
}
EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset()
{
}
void EncSampleAdaptiveOffset::createEncData(bool isPreDBFSamplesUsed, uint32_t numCTUsPic)
{
//statistics
const uint32_t sizeInCtus = numCTUsPic;
m_statData.resize( sizeInCtus );
for(uint32_t i=0; i< sizeInCtus; i++)
{
m_statData[i] = new SAOStatData*[MAX_NUM_COMPONENT];
for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
m_statData[i][compIdx] = new SAOStatData[NUM_SAO_NEW_TYPES];
}
}
if(isPreDBFSamplesUsed)
{
m_preDBFstatData.resize( sizeInCtus );
for(uint32_t i=0; i< sizeInCtus; i++)
{
m_preDBFstatData[i] = new SAOStatData*[MAX_NUM_COMPONENT];
for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
m_preDBFstatData[i][compIdx] = new SAOStatData[NUM_SAO_NEW_TYPES];
}
}
}
for(int typeIdc=0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++)
{
m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
if(isPreDBFSamplesUsed)
{
switch(typeIdc)
{
case SAO_TYPE_EO_0:
{
m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
m_skipLinesB[COMPONENT_Y ][typeIdc]= 3;
m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 1;
}
break;
case SAO_TYPE_EO_90:
{
m_skipLinesR[COMPONENT_Y ][typeIdc]= 4;
m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 2;
m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
}
break;
case SAO_TYPE_EO_135:
case SAO_TYPE_EO_45:
{
m_skipLinesR[COMPONENT_Y ][typeIdc]= 5;
m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 3;
m_skipLinesB[COMPONENT_Y ][typeIdc]= 4;
m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 2;
}
break;
case SAO_TYPE_BO:
{
m_skipLinesR[COMPONENT_Y ][typeIdc]= 4;
m_skipLinesR[COMPONENT_Cb][typeIdc]= m_skipLinesR[COMPONENT_Cr][typeIdc]= 2;
m_skipLinesB[COMPONENT_Y ][typeIdc]= 3;
m_skipLinesB[COMPONENT_Cb][typeIdc]= m_skipLinesB[COMPONENT_Cr][typeIdc]= 1;
}
break;
default:
{
THROW("Not a supported type");
}
}
}
}
#if JVET_W0066_CCSAO
if (m_createdEnc)
{
return;
}
m_createdEnc = true;
for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
{
Che-Wei Kuo
committed
m_ccSaoStatData [i] = new CcSaoStatData[m_numCTUsInPic];
#if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
m_ccSaoStatDataEdge[i] = new CcSaoStatData[m_numCTUsInPic];
#endif
#if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
Che-Wei Kuo
committed
#if JVET_AE0151_CCSAO_HISTORY_OFFSETS_AND_EXT_EO
int numStatsEdge = m_numCTUsInPic * MAX_CCSAO_EDGE_DIR * MAX_CCSAO_EDGE_THR * MAX_CCSAO_BAND_IDC * MAX_NUM_COMPONENT * MAX_CCSAO_EDGE_IDC;
Che-Wei Kuo
committed
m_ccSaoStatDataEdgePre = new CcSaoStatData[numStatsEdge];
#else
for (int comp = Y_C; comp < N_C; comp++)
{
m_ccSaoStatDataEdgeNew[comp] = new CcSaoStatData[m_numCTUsInPic * (CCSAO_EDGE_BAND_NUM_Y + CCSAO_EDGE_BAND_NUM_C)
* CCSAO_QUAN_NUM * CCSAO_EDGE_TYPE];
}
Che-Wei Kuo
committed
#endif
m_bestCcSaoControl = new uint8_t[m_numCTUsInPic];
m_tempCcSaoControl = new uint8_t[m_numCTUsInPic];
m_initCcSaoControl = new uint8_t[m_numCTUsInPic];
for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
{
m_trainingDistortion[i] = new int64_t[m_numCTUsInPic];
}
#endif

Karsten Suehring
committed
}
void EncSampleAdaptiveOffset::destroyEncData()
{
for(uint32_t i=0; i< m_statData.size(); i++)
{
for(uint32_t compIdx=0; compIdx< MAX_NUM_COMPONENT; compIdx++)
{
delete[] m_statData[i][compIdx];
}
delete[] m_statData[i];
}
m_statData.clear();
for(int i=0; i< m_preDBFstatData.size(); i++)
{
for(int compIdx=0; compIdx< MAX_NUM_COMPONENT; compIdx++)
{
delete[] m_preDBFstatData[i][compIdx];
}
delete[] m_preDBFstatData[i];
}
m_preDBFstatData.clear();
#if JVET_W0066_CCSAO
if (!m_createdEnc)
{
return;
}
m_createdEnc = false;
for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
{
Che-Wei Kuo
committed
if (m_ccSaoStatData [i]) { delete[] m_ccSaoStatData [i]; m_ccSaoStatData [i] = nullptr; }
#if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
Che-Wei Kuo
committed
if (m_ccSaoStatDataEdge[i]) { delete[] m_ccSaoStatDataEdge[i]; m_ccSaoStatDataEdge[i] = nullptr; }
#if JVET_Y0106_CCSAO_EDGE_CLASSIFIER
Che-Wei Kuo
committed
#if JVET_AE0151_CCSAO_HISTORY_OFFSETS_AND_EXT_EO
if (m_ccSaoStatDataEdgePre) { delete[] m_ccSaoStatDataEdgePre; m_ccSaoStatDataEdgePre = nullptr; }
#else
for (int comp = Y_C; comp < N_C; comp++)
{
if (m_ccSaoStatDataEdgeNew[comp])
{
delete[] m_ccSaoStatDataEdgeNew[comp];
m_ccSaoStatDataEdgeNew[comp] = nullptr;
}
}
Che-Wei Kuo
committed
#endif
if (m_bestCcSaoControl) { delete[] m_bestCcSaoControl; m_bestCcSaoControl = nullptr; }
if (m_tempCcSaoControl) { delete[] m_tempCcSaoControl; m_tempCcSaoControl = nullptr; }
if (m_initCcSaoControl) { delete[] m_initCcSaoControl; m_initCcSaoControl = nullptr; }
for (int i = 0; i < MAX_CCSAO_SET_NUM; i++)
{
if (m_trainingDistortion[i]) { delete[] m_trainingDistortion[i]; m_trainingDistortion[i] = nullptr; }
}
#endif

Karsten Suehring
committed
}
void EncSampleAdaptiveOffset::initCABACEstimator( CABACEncoder* cabacEncoder, CtxCache* ctxCache, Slice* pcSlice )
{
m_CABACEstimator = cabacEncoder->getCABACEstimator( pcSlice->getSPS() );

Karsten Suehring
committed
m_CABACEstimator->initCtxModels( *pcSlice );
m_CABACEstimator->resetBits();
}

Christian Helmrich
committed
void EncSampleAdaptiveOffset::SAOProcess( CodingStructure& cs, bool* sliceEnabled, const double* lambdas,
#if ENABLE_QPA
const double lambdaChromaWeight,
#endif
const bool bTestSAODisableAtPictureLevel, const double saoEncodingRate, const double saoEncodingRateChroma, const bool isPreDBFSamplesUsed, bool isGreedyMergeEncoding
,BIFCabacEst* bifCABACEstimator

Karsten Suehring
committed
{
#if ALF_SAO_TRUE_ORG && !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER

Karsten Suehring
committed
PelUnitBuf org = cs.getOrgBuf();

Karsten Suehring
committed
PelUnitBuf res = cs.getRecoBuf();
PelUnitBuf src = m_tempBuf;
#if !JVET_V0094_BILATERAL_FILTER && !JVET_X0071_CHROMA_BILATERAL_FILTER
// Moved until after the bilateral filter has been initialized

Karsten Suehring
committed
memcpy(m_lambda, lambdas, sizeof(m_lambda));

Karsten Suehring
committed
src.copyFrom(res);
BifParams& bifParams = cs.picture->getBifParam(COMPONENT_Y);
int width = cs.picture->lwidth();
int height = cs.picture->lheight();
int block_width = pcv.maxCUWidth;
int block_height = pcv.maxCUHeight;
int width_in_blocks = width / block_width + (width % block_width != 0);
int height_in_blocks = height / block_height + (height % block_height != 0);
bifParams.numBlocks = width_in_blocks * height_in_blocks;
bifParams.ctuOn.resize(bifParams.numBlocks);
std::fill(bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 0);
// Currently no RDO to figure out if we should turn CTUs on or off
bifParams.frmOn = 1;
bifParams.allCtuOn = 1;
if( bifParams.frmOn == 0 )
{
std::fill( bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 0 );
}
else if( bifParams.allCtuOn )
{
std::fill( bifParams.ctuOn.begin(), bifParams.ctuOn.end(), 1 );
}
//double MseNoFltFrame = 0;
//double MseFltDefFrame = 0;
//double MseFltDefSwitchFrame = 0;
//int CtuIdx = 0;
#endif
#if JVET_X0071_CHROMA_BILATERAL_FILTER
if(cs.pps->getUseChromaBIF())
{
const PreCalcValues& pcv = *cs.pcv;
BifParams& bifParamsCb = cs.picture->getBifParam( COMPONENT_Cb );
BifParams& bifParamsCr = cs.picture->getBifParam( COMPONENT_Cr );
int width = cs.picture->lwidth();
int height = cs.picture->lheight();
int blockWidth = pcv.maxCUWidth;
int blockHeight = pcv.maxCUHeight;
int widthInBlocks = width / blockWidth + (width % blockWidth != 0);
int heightInBlocks = height / blockHeight + (height % blockHeight != 0);
bifParamsCb.numBlocks = widthInBlocks * heightInBlocks;
bifParamsCr.numBlocks = widthInBlocks * heightInBlocks;
bifParamsCb.ctuOn.resize( bifParamsCb.numBlocks);
bifParamsCr.ctuOn.resize( bifParamsCr.numBlocks);
std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 0);
std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 0);
bifParamsCb.frmOn = 0;
bifParamsCr.frmOn = 0;
bifParamsCb.allCtuOn = 0;
bifParamsCr.allCtuOn = 0;
std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 0 );
else if ( bifParamsCb.allCtuOn)
std::fill( bifParamsCb.ctuOn.begin(), bifParamsCb.ctuOn.end(), 1 );
std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 0 );
else if ( bifParamsCr.allCtuOn)
std::fill( bifParamsCr.ctuOn.begin(), bifParamsCr.ctuOn.end(), 1 );
}
}
#endif
#if JVET_X0071_CHROMA_BILATERAL_FILTER
BilateralFilter bilateralFilter;
if(!cs.sps->getSAOEnabledFlag() && (cs.pps->getUseBIF() || cs.pps->getUseChromaBIF()))
bilateralFilter.create();
if( cs.pps->getUseBIF() )
{
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator ); // Filters from src to res
}
if( cs.pps->getUseChromaBIF() )
{
//Cb
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
}
memcpy(m_lambda, lambdas, sizeof(m_lambda));
#else
BilateralFilter bilateralFilter;
// Special case when SAO = 0 and BIF = 1.
// Just filter reconstruction and return.
// No need to estimate SAO parameters.
if(!cs.sps->getSAOEnabledFlag() && cs.pps->getUseBIF())
{
bilateralFilter.create();
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator); // Filters from src to res
bilateralFilter.destroy();
return;
}
memcpy(m_lambda, lambdas, sizeof(m_lambda));
#endif
#else
#if JVET_X0071_CHROMA_BILATERAL_FILTER
BilateralFilter bilateralFilter;
if(!cs.sps->getSAOEnabledFlag() && cs.pps->getUseChromaBIF())
{
bilateralFilter.create();
//Cb
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
bilateralFilter.destroy();
return;
}
memcpy(m_lambda, lambdas, sizeof(m_lambda));
#else
//do nothing
#endif

Karsten Suehring
committed
//collect statistics
#if JVET_X0071_CHROMA_BILATERAL_FILTER
if( cs.pps->getUseBIF() || cs.pps->getUseChromaBIF() )
{
bilateralFilter.create();
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator ); // Filters from src to res'
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
bilateralFilter.destroy();
}
else
{
getStatistics(m_statData, org, src, src, cs);
}
#else
//apply BILAT to res
if(cs.pps->getUseBIF())
{
bilateralFilter.create();
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Y, cs, src, bifCABACEstimator); // Filters from src to res
getStatistics(m_statData, org, src, res, cs);
getStatistics(m_statData, org, src, src, cs);
#endif
#else
#if JVET_X0071_CHROMA_BILATERAL_FILTER
if(cs.pps->getUseChromaBIF())
{
bilateralFilter.create();
//Cb
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cb, cs, src, bifCABACEstimator );
bilateralFilter.bilateralFilterPicRDOperCTU( COMPONENT_Cr, cs, src, bifCABACEstimator );
getStatistics(m_statData, org, src, res, cs);
bilateralFilter.destroy();
}
else
{
getStatistics(m_statData, org, src, src, cs);
}

Karsten Suehring
committed
getStatistics(m_statData, org, src, cs);

Karsten Suehring
committed
if(isPreDBFSamplesUsed)
{
addPreDBFStatistics(m_statData);
}
#if JVET_X0071_CHROMA_BILATERAL_FILTER
if(cs.pps->getUseBIF() || cs.pps->getUseChromaBIF())
{
res.copyFrom(src);
}
#else
//undo BILAT on res
if(cs.pps->getUseBIF())
res.copyFrom(src);
#endif
#else
#if JVET_X0071_CHROMA_BILATERAL_FILTER
if(cs.pps->getUseChromaBIF())
{
res.copyFrom(src);
}
#else
//do nothing
#endif

Karsten Suehring
committed
//slice on/off
decidePicParams(*cs.slice, sliceEnabled, saoEncodingRate, saoEncodingRateChroma);
//block on/off
std::vector<SAOBlkParam> reconParams(cs.pcv->sizeInCtus);

Christian Helmrich
committed
decideBlkParams( cs, sliceEnabled, m_statData, src, res, &reconParams[0], cs.picture->getSAO(), bTestSAODisableAtPictureLevel,
#if ENABLE_QPA
lambdaChromaWeight,
#endif
saoEncodingRate, saoEncodingRateChroma, isGreedyMergeEncoding );

Karsten Suehring
committed
DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC())));
DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y);
DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb);
DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr);
DTRACE ( g_trace_ctx, D_CRC, "SAO" );
DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() );
}
void EncSampleAdaptiveOffset::getPreDBFStatistics(CodingStructure& cs)
{
#if ALF_SAO_TRUE_ORG
PelUnitBuf org = cs.getTrueOrgBuf();
#else

Karsten Suehring
committed
PelUnitBuf org = cs.getOrgBuf();

Karsten Suehring
committed
PelUnitBuf rec = cs.getRecoBuf();
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
rec, rec,
#else
rec,
#endif
cs, true);

Karsten Suehring
committed
}
void EncSampleAdaptiveOffset::addPreDBFStatistics(std::vector<SAOStatData**>& blkStats)
{
const uint32_t numCTUsPic = (uint32_t)blkStats.size();
for(uint32_t n=0; n< numCTUsPic; n++)
{
for(uint32_t compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
for(uint32_t typeIdc=0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++)
{
blkStats[n][compIdx][typeIdc] += m_preDBFstatData[n][compIdx][typeIdc];
}
}
}
}
void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv,
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
PelUnitBuf& bifYuv,
#endif
CodingStructure& cs, bool isCalculatePreDeblockSamples)

Karsten Suehring
committed
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
{
bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail;
const PreCalcValues& pcv = *cs.pcv;
const int numberOfComponents = getNumberValidComponents(pcv.chrFormat);
size_t lineBufferSize = pcv.maxCUWidth + 1;
if (m_signLineBuf1.size() != lineBufferSize)
{
m_signLineBuf1.resize(lineBufferSize);
m_signLineBuf2.resize(lineBufferSize);
}
int ctuRsAddr = 0;
for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight )
{
for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth )
{
const uint32_t width = (xPos + pcv.maxCUWidth > pcv.lumaWidth) ? (pcv.lumaWidth - xPos) : pcv.maxCUWidth;
const uint32_t height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight;
const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) );
deriveLoopFilterBoundaryAvailibility(cs, area.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail );
//NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities.
//For simplicity, here only picture boundaries are considered.
isRightAvail = (xPos + pcv.maxCUWidth < pcv.lumaWidth );
isBelowAvail = (yPos + pcv.maxCUHeight < pcv.lumaHeight);
isAboveRightAvail = ((yPos > 0) && (isRightAvail));
Sheng-Yen Lin
committed
int numHorVirBndry = 0, numVerVirBndry = 0;
int horVirBndryPos[] = { -1,-1,-1 };
int verVirBndryPos[] = { -1,-1,-1 };
int horVirBndryPosComp[] = { -1,-1,-1 };
int verVirBndryPosComp[] = { -1,-1,-1 };
bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries(xPos, yPos, width, height, numHorVirBndry, numVerVirBndry, horVirBndryPos, verVirBndryPos, cs.picHeader );
Sheng-Yen Lin
committed

Karsten Suehring
committed
for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
{
const ComponentID compID = ComponentID(compIdx);
const CompArea& compArea = area.block( compID );
int srcStride = srcYuv.get(compID).stride;
Pel* srcBlk = srcYuv.get(compID).bufAt( compArea );
int orgStride = orgYuv.get(compID).stride;
Pel* orgBlk = orgYuv.get(compID).bufAt( compArea );
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
int bifStride = bifYuv.get(compID).stride;
Pel* bifBlk = bifYuv.get(compID).bufAt( compArea );
#endif
Sheng-Yen Lin
committed
for (int i = 0; i < numHorVirBndry; i++)
{
horVirBndryPosComp[i] = (horVirBndryPos[i] >> ::getComponentScaleY(compID, area.chromaFormat)) - compArea.y;
Sheng-Yen Lin
committed
}
for (int i = 0; i < numVerVirBndry; i++)
{
verVirBndryPosComp[i] = (verVirBndryPos[i] >> ::getComponentScaleX(compID, area.chromaFormat)) - compArea.x;
Sheng-Yen Lin
committed
}

Karsten Suehring
committed
getBlkStats(compID, cs.sps->getBitDepth(toChannelType(compID)), blkStats[ctuRsAddr][compID]
#if JVET_V0094_BILATERAL_FILTER || JVET_X0071_CHROMA_BILATERAL_FILTER
bifBlk, bifStride,
#endif
srcStride, orgStride, compArea.width, compArea.height

Karsten Suehring
committed
, isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail
, isCalculatePreDeblockSamples
, isCtuCrossedByVirtualBoundaries, horVirBndryPosComp, verVirBndryPosComp, numHorVirBndry, numVerVirBndry

Karsten Suehring
committed
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
);
}
ctuRsAddr++;
}
}
}
void EncSampleAdaptiveOffset::decidePicParams(const Slice& slice, bool* sliceEnabled, const double saoEncodingRate, const double saoEncodingRateChroma)
{
if ( slice.getPendingRasInit() )
{ // reset
for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
for (int tempLayer = 1; tempLayer < MAX_TLAYER; tempLayer++)
{
m_saoDisabledRate[compIdx][tempLayer] = 0.0;
}
}
}
const int picTempLayer = slice.getDepth();
//decide sliceEnabled[compIdx]
const int numberOfComponents = m_numberOfComponents;
for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++)
{
sliceEnabled[compIdx] = false;
}
for (int compIdx = 0; compIdx < numberOfComponents; compIdx++)
{
// reset flags & counters
sliceEnabled[compIdx] = true;
if (saoEncodingRate>0.0)
{
if (saoEncodingRateChroma>0.0)
{
// decide slice-level on/off based on previous results
if( (picTempLayer > 0)
&& (m_saoDisabledRate[compIdx][picTempLayer-1] > ((compIdx==COMPONENT_Y) ? saoEncodingRate : saoEncodingRateChroma)) )
{
sliceEnabled[compIdx] = false;
}
}
else
{
// decide slice-level on/off based on previous results
if( (picTempLayer > 0)
&& (m_saoDisabledRate[COMPONENT_Y][0] > saoEncodingRate) )
{
sliceEnabled[compIdx] = false;
}
}
}
}
}
int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData)
{
int64_t dist = 0;
int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth);
switch(typeIdc)
{
case SAO_TYPE_EO_0:
case SAO_TYPE_EO_90:
case SAO_TYPE_EO_135:
case SAO_TYPE_EO_45:
{
for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++)
{
dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift);
}
}
break;
case SAO_TYPE_BO:
{
for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++)
{
int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ;
dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift);
}
}
break;
default:
{
THROW("Not a supported type");
}
}
return dist;
}
inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift)
{
return (( count*offset*offset-diffSum*offset*2 ) >> shift);
}
inline int EncSampleAdaptiveOffset::estIterOffset(int typeIdx, double lambda, int offsetInput, int64_t count, int64_t diffSum, int shift, int bitIncrease, int64_t& bestDist, double& bestCost, int offsetTh )
{
int iterOffset, tempOffset;
int64_t tempDist, tempRate;
double tempCost, tempMinCost;
int offsetOutput = 0;
iterOffset = offsetInput;
// Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit. entropy coder can be used to measure the exact rate here.
tempMinCost = lambda;
while (iterOffset != 0)
{
// Calculate the bits required for signaling the offset
tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1);
if (abs((int)iterOffset)==offsetTh) //inclusive
{
tempRate --;
}
// Do the dequantization before distortion calculation
tempOffset = iterOffset << bitIncrease;
tempDist = estSaoDist( count, tempOffset, diffSum, shift);
tempCost = ((double)tempDist + lambda * (double) tempRate);
if(tempCost < tempMinCost)
{
tempMinCost = tempCost;
offsetOutput = iterOffset;
bestDist = tempDist;
bestCost = tempCost;
}
iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1);
}
return offsetOutput;
}
void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo)
{
int bitDepth = channelBitDepth;
int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth);
int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth); //inclusive
::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
//derive initial offsets
int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES);
for(int classIdx=0; classIdx< numClasses; classIdx++)
{
if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN) )
{
continue; //offset will be zero
}
if(statData.count[classIdx] == 0)
{
continue; //offset will be zero
}
quantOffsets[classIdx] =
(int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] << DISTORTION_PRECISION_ADJUSTMENT(bitDepth))
/ (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx]));
quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]);
}
// adjust offsets
switch(typeIdc)
{
case SAO_TYPE_EO_0:
case SAO_TYPE_EO_90:
case SAO_TYPE_EO_135:
case SAO_TYPE_EO_45:
{
int64_t classDist;
double classCost;
for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++)
{
if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0)
{
quantOffsets[classIdx] =0;
}
if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0)
{
quantOffsets[classIdx] =0;
}
if(classIdx==SAO_CLASS_EO_HALF_PEAK && quantOffsets[classIdx] > 0)
{
quantOffsets[classIdx] =0;
}
if(classIdx==SAO_CLASS_EO_FULL_PEAK && quantOffsets[classIdx] > 0)
{
quantOffsets[classIdx] =0;
}
if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
{
quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh );
}
}
typeAuxInfo =0;
}
break;
case SAO_TYPE_BO:
{
int64_t distBOClasses[NUM_SAO_BO_CLASSES];
double costBOClasses[NUM_SAO_BO_CLASSES];
::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES);
for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++)
{
costBOClasses[classIdx]= m_lambda[compIdx];
if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
{
quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh );
}
}
//decide the starting band index
double minCost = MAX_DOUBLE, cost;
for(int band=0; band< NUM_SAO_BO_CLASSES- 4+ 1; band++)
{
cost = costBOClasses[band ];
cost += costBOClasses[band+1];
cost += costBOClasses[band+2];
cost += costBOClasses[band+3];
if(cost < minCost)
{
minCost = cost;
typeAuxInfo = band;
}
}
//clear those unused classes
int clearQuantOffset[NUM_SAO_BO_CLASSES];
::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES);
for(int i=0; i< 4; i++)
{
int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES;
clearQuantOffset[band] = quantOffsets[band];
}
::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES);
}
break;
default:
{
THROW("Not a supported type");
}
}
}
void EncSampleAdaptiveOffset::deriveModeNewRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], bool* sliceEnabled, std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost )
{
double minCost, cost;
uint64_t previousFracBits;
const int numberOfComponents = m_numberOfComponents;
int64_t dist[MAX_NUM_COMPONENT], modeDist[MAX_NUM_COMPONENT];
SAOOffset testOffset[MAX_NUM_COMPONENT];
int invQuantOffset[MAX_NUM_SAO_CLASSES];
for(int comp=0; comp < MAX_NUM_COMPONENT; comp++)
{
modeDist[comp] = 0;
}
//pre-encode merge flags
modeParam[COMPONENT_Y].modeIdc = SAO_MODE_OFF;
const TempCtx ctxStartBlk ( m_ctxCache, SAOCtx( m_CABACEstimator->getCtx() ) );

Karsten Suehring
committed
m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true );
const TempCtx ctxStartLuma ( m_ctxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
TempCtx ctxBestLuma ( m_ctxCache );

Karsten Suehring
committed
//------ luma --------//
const ComponentID compIdx = COMPONENT_Y;
//"off" case as initial cost
modeParam[compIdx].modeIdc = SAO_MODE_OFF;
m_CABACEstimator->resetBits();
m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths.recon[CHANNEL_TYPE_LUMA] );
modeDist[compIdx] = 0;
minCost = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits());
ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
if( sliceEnabled[compIdx] )

Karsten Suehring
committed
{
for( int typeIdc = 0; typeIdc < NUM_SAO_NEW_TYPES; typeIdc++ )

Karsten Suehring
committed
{
testOffset[compIdx].modeIdc = SAO_MODE_NEW;
testOffset[compIdx].typeIdc = typeIdc;

Karsten Suehring
committed
//derive coded offset
deriveOffsets( compIdx, bitDepths.recon[CHANNEL_TYPE_LUMA], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo );

Karsten Suehring
committed
//inversed quantized offsets
invertQuantOffsets( compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset );

Karsten Suehring
committed
//get distortion
dist[compIdx] = getDistortion( bitDepths.recon[CHANNEL_TYPE_LUMA], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc] );

Karsten Suehring
committed
//get rate
m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma );
m_CABACEstimator->resetBits();
m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths.recon[CHANNEL_TYPE_LUMA] );
double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
cost = ( double ) dist[compIdx] + m_lambda[compIdx] * rate;
if( cost < minCost )
{
minCost = cost;
modeDist[compIdx] = dist[compIdx];
modeParam[compIdx] = testOffset[compIdx];
ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );

Karsten Suehring
committed
}
}
}
m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );

Karsten Suehring
committed
//------ chroma --------//

Karsten Suehring
committed
cost = 0;
previousFracBits = 0;
m_CABACEstimator->resetBits();
for(uint32_t componentIndex = COMPONENT_Cb; componentIndex < numberOfComponents; componentIndex++)