/src/vvenc/source/Lib/EncoderLib/EncSampleAdaptiveOffset.cpp
Line | Count | Source |
1 | | /* ----------------------------------------------------------------------------- |
2 | | The copyright in this software is being made available under the Clear BSD |
3 | | License, included below. No patent rights, trademark rights and/or |
4 | | other Intellectual Property Rights other than the copyrights concerning |
5 | | the Software are granted under this license. |
6 | | |
7 | | The Clear BSD License |
8 | | |
9 | | Copyright (c) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors. |
10 | | All rights reserved. |
11 | | |
12 | | Redistribution and use in source and binary forms, with or without modification, |
13 | | are permitted (subject to the limitations in the disclaimer below) provided that |
14 | | the following conditions are met: |
15 | | |
16 | | * Redistributions of source code must retain the above copyright notice, |
17 | | this list of conditions and the following disclaimer. |
18 | | |
19 | | * Redistributions in binary form must reproduce the above copyright |
20 | | notice, this list of conditions and the following disclaimer in the |
21 | | documentation and/or other materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the copyright holder nor the names of its |
24 | | contributors may be used to endorse or promote products derived from this |
25 | | software without specific prior written permission. |
26 | | |
27 | | NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY |
28 | | THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
29 | | CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
31 | | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
32 | | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
33 | | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
34 | | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
35 | | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER |
36 | | IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
37 | | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
38 | | POSSIBILITY OF SUCH DAMAGE. |
39 | | |
40 | | |
41 | | ------------------------------------------------------------------------------------------- */ |
42 | | |
43 | | |
44 | | /** |
45 | | \file EncSampleAdaptiveOffset.cpp |
46 | | \brief estimation part of sample adaptive offset class |
47 | | */ |
48 | | |
49 | | #include "EncSampleAdaptiveOffset.h" |
50 | | #include "CommonLib/UnitTools.h" |
51 | | #include "CommonLib/dtrace_codingstruct.h" |
52 | | #include "CommonLib/dtrace_buffer.h" |
53 | | #include "CommonLib/CodingStructure.h" |
54 | | #include <string.h> |
55 | | #include <stdlib.h> |
56 | | #include <math.h> |
57 | | #include "vvenc/vvencCfg.h" |
58 | | |
59 | | //! \ingroup EncoderLib |
60 | | //! \{ |
61 | | |
62 | | namespace vvenc { |
63 | | |
64 | | |
65 | 0 | #define SAOCtx(c) SubCtx( Ctx::Sao, c ) |
66 | | |
67 | | |
68 | | //! rounding with IBDI |
69 | | inline double xRoundIbdi2(int bitDepth, double x) |
70 | 0 | { |
71 | 0 | return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5))); |
72 | 0 | } |
73 | | |
74 | | inline double xRoundIbdi(int bitDepth, double x) |
75 | 0 | { |
76 | 0 | return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ; |
77 | 0 | } |
78 | | |
79 | | |
80 | | EncSampleAdaptiveOffset::EncSampleAdaptiveOffset() |
81 | 0 | : m_CABACEstimator( nullptr ) |
82 | 0 | , m_CtxCache ( nullptr ) |
83 | 0 | { |
84 | 0 | } |
85 | | |
86 | | EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset() |
87 | 0 | { |
88 | 0 | } |
89 | | |
90 | | void EncSampleAdaptiveOffset::init( const VVEncCfg& encCfg ) |
91 | 0 | { |
92 | 0 | m_EncCfg = &encCfg; |
93 | |
|
94 | 0 | if ( encCfg.m_bUseSAO ) |
95 | 0 | { |
96 | 0 | SampleAdaptiveOffset::init( encCfg.m_internChromaFormat, encCfg.m_CTUSize, encCfg.m_CTUSize, encCfg.m_log2SaoOffsetScale[CH_L], encCfg.m_log2SaoOffsetScale[CH_C] ); |
97 | 0 | } |
98 | 0 | } |
99 | | |
100 | | void EncSampleAdaptiveOffset::initSlice( const Slice* slice ) |
101 | 0 | { |
102 | 0 | memcpy( m_lambda, slice->getLambdas(), sizeof( m_lambda ) ); |
103 | 0 | } |
104 | | |
105 | | void EncSampleAdaptiveOffset::setCtuEncRsrc( CABACWriter* cabacEstimator, CtxCache* ctxCache ) |
106 | 0 | { |
107 | 0 | m_CABACEstimator = cabacEstimator; |
108 | 0 | m_CtxCache = ctxCache; |
109 | 0 | } |
110 | | |
111 | | void EncSampleAdaptiveOffset::disabledRate( CodingStructure& cs, double saoDisabledRate[ MAX_NUM_COMP ][ VVENC_MAX_TLAYER ], SAOBlkParam* reconParams, const double saoEncodingRate, const double saoEncodingRateChroma, const ChromaFormat& chromaFormat ) |
112 | 0 | { |
113 | 0 | if ( saoEncodingRate > 0.0 ) |
114 | 0 | { |
115 | 0 | const PreCalcValues& pcv = *cs.pcv; |
116 | 0 | const int numberOfComponents = getNumberValidComponents( chromaFormat ); |
117 | 0 | const int picTempLayer = cs.slice->TLayer; |
118 | 0 | int numCtusForSAOOff[MAX_NUM_COMP]; |
119 | |
|
120 | 0 | for (int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
121 | 0 | { |
122 | 0 | numCtusForSAOOff[compIdx] = 0; |
123 | 0 | for( int ctuRsAddr=0; ctuRsAddr< pcv.sizeInCtus; ctuRsAddr++) |
124 | 0 | { |
125 | 0 | if( reconParams[ctuRsAddr][compIdx].modeIdc == SAO_MODE_OFF) |
126 | 0 | { |
127 | 0 | numCtusForSAOOff[compIdx]++; |
128 | 0 | } |
129 | 0 | } |
130 | 0 | } |
131 | 0 | if (saoEncodingRateChroma > 0.0) |
132 | 0 | { |
133 | 0 | for (int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
134 | 0 | { |
135 | 0 | saoDisabledRate[compIdx][picTempLayer] = (double)numCtusForSAOOff[compIdx]/(double)pcv.sizeInCtus; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | else if (picTempLayer == 0) |
139 | 0 | { |
140 | 0 | saoDisabledRate[COMP_Y][0] = (double)(numCtusForSAOOff[COMP_Y]+numCtusForSAOOff[COMP_Cb]+numCtusForSAOOff[COMP_Cr])/(double)(pcv.sizeInCtus *3); |
141 | 0 | } |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | void EncSampleAdaptiveOffset::decidePicParams( const CodingStructure& cs, double saoDisabledRate[ MAX_NUM_COMP ][ VVENC_MAX_TLAYER ], bool saoEnabled[ MAX_NUM_COMP ], const double saoEncodingRate, const double saoEncodingRateChroma, const ChromaFormat& chromaFormat ) |
146 | 0 | { |
147 | 0 | const Slice& slice = *cs.slice; |
148 | 0 | const int numberOfComponents = getNumberValidComponents( chromaFormat ); |
149 | | |
150 | | // reset |
151 | 0 | if( slice.pendingRasInit ) |
152 | 0 | { |
153 | 0 | for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ ) |
154 | 0 | { |
155 | 0 | for( int tempLayer = 1; tempLayer < VVENC_MAX_TLAYER; tempLayer++ ) |
156 | 0 | { |
157 | 0 | saoDisabledRate[ compIdx ][ tempLayer ] = 0.0; |
158 | 0 | } |
159 | 0 | } |
160 | 0 | } |
161 | |
|
162 | 0 | for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ ) |
163 | 0 | { |
164 | 0 | saoEnabled[ compIdx ] = false; |
165 | 0 | } |
166 | |
|
167 | 0 | const int picTempLayer = slice.TLayer; |
168 | 0 | for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ ) |
169 | 0 | { |
170 | | // enable per default |
171 | 0 | saoEnabled[ compIdx ] = true; |
172 | |
|
173 | 0 | if( saoEncodingRate > 0.0 ) |
174 | 0 | { |
175 | 0 | if( saoEncodingRateChroma > 0.0 ) |
176 | 0 | { |
177 | | // decide slice-level on/off based on previous results |
178 | 0 | if( ( picTempLayer > 0 ) |
179 | 0 | && ( saoDisabledRate[ compIdx ][ picTempLayer - 1 ] > ( ( compIdx == COMP_Y ) ? saoEncodingRate : saoEncodingRateChroma ) ) ) |
180 | 0 | { |
181 | 0 | saoEnabled[ compIdx ] = false; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | else |
185 | 0 | { |
186 | | // decide slice-level on/off based on previous results |
187 | 0 | if( ( picTempLayer > 0 ) |
188 | 0 | && ( saoDisabledRate[ COMP_Y ][ 0 ] > saoEncodingRate ) ) |
189 | 0 | { |
190 | 0 | saoEnabled[ compIdx ] = false; |
191 | 0 | } |
192 | 0 | } |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | void EncSampleAdaptiveOffset::storeCtuReco( CodingStructure& cs, const UnitArea& ctuArea, const int ctuX, const int ctuY ) |
198 | 0 | { |
199 | 0 | const int STORE_CTU_INCREASE = 8; |
200 | 0 | Position lPos( ctuArea.lx() + STORE_CTU_INCREASE, ctuArea.ly() + STORE_CTU_INCREASE ); |
201 | 0 | Size lSize( ctuArea.lwidth(), ctuArea.lheight() ); |
202 | |
|
203 | 0 | const bool tileBdryClip = cs.pps->getNumTiles() > 1 && !cs.pps->loopFilterAcrossTilesEnabled; |
204 | 0 | int startX = 0; |
205 | 0 | int startY = 0; |
206 | 0 | if( tileBdryClip ) |
207 | 0 | { |
208 | 0 | startX = cs.pps->tileColBd[cs.pps->ctuToTileCol[ctuX]] << cs.pcv->maxCUSizeLog2; |
209 | 0 | startY = cs.pps->tileRowBd[cs.pps->ctuToTileRow[ctuY]] << cs.pcv->maxCUSizeLog2; |
210 | 0 | } |
211 | |
|
212 | 0 | if ( ctuArea.lx() == startX ) |
213 | 0 | { |
214 | 0 | lPos.x = ctuArea.lx(); |
215 | 0 | lSize.width += STORE_CTU_INCREASE; |
216 | 0 | } |
217 | 0 | if ( ctuArea.ly() == startY ) |
218 | 0 | { |
219 | 0 | lPos.y = ctuArea.ly(); |
220 | 0 | lSize.height += STORE_CTU_INCREASE; |
221 | 0 | } |
222 | |
|
223 | 0 | int clipX = cs.pcv->lumaWidth - lPos.x; |
224 | 0 | int clipY = cs.pcv->lumaHeight - lPos.y; |
225 | 0 | if( tileBdryClip ) |
226 | 0 | { |
227 | 0 | clipX = cs.pps->tileColBdRgt[cs.pps->ctuToTileCol[ctuX]] - lPos.x; |
228 | 0 | clipY = cs.pps->tileRowBdBot[cs.pps->ctuToTileRow[ctuY]] - lPos.y; |
229 | 0 | } |
230 | 0 | lSize.clipSize( clipX, clipY ); |
231 | |
|
232 | 0 | const UnitArea relocArea( ctuArea.chromaFormat, Area( lPos, lSize ) ); |
233 | 0 | Picture& pic = *cs.picture; |
234 | 0 | PelUnitBuf recoYuv = pic.getRecoBuf().subBuf( relocArea ); |
235 | 0 | PelUnitBuf tempYuv = pic.getSaoBuf().subBuf( relocArea ); |
236 | 0 | tempYuv.copyFrom( recoYuv ); |
237 | 0 | } |
238 | | |
239 | | void EncSampleAdaptiveOffset::getCtuStatistics( CodingStructure& cs, std::vector<SAOStatData**>& saoStatistics, const UnitArea& ctuArea, const int ctuRsAddr ) |
240 | 0 | { |
241 | 0 | const PreCalcValues& pcv = *cs.pcv; |
242 | 0 | const int numberOfComponents = getNumberValidComponents( pcv.chrFormat ); |
243 | 0 | bool isLeftAvail = false; |
244 | 0 | bool isRightAvail = false; |
245 | 0 | bool isAboveAvail = false; |
246 | 0 | bool isBelowAvail = false; |
247 | 0 | bool isAboveLeftAvail = false; |
248 | 0 | bool isAboveRightAvail = false; |
249 | |
|
250 | 0 | deriveLoopFilterBoundaryAvailibility( cs, ctuArea.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail ); |
251 | | |
252 | | // NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities. |
253 | | // For simplicity, here only picture boundaries are considered. |
254 | |
|
255 | 0 | isRightAvail = ( ctuArea.Y().x + pcv.maxCUSize < pcv.lumaWidth ); |
256 | 0 | isBelowAvail = ( ctuArea.Y().y + pcv.maxCUSize < pcv.lumaHeight ); |
257 | 0 | isAboveRightAvail = ( ( ctuArea.Y().y > 0 ) && ( isRightAvail ) ); |
258 | |
|
259 | 0 | CHECK( !cs.pps->loopFilterAcrossSlicesEnabled, "Not implemented" ); |
260 | 0 | if( cs.pps->getNumTiles() > 1 && !cs.pps->loopFilterAcrossTilesEnabled ) |
261 | 0 | { |
262 | 0 | const int ctuX = ctuArea.lx() >> cs.pcv->maxCUSizeLog2; |
263 | 0 | const int ctuY = ctuArea.ly() >> cs.pcv->maxCUSizeLog2; |
264 | 0 | isRightAvail = isRightAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 1, 0 ); |
265 | 0 | isBelowAvail = isBelowAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 0, 1 ); |
266 | 0 | isAboveRightAvail = isAboveRightAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 1,-1 ); |
267 | 0 | } |
268 | | |
269 | | //VirtualBoundaries vb; |
270 | | //bool isCtuCrossedByVirtualBoundaries = vb.isCrossedByVirtualBoundaries(xPos, yPos, width, height, cs.slice->pps); |
271 | |
|
272 | 0 | for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ ) |
273 | 0 | { |
274 | 0 | const ComponentID compID = ComponentID( compIdx ); |
275 | 0 | const CompArea& compArea = ctuArea.block( compID ); |
276 | |
|
277 | 0 | PelBuf srcBuf = cs.picture->getSaoBuf().get( compID ); |
278 | 0 | PelBuf orgBuf = cs.picture->getOrigBuf().get( compID ); |
279 | |
|
280 | 0 | getBlkStats( compID, |
281 | 0 | cs.sps->bitDepths[ toChannelType( compID ) ], |
282 | 0 | saoStatistics[ ctuRsAddr ][ compID ], |
283 | 0 | srcBuf.bufAt( compArea ), |
284 | 0 | orgBuf.bufAt( compArea ), |
285 | 0 | srcBuf.stride, |
286 | 0 | orgBuf.stride, |
287 | 0 | compArea.width, |
288 | 0 | compArea.height, |
289 | 0 | isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail |
290 | 0 | ); |
291 | 0 | } |
292 | 0 | } |
293 | | |
294 | | void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv, CodingStructure& cs ) |
295 | 0 | { |
296 | 0 | bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail; |
297 | |
|
298 | 0 | const PreCalcValues& pcv = *cs.pcv; |
299 | 0 | const int numberOfComponents = getNumberValidComponents(pcv.chrFormat); |
300 | |
|
301 | 0 | size_t lineBufferSize = pcv.maxCUSize + 1; |
302 | 0 | if (m_signLineBuf1.size() != lineBufferSize) |
303 | 0 | { |
304 | 0 | m_signLineBuf1.resize(lineBufferSize); |
305 | 0 | m_signLineBuf2.resize(lineBufferSize); |
306 | 0 | } |
307 | |
|
308 | 0 | int ctuRsAddr = 0; |
309 | 0 | for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUSize ) |
310 | 0 | { |
311 | 0 | for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUSize ) |
312 | 0 | { |
313 | 0 | const uint32_t width = (xPos + pcv.maxCUSize > pcv.lumaWidth) ? (pcv.lumaWidth - xPos) : pcv.maxCUSize; |
314 | 0 | const uint32_t height = (yPos + pcv.maxCUSize > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUSize; |
315 | 0 | const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) ); |
316 | |
|
317 | 0 | deriveLoopFilterBoundaryAvailibility(cs, area.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail ); |
318 | | |
319 | | //NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities. |
320 | | //For simplicity, here only picture boundaries are considered. |
321 | |
|
322 | 0 | isRightAvail = (xPos + pcv.maxCUSize < pcv.lumaWidth ); |
323 | 0 | isBelowAvail = (yPos + pcv.maxCUSize < pcv.lumaHeight); |
324 | 0 | isAboveRightAvail = ((yPos > 0) && (isRightAvail)); |
325 | |
|
326 | 0 | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
327 | 0 | { |
328 | 0 | const ComponentID compID = ComponentID(compIdx); |
329 | 0 | const CompArea& compArea = area.block( compID ); |
330 | |
|
331 | 0 | int srcStride = srcYuv.get(compID).stride; |
332 | 0 | Pel* srcBlk = srcYuv.get(compID).bufAt( compArea ); |
333 | |
|
334 | 0 | int orgStride = orgYuv.get(compID).stride; |
335 | 0 | Pel* orgBlk = orgYuv.get(compID).bufAt( compArea ); |
336 | |
|
337 | 0 | getBlkStats(compID, cs.sps->bitDepths[toChannelType(compID)], blkStats[ctuRsAddr][compID] |
338 | 0 | , srcBlk, orgBlk, srcStride, orgStride, compArea.width, compArea.height |
339 | 0 | , isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail ); |
340 | 0 | } |
341 | 0 | ctuRsAddr++; |
342 | 0 | } |
343 | 0 | } |
344 | 0 | } |
345 | | |
346 | | void EncSampleAdaptiveOffset::decideCtuParams( CodingStructure& cs, const std::vector<SAOStatData**>& saoStatistics, const bool saoEnabled[ MAX_NUM_COMP ], const bool allBlksDisabled, const UnitArea& ctuArea, const int ctuRsAddr, SAOBlkParam* reconParams, SAOBlkParam* codedParams ) |
347 | 0 | { |
348 | 0 | const PreCalcValues& pcv = *cs.pcv; |
349 | 0 | const Slice& slice = *cs.slice; |
350 | 0 | const int ctuPosX = ctuRsAddr % pcv.widthInCtus; |
351 | 0 | const int ctuPosY = ctuRsAddr / pcv.widthInCtus; |
352 | | |
353 | | // reset CABAC estimator |
354 | 0 | if( m_EncCfg->m_ensureWppBitEqual |
355 | 0 | && m_EncCfg->m_numThreads < 1 |
356 | 0 | && ctuPosX == 0 |
357 | 0 | && ctuPosY > 0 ) |
358 | 0 | { |
359 | 0 | m_CABACEstimator->initCtxModels( slice ); |
360 | 0 | } |
361 | | |
362 | | // check disabled |
363 | 0 | if( allBlksDisabled ) |
364 | 0 | { |
365 | 0 | codedParams[ ctuRsAddr ].reset(); |
366 | 0 | return; |
367 | 0 | } |
368 | | |
369 | | // get merge list |
370 | 0 | SAOBlkParam* mergeList[ NUM_SAO_MERGE_TYPES ] = { NULL }; |
371 | 0 | getMergeList( cs, ctuRsAddr, reconParams, mergeList ); |
372 | |
|
373 | 0 | const TempCtx ctxStart( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
374 | 0 | TempCtx ctxBest ( m_CtxCache ); |
375 | |
|
376 | 0 | SAOBlkParam modeParam; |
377 | 0 | double minCost = MAX_DOUBLE; |
378 | 0 | double modeCost = MAX_DOUBLE; |
379 | 0 | for( int mode = 1; mode < NUM_SAO_MODES; mode++ ) |
380 | 0 | { |
381 | 0 | if( mode > 1 ) |
382 | 0 | { |
383 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxStart ); |
384 | 0 | } |
385 | 0 | switch( mode ) |
386 | 0 | { |
387 | 0 | case SAO_MODE_NEW: |
388 | 0 | { |
389 | 0 | deriveModeNewRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost ); |
390 | 0 | } |
391 | 0 | break; |
392 | 0 | case SAO_MODE_MERGE: |
393 | 0 | { |
394 | 0 | deriveModeMergeRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost ); |
395 | 0 | } |
396 | 0 | break; |
397 | 0 | default: |
398 | 0 | { |
399 | 0 | THROW( "Not a supported SAO mode." ); |
400 | 0 | } |
401 | 0 | } |
402 | | |
403 | 0 | if( modeCost < minCost ) |
404 | 0 | { |
405 | 0 | minCost = modeCost; |
406 | 0 | codedParams[ ctuRsAddr ] = modeParam; |
407 | 0 | ctxBest = SAOCtx( m_CABACEstimator->getCtx() ); |
408 | 0 | } |
409 | 0 | } |
410 | | |
411 | | // apply reconstructed offsets |
412 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxBest ); |
413 | 0 | reconParams[ ctuRsAddr ] = codedParams[ ctuRsAddr ]; |
414 | |
|
415 | 0 | reconstructBlkSAOParam( reconParams[ ctuRsAddr ], mergeList ); |
416 | |
|
417 | 0 | Picture& pic = *cs.picture; |
418 | 0 | offsetCTU( ctuArea, pic.getSaoBuf(), cs.getRecoBuf(), reconParams[ ctuRsAddr ], cs ); |
419 | 0 | } |
420 | | |
421 | | int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData) |
422 | 0 | { |
423 | 0 | int64_t dist = 0; |
424 | 0 | int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth); |
425 | |
|
426 | 0 | switch(typeIdc) |
427 | 0 | { |
428 | 0 | case SAO_TYPE_EO_0: |
429 | 0 | case SAO_TYPE_EO_90: |
430 | 0 | case SAO_TYPE_EO_135: |
431 | 0 | case SAO_TYPE_EO_45: |
432 | 0 | { |
433 | 0 | for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++) |
434 | 0 | { |
435 | 0 | dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift); |
436 | 0 | } |
437 | 0 | } |
438 | 0 | break; |
439 | 0 | case SAO_TYPE_BO: |
440 | 0 | { |
441 | 0 | for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++) |
442 | 0 | { |
443 | 0 | int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ; |
444 | 0 | dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift); |
445 | 0 | } |
446 | 0 | } |
447 | 0 | break; |
448 | 0 | default: |
449 | 0 | { |
450 | 0 | THROW("Not a supported type"); |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | 0 | return dist; |
455 | 0 | } |
456 | | |
457 | | inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift) |
458 | 0 | { |
459 | 0 | return (( count*offset*offset-diffSum*offset*2 ) >> shift); |
460 | 0 | } |
461 | | |
462 | | |
463 | | 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 ) |
464 | 0 | { |
465 | 0 | int iterOffset, tempOffset; |
466 | 0 | int64_t tempDist, tempRate; |
467 | 0 | double tempCost, tempMinCost; |
468 | 0 | int offsetOutput = 0; |
469 | 0 | iterOffset = offsetInput; |
470 | | // 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. |
471 | 0 | tempMinCost = lambda; |
472 | 0 | while (iterOffset != 0) |
473 | 0 | { |
474 | | // Calculate the bits required for signaling the offset |
475 | 0 | tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1); |
476 | 0 | if (abs((int)iterOffset)==offsetTh) //inclusive |
477 | 0 | { |
478 | 0 | tempRate --; |
479 | 0 | } |
480 | | // Do the dequantization before distortion calculation |
481 | 0 | tempOffset = iterOffset * (1<< bitIncrease); |
482 | 0 | tempDist = estSaoDist( count, tempOffset, diffSum, shift); |
483 | 0 | tempCost = ((double)tempDist + lambda * (double) tempRate); |
484 | 0 | if(tempCost < tempMinCost) |
485 | 0 | { |
486 | 0 | tempMinCost = tempCost; |
487 | 0 | offsetOutput = iterOffset; |
488 | 0 | bestDist = tempDist; |
489 | 0 | bestCost = tempCost; |
490 | 0 | } |
491 | 0 | iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1); |
492 | 0 | } |
493 | 0 | return offsetOutput; |
494 | 0 | } |
495 | | |
496 | | void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo) |
497 | 0 | { |
498 | 0 | int bitDepth = channelBitDepth; |
499 | 0 | int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth); |
500 | 0 | int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth); //inclusive |
501 | |
|
502 | 0 | ::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES); |
503 | | |
504 | | //derive initial offsets |
505 | 0 | int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES); |
506 | 0 | for(int classIdx=0; classIdx< numClasses; classIdx++) |
507 | 0 | { |
508 | 0 | if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN) ) |
509 | 0 | { |
510 | 0 | continue; //offset will be zero |
511 | 0 | } |
512 | | |
513 | 0 | if(statData.count[classIdx] == 0) |
514 | 0 | { |
515 | 0 | continue; //offset will be zero |
516 | 0 | } |
517 | 0 | #if ( DISTORTION_PRECISION_ADJUSTMENT(x) == 0 ) |
518 | 0 | quantOffsets[classIdx] = |
519 | 0 | (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] ) / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx])); |
520 | 0 | quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]); |
521 | | #else |
522 | | quantOffsets[classIdx] = |
523 | | (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] << DISTORTION_PRECISION_ADJUSTMENT(bitDepth)) |
524 | | / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx])); |
525 | | quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]); |
526 | | #endif |
527 | 0 | } |
528 | | |
529 | | // adjust offsets |
530 | 0 | switch(typeIdc) |
531 | 0 | { |
532 | 0 | case SAO_TYPE_EO_0: |
533 | 0 | case SAO_TYPE_EO_90: |
534 | 0 | case SAO_TYPE_EO_135: |
535 | 0 | case SAO_TYPE_EO_45: |
536 | 0 | { |
537 | 0 | int64_t classDist; |
538 | 0 | double classCost; |
539 | 0 | for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++) |
540 | 0 | { |
541 | 0 | if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0) |
542 | 0 | { |
543 | 0 | quantOffsets[classIdx] =0; |
544 | 0 | } |
545 | 0 | if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0) |
546 | 0 | { |
547 | 0 | quantOffsets[classIdx] =0; |
548 | 0 | } |
549 | 0 | if(classIdx==SAO_CLASS_EO_HALF_PEAK && quantOffsets[classIdx] > 0) |
550 | 0 | { |
551 | 0 | quantOffsets[classIdx] =0; |
552 | 0 | } |
553 | 0 | if(classIdx==SAO_CLASS_EO_FULL_PEAK && quantOffsets[classIdx] > 0) |
554 | 0 | { |
555 | 0 | quantOffsets[classIdx] =0; |
556 | 0 | } |
557 | |
|
558 | 0 | if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero |
559 | 0 | { |
560 | 0 | quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh ); |
561 | 0 | } |
562 | 0 | } |
563 | |
|
564 | 0 | typeAuxInfo =0; |
565 | 0 | } |
566 | 0 | break; |
567 | 0 | case SAO_TYPE_BO: |
568 | 0 | { |
569 | 0 | int64_t distBOClasses[NUM_SAO_BO_CLASSES]; |
570 | 0 | double costBOClasses[NUM_SAO_BO_CLASSES]; |
571 | 0 | ::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES); |
572 | 0 | for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++) |
573 | 0 | { |
574 | 0 | costBOClasses[classIdx]= m_lambda[compIdx]; |
575 | 0 | if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero |
576 | 0 | { |
577 | 0 | quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh ); |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | | //decide the starting band index |
582 | 0 | double minCost = MAX_DOUBLE, cost; |
583 | 0 | for(int band=0; band< NUM_SAO_BO_CLASSES; band++) |
584 | 0 | { |
585 | 0 | cost = costBOClasses[(band )%NUM_SAO_BO_CLASSES]; |
586 | 0 | cost += costBOClasses[(band+1)%NUM_SAO_BO_CLASSES]; |
587 | 0 | cost += costBOClasses[(band+2)%NUM_SAO_BO_CLASSES]; |
588 | 0 | cost += costBOClasses[(band+3)%NUM_SAO_BO_CLASSES]; |
589 | |
|
590 | 0 | if(cost < minCost) |
591 | 0 | { |
592 | 0 | minCost = cost; |
593 | 0 | typeAuxInfo = band; |
594 | 0 | } |
595 | 0 | } |
596 | | //clear those unused classes |
597 | 0 | int clearQuantOffset[NUM_SAO_BO_CLASSES]; |
598 | 0 | ::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES); |
599 | 0 | for(int i=0; i< 4; i++) |
600 | 0 | { |
601 | 0 | int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES; |
602 | 0 | clearQuantOffset[band] = quantOffsets[band]; |
603 | 0 | } |
604 | 0 | ::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES); |
605 | 0 | } |
606 | 0 | break; |
607 | 0 | default: |
608 | 0 | { |
609 | 0 | THROW("Not a supported type"); |
610 | 0 | } |
611 | 0 | } |
612 | 0 | } |
613 | | |
614 | | void EncSampleAdaptiveOffset::deriveModeNewRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], const bool* sliceEnabled, const std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost ) |
615 | 0 | { |
616 | 0 | double minCost, cost; |
617 | 0 | uint64_t previousFracBits; |
618 | 0 | const int numberOfComponents = m_numberOfComponents; |
619 | |
|
620 | 0 | int64_t dist[MAX_NUM_COMP], modeDist[MAX_NUM_COMP]; |
621 | 0 | SAOOffset testOffset[MAX_NUM_COMP]; |
622 | 0 | int invQuantOffset[MAX_NUM_SAO_CLASSES]; |
623 | 0 | for(int comp=0; comp < MAX_NUM_COMP; comp++) |
624 | 0 | { |
625 | 0 | modeDist[comp] = 0; |
626 | 0 | } |
627 | | |
628 | | //pre-encode merge flags |
629 | 0 | modeParam[COMP_Y].modeIdc = SAO_MODE_OFF; |
630 | 0 | const TempCtx ctxStartBlk ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
631 | 0 | m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true ); |
632 | 0 | const TempCtx ctxStartLuma ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
633 | 0 | TempCtx ctxBestLuma ( m_CtxCache ); |
634 | | |
635 | | //------ luma --------// |
636 | 0 | { |
637 | 0 | const ComponentID compIdx = COMP_Y; |
638 | | //"off" case as initial cost |
639 | 0 | modeParam[compIdx].modeIdc = SAO_MODE_OFF; |
640 | 0 | m_CABACEstimator->resetBits(); |
641 | 0 | m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] ); |
642 | 0 | modeDist[compIdx] = 0; |
643 | 0 | minCost = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits()); |
644 | 0 | ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() ); |
645 | 0 | if(sliceEnabled[compIdx]) |
646 | 0 | { |
647 | 0 | for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++) |
648 | 0 | { |
649 | 0 | testOffset[compIdx].modeIdc = SAO_MODE_NEW; |
650 | 0 | testOffset[compIdx].typeIdc = typeIdc; |
651 | | |
652 | | //derive coded offset |
653 | 0 | deriveOffsets(compIdx, bitDepths[CH_L], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo); |
654 | | |
655 | | //inversed quantized offsets |
656 | 0 | invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset); |
657 | | |
658 | | //get distortion |
659 | 0 | dist[compIdx] = getDistortion(bitDepths[CH_L], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc]); |
660 | | |
661 | | //get rate |
662 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma ); |
663 | 0 | m_CABACEstimator->resetBits(); |
664 | 0 | m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] ); |
665 | 0 | double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
666 | 0 | cost = (double)dist[compIdx] + m_lambda[compIdx]*rate; |
667 | 0 | if(cost < minCost) |
668 | 0 | { |
669 | 0 | minCost = cost; |
670 | 0 | modeDist[compIdx] = dist[compIdx]; |
671 | 0 | modeParam[compIdx]= testOffset[compIdx]; |
672 | 0 | ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() ); |
673 | 0 | } |
674 | 0 | } |
675 | 0 | } |
676 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma ); |
677 | 0 | } |
678 | | |
679 | | //------ chroma --------// |
680 | | //"off" case as initial cost |
681 | 0 | cost = 0; |
682 | 0 | previousFracBits = 0; |
683 | 0 | m_CABACEstimator->resetBits(); |
684 | 0 | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
685 | 0 | { |
686 | 0 | const ComponentID component = ComponentID(componentIndex); |
687 | |
|
688 | 0 | modeParam[component].modeIdc = SAO_MODE_OFF; |
689 | 0 | modeDist [component] = 0; |
690 | 0 | m_CABACEstimator->sao_offset_pars( modeParam[component], component, sliceEnabled[component], bitDepths[CH_C] ); |
691 | 0 | const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits(); |
692 | 0 | cost += m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits); |
693 | 0 | previousFracBits = currentFracBits; |
694 | 0 | } |
695 | |
|
696 | 0 | minCost = cost; |
697 | | |
698 | | //doesn't need to store cabac status here since the whole CTU parameters will be re-encoded at the end of this function |
699 | |
|
700 | 0 | for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++) |
701 | 0 | { |
702 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma ); |
703 | 0 | m_CABACEstimator->resetBits(); |
704 | 0 | previousFracBits = 0; |
705 | 0 | cost = 0; |
706 | |
|
707 | 0 | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
708 | 0 | { |
709 | 0 | const ComponentID component = ComponentID(componentIndex); |
710 | 0 | if(!sliceEnabled[component]) |
711 | 0 | { |
712 | 0 | testOffset[component].modeIdc = SAO_MODE_OFF; |
713 | 0 | dist[component]= 0; |
714 | 0 | continue; |
715 | 0 | } |
716 | 0 | testOffset[component].modeIdc = SAO_MODE_NEW; |
717 | 0 | testOffset[component].typeIdc = typeIdc; |
718 | | |
719 | | //derive offset & get distortion |
720 | 0 | deriveOffsets(component, bitDepths[CH_C], typeIdc, blkStats[ctuRsAddr][component][typeIdc], testOffset[component].offset, testOffset[component].typeAuxInfo); |
721 | 0 | invertQuantOffsets(component, typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset); |
722 | 0 | dist[component] = getDistortion(bitDepths[CH_C], typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][component][typeIdc]); |
723 | 0 | m_CABACEstimator->sao_offset_pars( testOffset[component], component, sliceEnabled[component], bitDepths[CH_C] ); |
724 | 0 | const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits(); |
725 | 0 | cost += dist[component] + (m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits)); |
726 | 0 | previousFracBits = currentFracBits; |
727 | 0 | } |
728 | |
|
729 | 0 | if(cost < minCost) |
730 | 0 | { |
731 | 0 | minCost = cost; |
732 | 0 | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
733 | 0 | { |
734 | 0 | modeDist[componentIndex] = dist[componentIndex]; |
735 | 0 | modeParam[componentIndex] = testOffset[componentIndex]; |
736 | 0 | } |
737 | 0 | } |
738 | |
|
739 | 0 | } // SAO_TYPE loop |
740 | | |
741 | | //----- re-gen rate & normalized cost----// |
742 | 0 | modeNormCost = 0; |
743 | 0 | for(uint32_t componentIndex = COMP_Y; componentIndex < numberOfComponents; componentIndex++) |
744 | 0 | { |
745 | 0 | modeNormCost += (double)modeDist[componentIndex] / m_lambda[componentIndex]; |
746 | 0 | } |
747 | |
|
748 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxStartBlk ); |
749 | 0 | m_CABACEstimator->resetBits(); |
750 | 0 | m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false ); |
751 | 0 | modeNormCost += FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
752 | 0 | } |
753 | | |
754 | | void EncSampleAdaptiveOffset::deriveModeMergeRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], const bool* sliceEnabled, const std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost ) |
755 | 0 | { |
756 | 0 | modeNormCost = MAX_DOUBLE; |
757 | |
|
758 | 0 | double cost; |
759 | 0 | SAOBlkParam testBlkParam; |
760 | 0 | const int numberOfComponents = m_numberOfComponents; |
761 | |
|
762 | 0 | const TempCtx ctxStart ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
763 | 0 | TempCtx ctxBest ( m_CtxCache ); |
764 | |
|
765 | 0 | for(int mergeType=0; mergeType< NUM_SAO_MERGE_TYPES; mergeType++) |
766 | 0 | { |
767 | 0 | if(mergeList[mergeType] == NULL) |
768 | 0 | { |
769 | 0 | continue; |
770 | 0 | } |
771 | | |
772 | 0 | testBlkParam = *(mergeList[mergeType]); |
773 | | //normalized distortion |
774 | 0 | double normDist=0; |
775 | 0 | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
776 | 0 | { |
777 | 0 | testBlkParam[compIdx].modeIdc = SAO_MODE_MERGE; |
778 | 0 | testBlkParam[compIdx].typeIdc = mergeType; |
779 | |
|
780 | 0 | SAOOffset& mergedOffsetParam = (*(mergeList[mergeType]))[compIdx]; |
781 | |
|
782 | 0 | if( mergedOffsetParam.modeIdc != SAO_MODE_OFF) |
783 | 0 | { |
784 | | //offsets have been reconstructed. Don't call inversed quantization function. |
785 | 0 | normDist += (((double)getDistortion(bitDepths[toChannelType(ComponentID(compIdx))], mergedOffsetParam.typeIdc, mergedOffsetParam.typeAuxInfo, mergedOffsetParam.offset, blkStats[ctuRsAddr][compIdx][mergedOffsetParam.typeIdc])) |
786 | 0 | /m_lambda[compIdx] ); |
787 | 0 | } |
788 | 0 | } |
789 | | |
790 | | //rate |
791 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxStart ); |
792 | 0 | m_CABACEstimator->resetBits(); |
793 | 0 | m_CABACEstimator->sao_block_pars( testBlkParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false ); |
794 | 0 | double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
795 | 0 | cost = normDist+rate; |
796 | |
|
797 | 0 | if(cost < modeNormCost) |
798 | 0 | { |
799 | 0 | modeNormCost = cost; |
800 | 0 | modeParam = testBlkParam; |
801 | 0 | ctxBest = SAOCtx( m_CABACEstimator->getCtx() ); |
802 | 0 | } |
803 | 0 | } |
804 | 0 | if( modeNormCost < MAX_DOUBLE ) |
805 | 0 | { |
806 | 0 | m_CABACEstimator->getCtx() = SAOCtx( ctxBest ); |
807 | 0 | } |
808 | 0 | } |
809 | | |
810 | | void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int channelBitDepth, SAOStatData* statsDataTypes |
811 | | , Pel* srcBlk, Pel* orgBlk, int srcStride, int orgStride, int width, int height |
812 | | , bool isLeftAvail, bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail ) |
813 | 0 | { |
814 | 0 | int x, startX, startY, endX, endY, edgeType, firstLineStartX, firstLineEndX; |
815 | 0 | int64_t *diff, *count; |
816 | 0 | Pel* srcLine, *orgLine; |
817 | 0 | const int skipLinesR = compIdx == COMP_Y ? 5 : 3; |
818 | 0 | const int skipLinesB = compIdx == COMP_Y ? 4 : 2; |
819 | |
|
820 | 0 | for(int typeIdx=0; typeIdx< NUM_SAO_NEW_TYPES; typeIdx++) |
821 | 0 | { |
822 | 0 | SAOStatData& statsData= statsDataTypes[typeIdx]; |
823 | 0 | statsData.reset(); |
824 | 0 | srcLine = srcBlk; |
825 | 0 | orgLine = orgBlk; |
826 | 0 | diff = statsData.diff; |
827 | 0 | count = statsData.count; |
828 | 0 | switch(typeIdx) |
829 | 0 | { |
830 | 0 | case SAO_TYPE_EO_0: |
831 | 0 | { |
832 | 0 | endY = isBelowAvail ? (height - skipLinesB) : height; |
833 | 0 | startX = (isLeftAvail ? 0 : 1); |
834 | 0 | endX = (isRightAvail ? (width - skipLinesR) : (width - 1)); |
835 | 0 | calcSaoStatisticsEo0(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff); |
836 | 0 | } |
837 | 0 | break; |
838 | 0 | case SAO_TYPE_EO_90: |
839 | 0 | { |
840 | 0 | int8_t *signUpLine = &m_signLineBuf1[0]; |
841 | 0 | startX = 0; |
842 | 0 | startY = isAboveAvail ? 0 : 1; |
843 | 0 | endX = (isRightAvail ? (width - skipLinesR) : width); |
844 | 0 | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
845 | 0 | if (!isAboveAvail) |
846 | 0 | { |
847 | 0 | srcLine += srcStride; |
848 | 0 | orgLine += orgStride; |
849 | 0 | } |
850 | 0 | calcSaoStatisticsEo90(width,endX,startY,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine); |
851 | 0 | } |
852 | 0 | break; |
853 | 0 | case SAO_TYPE_EO_135: |
854 | 0 | { |
855 | 0 | diff +=2; |
856 | 0 | count+=2; |
857 | 0 | int8_t *signUpLine, *signDownLine; |
858 | 0 | signUpLine = &m_signLineBuf1[0]; |
859 | 0 | signDownLine= &m_signLineBuf2[0]; |
860 | 0 | startX = isLeftAvail ? 0 : 1; |
861 | 0 | endX = isRightAvail ? (width - skipLinesR): (width - 1); |
862 | 0 | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
863 | | //prepare 2nd line's upper sign |
864 | 0 | Pel* srcLineBelow = srcLine + srcStride; |
865 | 0 | for (x=startX; x<endX+1; x++) |
866 | 0 | { |
867 | 0 | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x-1]); |
868 | 0 | } |
869 | | //1st line |
870 | 0 | Pel* srcLineAbove = srcLine - srcStride; |
871 | 0 | firstLineStartX = isAboveLeftAvail ? 0 : 1; |
872 | 0 | firstLineEndX = isAboveAvail ? endX : 1; |
873 | 0 | for(x=firstLineStartX; x<firstLineEndX; x++) |
874 | 0 | { |
875 | 0 | edgeType = sgn(srcLine[x] - srcLineAbove[x-1]) - signUpLine[x+1]; |
876 | 0 | diff [edgeType] += (orgLine[x] - srcLine[x]); |
877 | 0 | count[edgeType] ++; |
878 | 0 | } |
879 | 0 | srcLine += srcStride; |
880 | 0 | orgLine += orgStride; |
881 | 0 | calcSaoStatisticsEo135(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine,signDownLine); |
882 | 0 | } |
883 | 0 | break; |
884 | 0 | case SAO_TYPE_EO_45: |
885 | 0 | { |
886 | 0 | diff +=2; |
887 | 0 | count+=2; |
888 | 0 | int8_t *signUpLine = &m_signLineBuf1[1]; |
889 | |
|
890 | 0 | startX = isLeftAvail ? 0 : 1; |
891 | 0 | endX = isRightAvail ? (width - skipLinesR) : (width - 1); |
892 | 0 | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
893 | | |
894 | | //prepare 2nd line upper sign |
895 | 0 | Pel* srcLineBelow = srcLine + srcStride; |
896 | 0 | for (x=startX-1; x<endX; x++) |
897 | 0 | { |
898 | 0 | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]); |
899 | 0 | } |
900 | | //first line |
901 | 0 | Pel* srcLineAbove = srcLine - srcStride; |
902 | 0 | firstLineStartX = isAboveAvail ? startX : endX; |
903 | 0 | firstLineEndX = (!isRightAvail && isAboveRightAvail) ? width : endX; |
904 | 0 | for(x=firstLineStartX; x<firstLineEndX; x++) |
905 | 0 | { |
906 | 0 | edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) - signUpLine[x-1]; |
907 | 0 | diff [edgeType] += (orgLine[x] - srcLine[x]); |
908 | 0 | count[edgeType] ++; |
909 | 0 | } |
910 | 0 | srcLine += srcStride; |
911 | 0 | orgLine += orgStride; |
912 | 0 | calcSaoStatisticsEo45(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine); |
913 | 0 | } |
914 | 0 | break; |
915 | 0 | case SAO_TYPE_BO: |
916 | 0 | { |
917 | 0 | startX = 0; |
918 | 0 | endX = isRightAvail ? (width - skipLinesR) : width; |
919 | 0 | endY = isBelowAvail ? (height- skipLinesB) : height; |
920 | 0 | calcSaoStatisticsBo(width,endX,endY,srcLine,orgLine,srcStride,orgStride,channelBitDepth,count,diff); |
921 | 0 | } |
922 | 0 | break; |
923 | 0 | default: |
924 | 0 | { |
925 | 0 | THROW("Not a supported SAO type"); |
926 | 0 | } |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } |
930 | | |
931 | | void EncSampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility(CodingStructure& cs, const Position& pos, bool& isLeftAvail, bool& isAboveAvail, bool& isAboveLeftAvail) const |
932 | 0 | { |
933 | 0 | const bool isLoopFiltAcrossSlicePPS = cs.pps->loopFilterAcrossSlicesEnabled; |
934 | 0 | const bool isLoopFiltAcrossTilePPS = cs.pps->loopFilterAcrossTilesEnabled; |
935 | |
|
936 | 0 | const int width = cs.pcv->maxCUSize; |
937 | 0 | const int height = cs.pcv->maxCUSize; |
938 | 0 | const CodingUnit* cuCurr = cs.getCU(pos, CH_L, TREE_D); |
939 | 0 | const int ctuX = pos.x >> cs.pcv->maxCUSizeLog2; |
940 | 0 | const int ctuY = pos.y >> cs.pcv->maxCUSizeLog2; |
941 | 0 | const PPS* pps = cs.slice->pps; |
942 | 0 | const CodingUnit* cuLeft = ctuX > 0 && pps->canFilterCtuBdry( ctuX, ctuY, -1, 0 ) ? cs.getCU(pos.offset(-width, 0), CH_L, TREE_D): nullptr; |
943 | 0 | const CodingUnit* cuAbove = ctuY > 0 && pps->canFilterCtuBdry( ctuX, ctuY, 0, -1 ) ? cs.getCU(pos.offset(0, -height), CH_L, TREE_D): nullptr; |
944 | 0 | const CodingUnit* cuAboveLeft = ctuY > 0 && ctuX > 0 && pps->canFilterCtuBdry( ctuX, ctuY, -1,-1 ) ? cs.getCU(pos.offset(-width, -height), CH_L, TREE_D): nullptr; |
945 | |
|
946 | 0 | if (!isLoopFiltAcrossSlicePPS) |
947 | 0 | { |
948 | 0 | isLeftAvail = (cuLeft == NULL) ? false : CU::isSameSlice(*cuCurr, *cuLeft); |
949 | 0 | isAboveAvail = (cuAbove == NULL) ? false : CU::isSameSlice(*cuCurr, *cuAbove); |
950 | 0 | isAboveLeftAvail = (cuAboveLeft == NULL) ? false : CU::isSameSlice(*cuCurr, *cuAboveLeft); |
951 | 0 | } |
952 | 0 | else |
953 | 0 | { |
954 | 0 | isLeftAvail = (cuLeft != NULL); |
955 | 0 | isAboveAvail = (cuAbove != NULL); |
956 | 0 | isAboveLeftAvail = (cuAboveLeft != NULL); |
957 | 0 | } |
958 | |
|
959 | 0 | if (!isLoopFiltAcrossTilePPS) |
960 | 0 | { |
961 | 0 | isLeftAvail = (!isLeftAvail) ? false : CU::isSameTile(*cuCurr, *cuLeft); |
962 | 0 | isAboveAvail = (!isAboveAvail) ? false : CU::isSameTile(*cuCurr, *cuAbove); |
963 | 0 | isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameTile(*cuCurr, *cuAboveLeft); |
964 | 0 | } |
965 | | |
966 | |
|
967 | 0 | SubPic curSubPic = cs.pps->getSubPicFromCU(*cuCurr); |
968 | 0 | if (!curSubPic.loopFilterAcrossSubPicEnabled ) |
969 | 0 | { |
970 | 0 | isLeftAvail = (!isLeftAvail) ? false : CU::isSameSubPic(*cuCurr, *cuLeft); |
971 | 0 | isAboveAvail = (!isAboveAvail) ? false : CU::isSameSubPic(*cuCurr, *cuAbove); |
972 | 0 | isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameSubPic(*cuCurr, *cuAboveLeft); |
973 | 0 | } |
974 | |
|
975 | 0 | } |
976 | | |
977 | | } // namespace vvenc |
978 | | |
979 | | //! \} |
980 | | |