/work/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 | 80.1k | #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 | 12.8k | { |
76 | 12.8k | return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ; |
77 | 12.8k | } |
78 | | |
79 | | |
80 | | void SaoPreFilterEdges::create( const ChromaFormat chFmt, const PreCalcValues& pcv ) |
81 | 4.62k | { |
82 | 18.4k | for( int compIdx = 0; compIdx < getNumberValidComponents( chFmt ); compIdx++ ) |
83 | 13.8k | { |
84 | 13.8k | const ComponentID compID = ComponentID( compIdx ); |
85 | 13.8k | width [compIdx] = pcv.lumaWidth >> getComponentScaleX( compID, chFmt ); |
86 | 13.8k | ctuHeight[compIdx] = pcv.maxCUSize >> getComponentScaleY( compID, chFmt ); |
87 | 13.8k | bottomRows[compIdx].resize( pcv.heightInCtus * width[compIdx] ); |
88 | 13.8k | rightCols [compIdx].resize( pcv.heightInCtus * ctuHeight[compIdx] ); |
89 | 13.8k | } |
90 | 4.62k | } |
91 | | |
92 | | EncSampleAdaptiveOffset::EncSampleAdaptiveOffset() |
93 | 8.15k | : m_CABACEstimator( nullptr ) |
94 | 8.15k | , m_CtxCache ( nullptr ) |
95 | 8.15k | , m_ctuPreSao ( nullptr ) |
96 | 8.15k | { |
97 | 8.15k | } |
98 | | |
99 | | EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset() |
100 | 8.15k | { |
101 | 8.15k | } |
102 | | |
103 | | void EncSampleAdaptiveOffset::init( const VVEncCfg& encCfg ) |
104 | 8.15k | { |
105 | 8.15k | m_EncCfg = &encCfg; |
106 | | |
107 | 8.15k | if ( encCfg.m_bUseSAO ) |
108 | 8.15k | { |
109 | 8.15k | SampleAdaptiveOffset::init( encCfg.m_internChromaFormat, encCfg.m_CTUSize, encCfg.m_CTUSize, encCfg.m_log2SaoOffsetScale[CH_L], encCfg.m_log2SaoOffsetScale[CH_C] ); |
110 | 8.15k | } |
111 | 8.15k | } |
112 | | |
113 | | void EncSampleAdaptiveOffset::initSlice( const Slice* slice ) |
114 | 2.03k | { |
115 | 2.03k | memcpy( m_lambda, slice->getLambdas(), sizeof( m_lambda ) ); |
116 | 2.03k | } |
117 | | |
118 | | void EncSampleAdaptiveOffset::setCtuEncRsrc( CABACWriter* cabacEstimator, CtxCache* ctxCache, PelStorage* ctuPreSaoBuf ) |
119 | 3.49k | { |
120 | 3.49k | m_CABACEstimator = cabacEstimator; |
121 | 3.49k | m_CtxCache = ctxCache; |
122 | 3.49k | m_ctuPreSao = ctuPreSaoBuf; |
123 | 3.49k | } |
124 | | |
125 | | void EncSampleAdaptiveOffset::disabledRate( CodingStructure& cs, double saoDisabledRate[ MAX_NUM_COMP ][ VVENC_MAX_TLAYER ], SAOBlkParam* reconParams, const double saoEncodingRate, const double saoEncodingRateChroma, const ChromaFormat& chromaFormat ) |
126 | 0 | { |
127 | 0 | if ( saoEncodingRate > 0.0 ) |
128 | 0 | { |
129 | 0 | const PreCalcValues& pcv = *cs.pcv; |
130 | 0 | const int numberOfComponents = getNumberValidComponents( chromaFormat ); |
131 | 0 | const int picTempLayer = cs.slice->TLayer; |
132 | 0 | int numCtusForSAOOff[MAX_NUM_COMP]; |
133 | |
|
134 | 0 | for (int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
135 | 0 | { |
136 | 0 | numCtusForSAOOff[compIdx] = 0; |
137 | 0 | for( int ctuRsAddr=0; ctuRsAddr< pcv.sizeInCtus; ctuRsAddr++) |
138 | 0 | { |
139 | 0 | if( reconParams[ctuRsAddr][compIdx].modeIdc == SAO_MODE_OFF) |
140 | 0 | { |
141 | 0 | numCtusForSAOOff[compIdx]++; |
142 | 0 | } |
143 | 0 | } |
144 | 0 | } |
145 | 0 | if (saoEncodingRateChroma > 0.0) |
146 | 0 | { |
147 | 0 | for (int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
148 | 0 | { |
149 | 0 | saoDisabledRate[compIdx][picTempLayer] = (double)numCtusForSAOOff[compIdx]/(double)pcv.sizeInCtus; |
150 | 0 | } |
151 | 0 | } |
152 | 0 | else if (picTempLayer == 0) |
153 | 0 | { |
154 | 0 | saoDisabledRate[COMP_Y][0] = (double)(numCtusForSAOOff[COMP_Y]+numCtusForSAOOff[COMP_Cb]+numCtusForSAOOff[COMP_Cr])/(double)(pcv.sizeInCtus *3); |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } |
158 | | |
159 | | 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 ) |
160 | 1.15k | { |
161 | 1.15k | const Slice& slice = *cs.slice; |
162 | 1.15k | const int numberOfComponents = getNumberValidComponents( chromaFormat ); |
163 | | |
164 | | // reset |
165 | 1.15k | if( slice.pendingRasInit ) |
166 | 0 | { |
167 | 0 | for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ ) |
168 | 0 | { |
169 | 0 | for( int tempLayer = 1; tempLayer < VVENC_MAX_TLAYER; tempLayer++ ) |
170 | 0 | { |
171 | 0 | saoDisabledRate[ compIdx ][ tempLayer ] = 0.0; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | } |
175 | | |
176 | 4.62k | for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ ) |
177 | 3.46k | { |
178 | 3.46k | saoEnabled[ compIdx ] = false; |
179 | 3.46k | } |
180 | | |
181 | 1.15k | const int picTempLayer = slice.TLayer; |
182 | 4.62k | for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ ) |
183 | 3.46k | { |
184 | | // enable per default |
185 | 3.46k | saoEnabled[ compIdx ] = true; |
186 | | |
187 | 3.46k | if( saoEncodingRate > 0.0 ) |
188 | 0 | { |
189 | 0 | if( saoEncodingRateChroma > 0.0 ) |
190 | 0 | { |
191 | | // decide slice-level on/off based on previous results |
192 | 0 | if( ( picTempLayer > 0 ) |
193 | 0 | && ( saoDisabledRate[ compIdx ][ picTempLayer - 1 ] > ( ( compIdx == COMP_Y ) ? saoEncodingRate : saoEncodingRateChroma ) ) ) |
194 | 0 | { |
195 | 0 | saoEnabled[ compIdx ] = false; |
196 | 0 | } |
197 | 0 | } |
198 | 0 | else |
199 | 0 | { |
200 | | // decide slice-level on/off based on previous results |
201 | 0 | if( ( picTempLayer > 0 ) |
202 | 0 | && ( saoDisabledRate[ COMP_Y ][ 0 ] > saoEncodingRate ) ) |
203 | 0 | { |
204 | 0 | saoEnabled[ compIdx ] = false; |
205 | 0 | } |
206 | 0 | } |
207 | 0 | } |
208 | 3.46k | } |
209 | 1.15k | } |
210 | | |
211 | | void EncSampleAdaptiveOffset::loadCtuPreSao( CodingStructure& cs, const UnitArea& ctuArea, const int ctuY, SaoPreFilterEdges& edges ) |
212 | 3.49k | { |
213 | | // SAO of a CTU reads the CTU and one sample around it, all as they were before SAO. CTUs are |
214 | | // filtered row by row from the left, so the reconstruction still holds these samples for the CTU |
215 | | // itself and for the neighbours right of and below it. The CTUs above and to the left are |
216 | | // filtered already, and their edges come from the copies they saved here. |
217 | 3.49k | const PelUnitBuf reco = cs.picture->getRecoBuf(); |
218 | | |
219 | 13.9k | for( int compIdx = 0; compIdx < getNumberValidComponents( ctuArea.chromaFormat ); compIdx++ ) |
220 | 10.5k | { |
221 | 10.5k | const ComponentID compID = ComponentID( compIdx ); |
222 | 10.5k | const CompArea& area = ctuArea.block( compID ); |
223 | 10.5k | const CPelBuf src = reco.get( compID ); |
224 | 10.5k | PelBuf ctu = m_ctuPreSao->get( compID ); |
225 | | |
226 | 10.5k | const int x = area.x; |
227 | 10.5k | const int y = area.y; |
228 | 10.5k | const int w = area.width; |
229 | 10.5k | const int h = area.height; |
230 | 10.5k | const int colBegin = x > 0 ? -1 : 0; |
231 | 10.5k | const int colEnd = x + w < ( int ) src.width ? w + 1 : w; |
232 | 10.5k | Pel* const savedRow = &edges.bottomRows[compIdx][ctuY * edges.width[compIdx]]; |
233 | 10.5k | Pel* const savedCol = &edges.rightCols [compIdx][ctuY * edges.ctuHeight[compIdx]]; |
234 | | |
235 | | // the row above, saved by the CTU row above |
236 | 10.5k | if( y > 0 ) |
237 | 4.75k | { |
238 | 4.75k | const Pel* rowAbove = &edges.bottomRows[compIdx][( ctuY - 1 ) * edges.width[compIdx]]; |
239 | 4.75k | std::copy( rowAbove + x + colBegin, rowAbove + x + colEnd, ctu.bufAt( colBegin, -1 ) ); |
240 | 4.75k | } |
241 | | // the CTU and the column right of it |
242 | 620k | for( int r = 0; r < h; r++ ) |
243 | 610k | { |
244 | 610k | const Pel* line = src.bufAt( x, y + r ); |
245 | 610k | std::copy( line, line + colEnd, ctu.bufAt( 0, r ) ); |
246 | 610k | } |
247 | | // the column left of it, saved by the CTU to the left |
248 | 10.5k | if( x > 0 ) |
249 | 4.39k | { |
250 | 264k | for( int r = 0; r < h; r++ ) |
251 | 260k | { |
252 | 260k | ctu.at( -1, r ) = savedCol[r]; |
253 | 260k | } |
254 | 4.39k | } |
255 | | // the row below |
256 | 10.5k | if( y + h < ( int ) src.height ) |
257 | 4.75k | { |
258 | 4.75k | const Pel* line = src.bufAt( x, y + h ); |
259 | 4.75k | std::copy( line + colBegin, line + colEnd, ctu.bufAt( colBegin, h ) ); |
260 | 4.75k | } |
261 | | |
262 | | // save this CTU's bottom row and right column for the CTUs filtered after it |
263 | 10.5k | std::copy( ctu.bufAt( 0, h - 1 ), ctu.bufAt( 0, h - 1 ) + w, savedRow + x ); |
264 | 621k | for( int r = 0; r < h; r++ ) |
265 | 610k | { |
266 | 610k | savedCol[r] = ctu.at( w - 1, r ); |
267 | 610k | } |
268 | 10.5k | } |
269 | 3.49k | } |
270 | | |
271 | | void EncSampleAdaptiveOffset::getCtuStatistics( CodingStructure& cs, std::vector<SAOStatData**>& saoStatistics, const UnitArea& ctuArea, const int ctuRsAddr ) |
272 | 3.50k | { |
273 | 3.50k | const PreCalcValues& pcv = *cs.pcv; |
274 | 3.50k | const int numberOfComponents = getNumberValidComponents( pcv.chrFormat ); |
275 | 3.50k | bool isLeftAvail = false; |
276 | 3.50k | bool isRightAvail = false; |
277 | 3.50k | bool isAboveAvail = false; |
278 | 3.50k | bool isBelowAvail = false; |
279 | 3.50k | bool isAboveLeftAvail = false; |
280 | 3.50k | bool isAboveRightAvail = false; |
281 | | |
282 | 3.50k | deriveLoopFilterBoundaryAvailibility( cs, ctuArea.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail ); |
283 | | |
284 | | // NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities. |
285 | | // For simplicity, here only picture boundaries are considered. |
286 | | |
287 | 3.50k | isRightAvail = ( ctuArea.Y().x + pcv.maxCUSize < pcv.lumaWidth ); |
288 | 3.50k | isBelowAvail = ( ctuArea.Y().y + pcv.maxCUSize < pcv.lumaHeight ); |
289 | 3.50k | isAboveRightAvail = ( ( ctuArea.Y().y > 0 ) && ( isRightAvail ) ); |
290 | | |
291 | 3.50k | CHECK( !cs.pps->loopFilterAcrossSlicesEnabled, "Not implemented" ); |
292 | 3.50k | if( cs.pps->getNumTiles() > 1 && !cs.pps->loopFilterAcrossTilesEnabled ) |
293 | 0 | { |
294 | 0 | const int ctuX = ctuArea.lx() >> cs.pcv->maxCUSizeLog2; |
295 | 0 | const int ctuY = ctuArea.ly() >> cs.pcv->maxCUSizeLog2; |
296 | 0 | isRightAvail = isRightAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 1, 0 ); |
297 | 0 | isBelowAvail = isBelowAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 0, 1 ); |
298 | 0 | isAboveRightAvail = isAboveRightAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY, 1,-1 ); |
299 | 0 | } |
300 | | |
301 | | //VirtualBoundaries vb; |
302 | | //bool isCtuCrossedByVirtualBoundaries = vb.isCrossedByVirtualBoundaries(xPos, yPos, width, height, cs.slice->pps); |
303 | | |
304 | 14.0k | for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ ) |
305 | 10.5k | { |
306 | 10.5k | const ComponentID compID = ComponentID( compIdx ); |
307 | 10.5k | const CompArea& compArea = ctuArea.block( compID ); |
308 | | |
309 | 10.5k | PelBuf srcBuf = m_ctuPreSao->get( compID ); |
310 | 10.5k | PelBuf orgBuf = cs.picture->getOrigBuf().get( compID ); |
311 | | |
312 | 10.5k | getBlkStats( compID, |
313 | 10.5k | cs.sps->bitDepths[ toChannelType( compID ) ], |
314 | 10.5k | saoStatistics[ ctuRsAddr ][ compID ], |
315 | 10.5k | srcBuf.buf, |
316 | 10.5k | orgBuf.bufAt( compArea ), |
317 | 10.5k | srcBuf.stride, |
318 | 10.5k | orgBuf.stride, |
319 | 10.5k | compArea.width, |
320 | 10.5k | compArea.height, |
321 | 10.5k | isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail |
322 | 10.5k | ); |
323 | 10.5k | } |
324 | 3.50k | } |
325 | | |
326 | | void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv, CodingStructure& cs ) |
327 | 0 | { |
328 | 0 | bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail; |
329 | |
|
330 | 0 | const PreCalcValues& pcv = *cs.pcv; |
331 | 0 | const int numberOfComponents = getNumberValidComponents(pcv.chrFormat); |
332 | |
|
333 | 0 | size_t lineBufferSize = pcv.maxCUSize + 1; |
334 | 0 | if (m_signLineBuf1.size() != lineBufferSize) |
335 | 0 | { |
336 | 0 | m_signLineBuf1.resize(lineBufferSize); |
337 | 0 | m_signLineBuf2.resize(lineBufferSize); |
338 | 0 | } |
339 | |
|
340 | 0 | int ctuRsAddr = 0; |
341 | 0 | for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUSize ) |
342 | 0 | { |
343 | 0 | for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUSize ) |
344 | 0 | { |
345 | 0 | const uint32_t width = (xPos + pcv.maxCUSize > pcv.lumaWidth) ? (pcv.lumaWidth - xPos) : pcv.maxCUSize; |
346 | 0 | const uint32_t height = (yPos + pcv.maxCUSize > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUSize; |
347 | 0 | const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) ); |
348 | |
|
349 | 0 | deriveLoopFilterBoundaryAvailibility(cs, area.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail ); |
350 | | |
351 | | //NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities. |
352 | | //For simplicity, here only picture boundaries are considered. |
353 | |
|
354 | 0 | isRightAvail = (xPos + pcv.maxCUSize < pcv.lumaWidth ); |
355 | 0 | isBelowAvail = (yPos + pcv.maxCUSize < pcv.lumaHeight); |
356 | 0 | isAboveRightAvail = ((yPos > 0) && (isRightAvail)); |
357 | |
|
358 | 0 | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
359 | 0 | { |
360 | 0 | const ComponentID compID = ComponentID(compIdx); |
361 | 0 | const CompArea& compArea = area.block( compID ); |
362 | |
|
363 | 0 | int srcStride = srcYuv.get(compID).stride; |
364 | 0 | Pel* srcBlk = srcYuv.get(compID).bufAt( compArea ); |
365 | |
|
366 | 0 | int orgStride = orgYuv.get(compID).stride; |
367 | 0 | Pel* orgBlk = orgYuv.get(compID).bufAt( compArea ); |
368 | |
|
369 | 0 | getBlkStats(compID, cs.sps->bitDepths[toChannelType(compID)], blkStats[ctuRsAddr][compID] |
370 | 0 | , srcBlk, orgBlk, srcStride, orgStride, compArea.width, compArea.height |
371 | 0 | , isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail ); |
372 | 0 | } |
373 | 0 | ctuRsAddr++; |
374 | 0 | } |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | | 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 ) |
379 | 3.50k | { |
380 | 3.50k | const PreCalcValues& pcv = *cs.pcv; |
381 | 3.50k | const Slice& slice = *cs.slice; |
382 | 3.50k | const int ctuPosX = ctuRsAddr % pcv.widthInCtus; |
383 | 3.50k | const int ctuPosY = ctuRsAddr / pcv.widthInCtus; |
384 | | |
385 | | // reset CABAC estimator |
386 | 3.50k | if( m_EncCfg->m_ensureWppBitEqual |
387 | 3.50k | && m_EncCfg->m_numThreads < 1 |
388 | 0 | && ctuPosX == 0 |
389 | 0 | && ctuPosY > 0 ) |
390 | 0 | { |
391 | 0 | m_CABACEstimator->initCtxModels( slice ); |
392 | 0 | } |
393 | | |
394 | | // check disabled |
395 | 3.50k | if( allBlksDisabled ) |
396 | 0 | { |
397 | 0 | codedParams[ ctuRsAddr ].reset(); |
398 | 0 | return; |
399 | 0 | } |
400 | | |
401 | | // get merge list |
402 | 3.50k | SAOBlkParam* mergeList[ NUM_SAO_MERGE_TYPES ] = { NULL }; |
403 | 3.50k | getMergeList( cs, ctuRsAddr, reconParams, mergeList ); |
404 | | |
405 | 3.50k | const TempCtx ctxStart( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
406 | 3.50k | TempCtx ctxBest ( m_CtxCache ); |
407 | | |
408 | 3.50k | SAOBlkParam modeParam; |
409 | 3.50k | double minCost = MAX_DOUBLE; |
410 | 3.50k | double modeCost = MAX_DOUBLE; |
411 | 10.5k | for( int mode = 1; mode < NUM_SAO_MODES; mode++ ) |
412 | 7.00k | { |
413 | 7.00k | if( mode > 1 ) |
414 | 3.50k | { |
415 | 3.50k | m_CABACEstimator->getCtx() = SAOCtx( ctxStart ); |
416 | 3.50k | } |
417 | 7.00k | switch( mode ) |
418 | 7.00k | { |
419 | 3.50k | case SAO_MODE_NEW: |
420 | 3.50k | { |
421 | 3.50k | deriveModeNewRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost ); |
422 | 3.50k | } |
423 | 3.50k | break; |
424 | 3.50k | case SAO_MODE_MERGE: |
425 | 3.50k | { |
426 | 3.50k | deriveModeMergeRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost ); |
427 | 3.50k | } |
428 | 3.50k | break; |
429 | 0 | default: |
430 | 0 | { |
431 | 0 | THROW( "Not a supported SAO mode." ); |
432 | 0 | } |
433 | 7.00k | } |
434 | | |
435 | 7.00k | if( modeCost < minCost ) |
436 | 5.84k | { |
437 | 5.84k | minCost = modeCost; |
438 | 5.84k | codedParams[ ctuRsAddr ] = modeParam; |
439 | 5.84k | ctxBest = SAOCtx( m_CABACEstimator->getCtx() ); |
440 | 5.84k | } |
441 | 7.00k | } |
442 | | |
443 | | // apply reconstructed offsets |
444 | 3.50k | m_CABACEstimator->getCtx() = SAOCtx( ctxBest ); |
445 | 3.50k | reconParams[ ctuRsAddr ] = codedParams[ ctuRsAddr ]; |
446 | | |
447 | 3.50k | reconstructBlkSAOParam( reconParams[ ctuRsAddr ], mergeList ); |
448 | | |
449 | 3.50k | offsetCTU( ctuArea, *m_ctuPreSao, cs.getRecoBuf(), reconParams[ ctuRsAddr ], cs ); |
450 | 3.50k | } |
451 | | |
452 | | int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData) |
453 | 52.5k | { |
454 | 52.5k | int64_t dist = 0; |
455 | 52.5k | int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth); |
456 | | |
457 | 52.5k | switch(typeIdc) |
458 | 52.5k | { |
459 | 10.5k | case SAO_TYPE_EO_0: |
460 | 21.0k | case SAO_TYPE_EO_90: |
461 | 31.5k | case SAO_TYPE_EO_135: |
462 | 42.0k | case SAO_TYPE_EO_45: |
463 | 42.0k | { |
464 | 252k | for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++) |
465 | 210k | { |
466 | 210k | dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift); |
467 | 210k | } |
468 | 42.0k | } |
469 | 42.0k | break; |
470 | 10.5k | case SAO_TYPE_BO: |
471 | 10.5k | { |
472 | 52.5k | for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++) |
473 | 42.0k | { |
474 | 42.0k | int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ; |
475 | 42.0k | dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift); |
476 | 42.0k | } |
477 | 10.5k | } |
478 | 10.5k | break; |
479 | 0 | default: |
480 | 0 | { |
481 | 0 | THROW("Not a supported type"); |
482 | 31.5k | } |
483 | 52.5k | } |
484 | | |
485 | 52.5k | return dist; |
486 | 52.5k | } |
487 | | |
488 | | inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift) |
489 | 253k | { |
490 | 253k | return (( count*offset*offset-diffSum*offset*2 ) >> shift); |
491 | 253k | } |
492 | | |
493 | | |
494 | | 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 ) |
495 | 1.38k | { |
496 | 1.38k | int iterOffset, tempOffset; |
497 | 1.38k | int64_t tempDist, tempRate; |
498 | 1.38k | double tempCost, tempMinCost; |
499 | 1.38k | int offsetOutput = 0; |
500 | 1.38k | iterOffset = offsetInput; |
501 | | // 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. |
502 | 1.38k | tempMinCost = lambda; |
503 | 2.78k | while (iterOffset != 0) |
504 | 1.39k | { |
505 | | // Calculate the bits required for signaling the offset |
506 | 1.39k | tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1); |
507 | 1.39k | if (abs((int)iterOffset)==offsetTh) //inclusive |
508 | 0 | { |
509 | 0 | tempRate --; |
510 | 0 | } |
511 | | // Do the dequantization before distortion calculation |
512 | 1.39k | tempOffset = iterOffset * (1<< bitIncrease); |
513 | 1.39k | tempDist = estSaoDist( count, tempOffset, diffSum, shift); |
514 | 1.39k | tempCost = ((double)tempDist + lambda * (double) tempRate); |
515 | 1.39k | if(tempCost < tempMinCost) |
516 | 284 | { |
517 | 284 | tempMinCost = tempCost; |
518 | 284 | offsetOutput = iterOffset; |
519 | 284 | bestDist = tempDist; |
520 | 284 | bestCost = tempCost; |
521 | 284 | } |
522 | 1.39k | iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1); |
523 | 1.39k | } |
524 | 1.38k | return offsetOutput; |
525 | 1.38k | } |
526 | | |
527 | | void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo) |
528 | 52.5k | { |
529 | 52.5k | int bitDepth = channelBitDepth; |
530 | 52.5k | int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth); |
531 | 52.5k | int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth); //inclusive |
532 | | |
533 | 52.5k | ::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES); |
534 | | |
535 | | //derive initial offsets |
536 | 52.5k | int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES); |
537 | 598k | for(int classIdx=0; classIdx< numClasses; classIdx++) |
538 | 546k | { |
539 | 546k | if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN) ) |
540 | 42.0k | { |
541 | 42.0k | continue; //offset will be zero |
542 | 42.0k | } |
543 | | |
544 | 504k | if(statData.count[classIdx] == 0) |
545 | 491k | { |
546 | 491k | continue; //offset will be zero |
547 | 491k | } |
548 | 12.8k | #if ( DISTORTION_PRECISION_ADJUSTMENT(x) == 0 ) |
549 | 12.8k | quantOffsets[classIdx] = |
550 | 12.8k | (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] ) / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx])); |
551 | 12.8k | quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]); |
552 | | #else |
553 | | quantOffsets[classIdx] = |
554 | | (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] << DISTORTION_PRECISION_ADJUSTMENT(bitDepth)) |
555 | | / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx])); |
556 | | quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]); |
557 | | #endif |
558 | 12.8k | } |
559 | | |
560 | | // adjust offsets |
561 | 52.5k | switch(typeIdc) |
562 | 52.5k | { |
563 | 10.5k | case SAO_TYPE_EO_0: |
564 | 21.0k | case SAO_TYPE_EO_90: |
565 | 31.5k | case SAO_TYPE_EO_135: |
566 | 42.0k | case SAO_TYPE_EO_45: |
567 | 42.0k | { |
568 | 42.0k | int64_t classDist; |
569 | 42.0k | double classCost; |
570 | 252k | for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++) |
571 | 210k | { |
572 | 210k | if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0) |
573 | 0 | { |
574 | 0 | quantOffsets[classIdx] =0; |
575 | 0 | } |
576 | 210k | if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0) |
577 | 0 | { |
578 | 0 | quantOffsets[classIdx] =0; |
579 | 0 | } |
580 | 210k | if(classIdx==SAO_CLASS_EO_HALF_PEAK && quantOffsets[classIdx] > 0) |
581 | 0 | { |
582 | 0 | quantOffsets[classIdx] =0; |
583 | 0 | } |
584 | 210k | if(classIdx==SAO_CLASS_EO_FULL_PEAK && quantOffsets[classIdx] > 0) |
585 | 0 | { |
586 | 0 | quantOffsets[classIdx] =0; |
587 | 0 | } |
588 | | |
589 | 210k | if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero |
590 | 1.07k | { |
591 | 1.07k | quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh ); |
592 | 1.07k | } |
593 | 210k | } |
594 | | |
595 | 42.0k | typeAuxInfo =0; |
596 | 42.0k | } |
597 | 42.0k | break; |
598 | 10.5k | case SAO_TYPE_BO: |
599 | 10.5k | { |
600 | 10.5k | int64_t distBOClasses[NUM_SAO_BO_CLASSES]; |
601 | 10.5k | double costBOClasses[NUM_SAO_BO_CLASSES]; |
602 | 10.5k | ::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES); |
603 | 346k | for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++) |
604 | 336k | { |
605 | 336k | costBOClasses[classIdx]= m_lambda[compIdx]; |
606 | 336k | if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero |
607 | 311 | { |
608 | 311 | quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh ); |
609 | 311 | } |
610 | 336k | } |
611 | | |
612 | | //decide the starting band index |
613 | 10.5k | double minCost = MAX_DOUBLE, cost; |
614 | 346k | for(int band=0; band< NUM_SAO_BO_CLASSES; band++) |
615 | 336k | { |
616 | 336k | cost = costBOClasses[(band )%NUM_SAO_BO_CLASSES]; |
617 | 336k | cost += costBOClasses[(band+1)%NUM_SAO_BO_CLASSES]; |
618 | 336k | cost += costBOClasses[(band+2)%NUM_SAO_BO_CLASSES]; |
619 | 336k | cost += costBOClasses[(band+3)%NUM_SAO_BO_CLASSES]; |
620 | | |
621 | 336k | if(cost < minCost) |
622 | 10.5k | { |
623 | 10.5k | minCost = cost; |
624 | 10.5k | typeAuxInfo = band; |
625 | 10.5k | } |
626 | 336k | } |
627 | | //clear those unused classes |
628 | 10.5k | int clearQuantOffset[NUM_SAO_BO_CLASSES]; |
629 | 10.5k | ::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES); |
630 | 52.5k | for(int i=0; i< 4; i++) |
631 | 42.0k | { |
632 | 42.0k | int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES; |
633 | 42.0k | clearQuantOffset[band] = quantOffsets[band]; |
634 | 42.0k | } |
635 | 10.5k | ::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES); |
636 | 10.5k | } |
637 | 10.5k | break; |
638 | 0 | default: |
639 | 0 | { |
640 | 0 | THROW("Not a supported type"); |
641 | 31.5k | } |
642 | 52.5k | } |
643 | 52.5k | } |
644 | | |
645 | | 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 ) |
646 | 3.50k | { |
647 | 3.50k | double minCost, cost; |
648 | 3.50k | uint64_t previousFracBits; |
649 | 3.50k | const int numberOfComponents = m_numberOfComponents; |
650 | | |
651 | 3.50k | int64_t dist[MAX_NUM_COMP], modeDist[MAX_NUM_COMP]; |
652 | 3.50k | SAOOffset testOffset[MAX_NUM_COMP]; |
653 | 3.50k | int invQuantOffset[MAX_NUM_SAO_CLASSES]; |
654 | 14.0k | for(int comp=0; comp < MAX_NUM_COMP; comp++) |
655 | 10.5k | { |
656 | 10.5k | modeDist[comp] = 0; |
657 | 10.5k | } |
658 | | |
659 | | //pre-encode merge flags |
660 | 3.50k | modeParam[COMP_Y].modeIdc = SAO_MODE_OFF; |
661 | 3.50k | const TempCtx ctxStartBlk ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
662 | 3.50k | m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true ); |
663 | 3.50k | const TempCtx ctxStartLuma ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
664 | 3.50k | TempCtx ctxBestLuma ( m_CtxCache ); |
665 | | |
666 | | //------ luma --------// |
667 | 3.50k | { |
668 | 3.50k | const ComponentID compIdx = COMP_Y; |
669 | | //"off" case as initial cost |
670 | 3.50k | modeParam[compIdx].modeIdc = SAO_MODE_OFF; |
671 | 3.50k | m_CABACEstimator->resetBits(); |
672 | 3.50k | m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] ); |
673 | 3.50k | modeDist[compIdx] = 0; |
674 | 3.50k | minCost = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits()); |
675 | 3.50k | ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() ); |
676 | 3.50k | if(sliceEnabled[compIdx]) |
677 | 3.50k | { |
678 | 21.0k | for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++) |
679 | 17.5k | { |
680 | 17.5k | testOffset[compIdx].modeIdc = SAO_MODE_NEW; |
681 | 17.5k | testOffset[compIdx].typeIdc = typeIdc; |
682 | | |
683 | | //derive coded offset |
684 | 17.5k | deriveOffsets(compIdx, bitDepths[CH_L], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo); |
685 | | |
686 | | //inversed quantized offsets |
687 | 17.5k | invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset); |
688 | | |
689 | | //get distortion |
690 | 17.5k | dist[compIdx] = getDistortion(bitDepths[CH_L], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc]); |
691 | | |
692 | | //get rate |
693 | 17.5k | m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma ); |
694 | 17.5k | m_CABACEstimator->resetBits(); |
695 | 17.5k | m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] ); |
696 | 17.5k | double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
697 | 17.5k | cost = (double)dist[compIdx] + m_lambda[compIdx]*rate; |
698 | 17.5k | if(cost < minCost) |
699 | 28 | { |
700 | 28 | minCost = cost; |
701 | 28 | modeDist[compIdx] = dist[compIdx]; |
702 | 28 | modeParam[compIdx]= testOffset[compIdx]; |
703 | 28 | ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() ); |
704 | 28 | } |
705 | 17.5k | } |
706 | 3.50k | } |
707 | 3.50k | m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma ); |
708 | 3.50k | } |
709 | | |
710 | | //------ chroma --------// |
711 | | //"off" case as initial cost |
712 | 3.50k | cost = 0; |
713 | 3.50k | previousFracBits = 0; |
714 | 3.50k | m_CABACEstimator->resetBits(); |
715 | 10.5k | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
716 | 7.00k | { |
717 | 7.00k | const ComponentID component = ComponentID(componentIndex); |
718 | | |
719 | 7.00k | modeParam[component].modeIdc = SAO_MODE_OFF; |
720 | 7.00k | modeDist [component] = 0; |
721 | 7.00k | m_CABACEstimator->sao_offset_pars( modeParam[component], component, sliceEnabled[component], bitDepths[CH_C] ); |
722 | 7.00k | const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits(); |
723 | 7.00k | cost += m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits); |
724 | 7.00k | previousFracBits = currentFracBits; |
725 | 7.00k | } |
726 | | |
727 | 3.50k | minCost = cost; |
728 | | |
729 | | //doesn't need to store cabac status here since the whole CTU parameters will be re-encoded at the end of this function |
730 | | |
731 | 21.0k | for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++) |
732 | 17.5k | { |
733 | 17.5k | m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma ); |
734 | 17.5k | m_CABACEstimator->resetBits(); |
735 | 17.5k | previousFracBits = 0; |
736 | 17.5k | cost = 0; |
737 | | |
738 | 52.5k | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
739 | 35.0k | { |
740 | 35.0k | const ComponentID component = ComponentID(componentIndex); |
741 | 35.0k | if(!sliceEnabled[component]) |
742 | 0 | { |
743 | 0 | testOffset[component].modeIdc = SAO_MODE_OFF; |
744 | 0 | dist[component]= 0; |
745 | 0 | continue; |
746 | 0 | } |
747 | 35.0k | testOffset[component].modeIdc = SAO_MODE_NEW; |
748 | 35.0k | testOffset[component].typeIdc = typeIdc; |
749 | | |
750 | | //derive offset & get distortion |
751 | 35.0k | deriveOffsets(component, bitDepths[CH_C], typeIdc, blkStats[ctuRsAddr][component][typeIdc], testOffset[component].offset, testOffset[component].typeAuxInfo); |
752 | 35.0k | invertQuantOffsets(component, typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset); |
753 | 35.0k | dist[component] = getDistortion(bitDepths[CH_C], typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][component][typeIdc]); |
754 | 35.0k | m_CABACEstimator->sao_offset_pars( testOffset[component], component, sliceEnabled[component], bitDepths[CH_C] ); |
755 | 35.0k | const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits(); |
756 | 35.0k | cost += dist[component] + (m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits)); |
757 | 35.0k | previousFracBits = currentFracBits; |
758 | 35.0k | } |
759 | | |
760 | 17.5k | if(cost < minCost) |
761 | 1 | { |
762 | 1 | minCost = cost; |
763 | 3 | for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++) |
764 | 2 | { |
765 | 2 | modeDist[componentIndex] = dist[componentIndex]; |
766 | 2 | modeParam[componentIndex] = testOffset[componentIndex]; |
767 | 2 | } |
768 | 1 | } |
769 | | |
770 | 17.5k | } // SAO_TYPE loop |
771 | | |
772 | | //----- re-gen rate & normalized cost----// |
773 | 3.50k | modeNormCost = 0; |
774 | 14.0k | for(uint32_t componentIndex = COMP_Y; componentIndex < numberOfComponents; componentIndex++) |
775 | 10.5k | { |
776 | 10.5k | modeNormCost += (double)modeDist[componentIndex] / m_lambda[componentIndex]; |
777 | 10.5k | } |
778 | | |
779 | 3.50k | m_CABACEstimator->getCtx() = SAOCtx( ctxStartBlk ); |
780 | 3.50k | m_CABACEstimator->resetBits(); |
781 | 3.50k | m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false ); |
782 | 3.50k | modeNormCost += FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
783 | 3.50k | } |
784 | | |
785 | | 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 ) |
786 | 3.50k | { |
787 | 3.50k | modeNormCost = MAX_DOUBLE; |
788 | | |
789 | 3.50k | double cost; |
790 | 3.50k | SAOBlkParam testBlkParam; |
791 | 3.50k | const int numberOfComponents = m_numberOfComponents; |
792 | | |
793 | 3.50k | const TempCtx ctxStart ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) ); |
794 | 3.50k | TempCtx ctxBest ( m_CtxCache ); |
795 | | |
796 | 10.5k | for(int mergeType=0; mergeType< NUM_SAO_MERGE_TYPES; mergeType++) |
797 | 7.00k | { |
798 | 7.00k | if(mergeList[mergeType] == NULL) |
799 | 3.95k | { |
800 | 3.95k | continue; |
801 | 3.95k | } |
802 | | |
803 | 3.04k | testBlkParam = *(mergeList[mergeType]); |
804 | | //normalized distortion |
805 | 3.04k | double normDist=0; |
806 | 12.1k | for(int compIdx = 0; compIdx < numberOfComponents; compIdx++) |
807 | 9.14k | { |
808 | 9.14k | testBlkParam[compIdx].modeIdc = SAO_MODE_MERGE; |
809 | 9.14k | testBlkParam[compIdx].typeIdc = mergeType; |
810 | | |
811 | 9.14k | SAOOffset& mergedOffsetParam = (*(mergeList[mergeType]))[compIdx]; |
812 | | |
813 | 9.14k | if( mergedOffsetParam.modeIdc != SAO_MODE_OFF) |
814 | 11 | { |
815 | | //offsets have been reconstructed. Don't call inversed quantization function. |
816 | 11 | normDist += (((double)getDistortion(bitDepths[toChannelType(ComponentID(compIdx))], mergedOffsetParam.typeIdc, mergedOffsetParam.typeAuxInfo, mergedOffsetParam.offset, blkStats[ctuRsAddr][compIdx][mergedOffsetParam.typeIdc])) |
817 | 11 | /m_lambda[compIdx] ); |
818 | 11 | } |
819 | 9.14k | } |
820 | | |
821 | | //rate |
822 | 3.04k | m_CABACEstimator->getCtx() = SAOCtx( ctxStart ); |
823 | 3.04k | m_CABACEstimator->resetBits(); |
824 | 3.04k | m_CABACEstimator->sao_block_pars( testBlkParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false ); |
825 | 3.04k | double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits(); |
826 | 3.04k | cost = normDist+rate; |
827 | | |
828 | 3.04k | if(cost < modeNormCost) |
829 | 2.34k | { |
830 | 2.34k | modeNormCost = cost; |
831 | 2.34k | modeParam = testBlkParam; |
832 | 2.34k | ctxBest = SAOCtx( m_CABACEstimator->getCtx() ); |
833 | 2.34k | } |
834 | 3.04k | } |
835 | 3.50k | if( modeNormCost < MAX_DOUBLE ) |
836 | 2.34k | { |
837 | 2.34k | m_CABACEstimator->getCtx() = SAOCtx( ctxBest ); |
838 | 2.34k | } |
839 | 3.50k | } |
840 | | |
841 | | void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int channelBitDepth, SAOStatData* statsDataTypes |
842 | | , Pel* srcBlk, Pel* orgBlk, int srcStride, int orgStride, int width, int height |
843 | | , bool isLeftAvail, bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail ) |
844 | 10.5k | { |
845 | 10.5k | int x, startX, startY, endX, endY, edgeType, firstLineStartX, firstLineEndX; |
846 | 10.5k | int64_t *diff, *count; |
847 | 10.5k | Pel* srcLine, *orgLine; |
848 | 10.5k | const int skipLinesR = compIdx == COMP_Y ? 5 : 3; |
849 | 10.5k | const int skipLinesB = compIdx == COMP_Y ? 4 : 2; |
850 | | |
851 | 63.0k | for(int typeIdx=0; typeIdx< NUM_SAO_NEW_TYPES; typeIdx++) |
852 | 52.5k | { |
853 | 52.5k | SAOStatData& statsData= statsDataTypes[typeIdx]; |
854 | 52.5k | statsData.reset(); |
855 | 52.5k | srcLine = srcBlk; |
856 | 52.5k | orgLine = orgBlk; |
857 | 52.5k | diff = statsData.diff; |
858 | 52.5k | count = statsData.count; |
859 | 52.5k | switch(typeIdx) |
860 | 52.5k | { |
861 | 10.5k | case SAO_TYPE_EO_0: |
862 | 10.5k | { |
863 | 10.5k | endY = isBelowAvail ? (height - skipLinesB) : height; |
864 | 10.5k | startX = (isLeftAvail ? 0 : 1); |
865 | 10.5k | endX = (isRightAvail ? (width - skipLinesR) : (width - 1)); |
866 | 10.5k | calcSaoStatisticsEo0(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff); |
867 | 10.5k | } |
868 | 10.5k | break; |
869 | 10.5k | case SAO_TYPE_EO_90: |
870 | 10.5k | { |
871 | 10.5k | int8_t *signUpLine = &m_signLineBuf1[0]; |
872 | 10.5k | startX = 0; |
873 | 10.5k | startY = isAboveAvail ? 0 : 1; |
874 | 10.5k | endX = (isRightAvail ? (width - skipLinesR) : width); |
875 | 10.5k | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
876 | 10.5k | if (!isAboveAvail) |
877 | 5.75k | { |
878 | 5.75k | srcLine += srcStride; |
879 | 5.75k | orgLine += orgStride; |
880 | 5.75k | } |
881 | 10.5k | calcSaoStatisticsEo90(width,endX,startY,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine); |
882 | 10.5k | } |
883 | 10.5k | break; |
884 | 10.5k | case SAO_TYPE_EO_135: |
885 | 10.5k | { |
886 | 10.5k | diff +=2; |
887 | 10.5k | count+=2; |
888 | 10.5k | int8_t *signUpLine, *signDownLine; |
889 | 10.5k | signUpLine = &m_signLineBuf1[0]; |
890 | 10.5k | signDownLine= &m_signLineBuf2[0]; |
891 | 10.5k | startX = isLeftAvail ? 0 : 1; |
892 | 10.5k | endX = isRightAvail ? (width - skipLinesR): (width - 1); |
893 | 10.5k | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
894 | | //prepare 2nd line's upper sign |
895 | 10.5k | Pel* srcLineBelow = srcLine + srcStride; |
896 | 592k | for (x=startX; x<endX+1; x++) |
897 | 581k | { |
898 | 581k | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x-1]); |
899 | 581k | } |
900 | | //1st line |
901 | 10.5k | Pel* srcLineAbove = srcLine - srcStride; |
902 | 10.5k | firstLineStartX = isAboveLeftAvail ? 0 : 1; |
903 | 10.5k | firstLineEndX = isAboveAvail ? endX : 1; |
904 | 273k | for(x=firstLineStartX; x<firstLineEndX; x++) |
905 | 262k | { |
906 | 262k | edgeType = sgn(srcLine[x] - srcLineAbove[x-1]) - signUpLine[x+1]; |
907 | 262k | diff [edgeType] += (orgLine[x] - srcLine[x]); |
908 | 262k | count[edgeType] ++; |
909 | 262k | } |
910 | 10.5k | srcLine += srcStride; |
911 | 10.5k | orgLine += orgStride; |
912 | 10.5k | calcSaoStatisticsEo135(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine,signDownLine); |
913 | 10.5k | } |
914 | 10.5k | break; |
915 | 10.5k | case SAO_TYPE_EO_45: |
916 | 10.5k | { |
917 | 10.5k | diff +=2; |
918 | 10.5k | count+=2; |
919 | 10.5k | int8_t *signUpLine = &m_signLineBuf1[1]; |
920 | | |
921 | 10.5k | startX = isLeftAvail ? 0 : 1; |
922 | 10.5k | endX = isRightAvail ? (width - skipLinesR) : (width - 1); |
923 | 10.5k | endY = isBelowAvail ? (height - skipLinesB) : (height - 1); |
924 | | |
925 | | //prepare 2nd line upper sign |
926 | 10.5k | Pel* srcLineBelow = srcLine + srcStride; |
927 | 592k | for (x=startX-1; x<endX; x++) |
928 | 581k | { |
929 | 581k | signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]); |
930 | 581k | } |
931 | | //first line |
932 | 10.5k | Pel* srcLineAbove = srcLine - srcStride; |
933 | 10.5k | firstLineStartX = isAboveAvail ? startX : endX; |
934 | 10.5k | firstLineEndX = (!isRightAvail && isAboveRightAvail) ? width : endX; |
935 | 273k | for(x=firstLineStartX; x<firstLineEndX; x++) |
936 | 262k | { |
937 | 262k | edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) - signUpLine[x-1]; |
938 | 262k | diff [edgeType] += (orgLine[x] - srcLine[x]); |
939 | 262k | count[edgeType] ++; |
940 | 262k | } |
941 | 10.5k | srcLine += srcStride; |
942 | 10.5k | orgLine += orgStride; |
943 | 10.5k | calcSaoStatisticsEo45(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine); |
944 | 10.5k | } |
945 | 10.5k | break; |
946 | 10.5k | case SAO_TYPE_BO: |
947 | 10.5k | { |
948 | 10.5k | startX = 0; |
949 | 10.5k | endX = isRightAvail ? (width - skipLinesR) : width; |
950 | 10.5k | endY = isBelowAvail ? (height- skipLinesB) : height; |
951 | 10.5k | calcSaoStatisticsBo(width,endX,endY,srcLine,orgLine,srcStride,orgStride,channelBitDepth,count,diff); |
952 | 10.5k | } |
953 | 10.5k | break; |
954 | 0 | default: |
955 | 0 | { |
956 | 0 | THROW("Not a supported SAO type"); |
957 | 0 | } |
958 | 52.5k | } |
959 | 52.5k | } |
960 | 10.5k | } |
961 | | |
962 | | void EncSampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility(CodingStructure& cs, const Position& pos, bool& isLeftAvail, bool& isAboveAvail, bool& isAboveLeftAvail) const |
963 | 3.50k | { |
964 | 3.50k | const bool isLoopFiltAcrossSlicePPS = cs.pps->loopFilterAcrossSlicesEnabled; |
965 | 3.50k | const bool isLoopFiltAcrossTilePPS = cs.pps->loopFilterAcrossTilesEnabled; |
966 | | |
967 | 3.50k | const int width = cs.pcv->maxCUSize; |
968 | 3.50k | const int height = cs.pcv->maxCUSize; |
969 | 3.50k | const CodingUnit* cuCurr = cs.getCU(pos, CH_L, TREE_D); |
970 | 3.50k | const int ctuX = pos.x >> cs.pcv->maxCUSizeLog2; |
971 | 3.50k | const int ctuY = pos.y >> cs.pcv->maxCUSizeLog2; |
972 | 3.50k | const PPS* pps = cs.slice->pps; |
973 | 3.50k | const CodingUnit* cuLeft = ctuX > 0 && pps->canFilterCtuBdry( ctuX, ctuY, -1, 0 ) ? cs.getCU(pos.offset(-width, 0), CH_L, TREE_D): nullptr; |
974 | 3.50k | const CodingUnit* cuAbove = ctuY > 0 && pps->canFilterCtuBdry( ctuX, ctuY, 0, -1 ) ? cs.getCU(pos.offset(0, -height), CH_L, TREE_D): nullptr; |
975 | 3.50k | const CodingUnit* cuAboveLeft = ctuY > 0 && ctuX > 0 && pps->canFilterCtuBdry( ctuX, ctuY, -1,-1 ) ? cs.getCU(pos.offset(-width, -height), CH_L, TREE_D): nullptr; |
976 | | |
977 | 3.50k | if (!isLoopFiltAcrossSlicePPS) |
978 | 0 | { |
979 | 0 | isLeftAvail = (cuLeft == NULL) ? false : CU::isSameSlice(*cuCurr, *cuLeft); |
980 | 0 | isAboveAvail = (cuAbove == NULL) ? false : CU::isSameSlice(*cuCurr, *cuAbove); |
981 | 0 | isAboveLeftAvail = (cuAboveLeft == NULL) ? false : CU::isSameSlice(*cuCurr, *cuAboveLeft); |
982 | 0 | } |
983 | 3.50k | else |
984 | 3.50k | { |
985 | 3.50k | isLeftAvail = (cuLeft != NULL); |
986 | 3.50k | isAboveAvail = (cuAbove != NULL); |
987 | 3.50k | isAboveLeftAvail = (cuAboveLeft != NULL); |
988 | 3.50k | } |
989 | | |
990 | 3.50k | if (!isLoopFiltAcrossTilePPS) |
991 | 0 | { |
992 | 0 | isLeftAvail = (!isLeftAvail) ? false : CU::isSameTile(*cuCurr, *cuLeft); |
993 | 0 | isAboveAvail = (!isAboveAvail) ? false : CU::isSameTile(*cuCurr, *cuAbove); |
994 | 0 | isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameTile(*cuCurr, *cuAboveLeft); |
995 | 0 | } |
996 | | |
997 | | |
998 | 3.50k | SubPic curSubPic = cs.pps->getSubPicFromCU(*cuCurr); |
999 | 3.50k | if (!curSubPic.loopFilterAcrossSubPicEnabled ) |
1000 | 0 | { |
1001 | 0 | isLeftAvail = (!isLeftAvail) ? false : CU::isSameSubPic(*cuCurr, *cuLeft); |
1002 | 0 | isAboveAvail = (!isAboveAvail) ? false : CU::isSameSubPic(*cuCurr, *cuAbove); |
1003 | 0 | isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameSubPic(*cuCurr, *cuAboveLeft); |
1004 | 0 | } |
1005 | | |
1006 | 3.50k | } |
1007 | | |
1008 | | } // namespace vvenc |
1009 | | |
1010 | | //! \} |
1011 | | |