1 /* -----------------------------------------------------------------------------
2 The copyright in this software is being made available under the 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 For any license concerning other Intellectual Property rights than the software,
8 especially patent licenses, a separate Agreement needs to be closed.
9 For more information please contact:
10 
11 Fraunhofer Heinrich Hertz Institute
12 Einsteinufer 37
13 10587 Berlin, Germany
14 www.hhi.fraunhofer.de/vvc
15 vvc@hhi.fraunhofer.de
16 
17 Copyright (c) 2018-2021, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V.
18 All rights reserved.
19 
20 Redistribution and use in source and binary forms, with or without
21 modification, are permitted provided that the following conditions are met:
22 
23  * Redistributions of source code must retain the above copyright notice,
24    this list of conditions and the following disclaimer.
25  * Redistributions in binary form must reproduce the above copyright notice,
26    this list of conditions and the following disclaimer in the documentation
27    and/or other materials provided with the distribution.
28  * Neither the name of Fraunhofer nor the names of its contributors may
29    be used to endorse or promote products derived from this software without
30    specific prior written permission.
31 
32 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
33 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
36 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
37 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
38 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
39 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
40 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
42 THE POSSIBILITY OF SUCH DAMAGE.
43 
44 
45 ------------------------------------------------------------------------------------------- */
46 
47 /** \file     Reshape.cpp
48     \brief    common reshaper class
49 */
50 #include "Reshape.h"
51 #include <stdio.h>
52 #include <string.h>
53 #include <math.h>
54 #include <UnitTools.h>
55 #include "CommonLib/TimeProfiler.h"
56 
57 namespace vvdec
58 {
59 
60 // ====================================================================================================================
61 // Constructor / destructor / create / destroy
62 // ====================================================================================================================
63 
Reshape()64 Reshape::Reshape()
65 {
66   m_fwdLUT = nullptr;
67   m_invLUT = nullptr;
68   m_chromaScale = 1 << CSCALE_FP_PREC;
69   m_vpduX = -1;
70   m_vpduY = -1;
71 }
72 
~Reshape()73 Reshape::~Reshape()
74 {
75   destroy();
76 }
77 
createDec(int bitDepth)78 void  Reshape::createDec(int bitDepth)
79 {
80   m_lumaBD = bitDepth;
81   m_reshapeLUTSize = 1 << m_lumaBD;
82   m_initCW = m_reshapeLUTSize / PIC_CODE_CW_BINS;
83   if( !m_fwdLUT )
84   {
85     m_fwdLUT = ( Pel* ) xMalloc( Pel, m_reshapeLUTSize + 1 );
86     memset( m_fwdLUT, 0, ( m_reshapeLUTSize + 1 ) * sizeof( Pel ) );
87   }
88   if( !m_invLUT )
89   {
90     m_invLUT = ( Pel* ) xMalloc( Pel, m_reshapeLUTSize + 1 );
91     memset( m_invLUT, 0, ( m_reshapeLUTSize + 1 ) * sizeof( Pel ) );
92   }
93   if (m_binCW.empty())
94     m_binCW.resize(PIC_CODE_CW_BINS, 0);
95   if (m_inputPivot.empty())
96     m_inputPivot.resize(PIC_CODE_CW_BINS + 1, 0);
97   if (m_fwdScaleCoef.empty())
98     m_fwdScaleCoef.resize(PIC_CODE_CW_BINS, 1 << FP_PREC);
99   if (m_invScaleCoef.empty())
100     m_invScaleCoef.resize(PIC_CODE_CW_BINS, 1 << FP_PREC);
101   if (m_reshapePivot.empty())
102     m_reshapePivot.resize(PIC_CODE_CW_BINS + 1, 0);
103   if (m_chromaAdjHelpLUT.empty())
104     m_chromaAdjHelpLUT.resize(PIC_CODE_CW_BINS, 1<<CSCALE_FP_PREC);
105 }
106 
destroy()107 void  Reshape::destroy()
108 {
109   xFree( m_fwdLUT );
110   m_fwdLUT = nullptr;
111   xFree( m_invLUT );
112   m_invLUT = nullptr;
113 }
114 
initSlice(int nalUnitLayerId,const PicHeader & picHeader,const VPS & vps)115 void  Reshape::initSlice( int nalUnitLayerId, const PicHeader& picHeader, const VPS& vps )
116 {
117   if( picHeader.getLmcsEnabledFlag() )
118   {
119     if( nalUnitLayerId != picHeader.getLmcsAPS()->getLayerId() )
120     {
121       for (int i = 0; i < vps.getNumOutputLayerSets(); i++ )
122       {
123         bool isCurrLayerInOls = false;
124         bool isRefLayerInOls = false;
125         for( int j = vps.getNumLayersInOls(i) - 1; j >= 0; j-- )
126         {
127           if( vps.getLayerIdInOls(i, j) == nalUnitLayerId )
128           {
129             isCurrLayerInOls = true;
130           }
131           if( vps.getLayerIdInOls(i, j) == picHeader.getLmcsAPS()->getLayerId() )
132           {
133             isRefLayerInOls = true;
134           }
135         }
136         CHECK( isCurrLayerInOls && !isRefLayerInOls, "When VCL NAl unit in layer A refers to APS in layer B, all OLS that contains layer A shall also contains layer B" );
137       }
138     }
139 
140     SliceReshapeInfo& sInfo = picHeader.getLmcsAPS()->getReshaperAPSInfo();
141     m_sliceReshapeInfo.sliceReshaperEnableFlag       = true;
142     m_sliceReshapeInfo.sliceReshaperModelPresentFlag = true;
143     m_sliceReshapeInfo.enableChromaAdj               = picHeader.getLmcsChromaResidualScaleFlag();
144     m_sliceReshapeInfo.reshaperModelMaxBinIdx        = sInfo.reshaperModelMaxBinIdx;
145     m_sliceReshapeInfo.reshaperModelMinBinIdx        = sInfo.reshaperModelMinBinIdx;
146     m_sliceReshapeInfo.maxNbitsNeededDeltaCW         = sInfo.maxNbitsNeededDeltaCW;
147     m_sliceReshapeInfo.chrResScalingOffset           = sInfo.chrResScalingOffset;
148     memcpy( m_sliceReshapeInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof( int ) * ( PIC_CODE_CW_BINS ) );
149     constructReshaper();
150   }
151   else
152   {
153     m_sliceReshapeInfo.sliceReshaperEnableFlag       = false;
154     m_sliceReshapeInfo.enableChromaAdj               = false;
155     m_sliceReshapeInfo.sliceReshaperModelPresentFlag = false;
156   }
157   m_vpduX = -1;
158   m_vpduY = -1;
159 }
160 
getCTUFlag(const Slice & slice) const161 bool Reshape::getCTUFlag( const Slice& slice ) const
162 {
163   if( (slice.getSliceType() == I_SLICE) && m_sliceReshapeInfo.sliceReshaperEnableFlag )
164   {
165     return false;
166   }
167   else
168   {
169     return m_sliceReshapeInfo.sliceReshaperEnableFlag;
170   }
171 }
172 
rspLine(CodingStructure & cs,int ln,const int offset) const173 void Reshape::rspLine( CodingStructure &cs, int ln, const int offset ) const
174 {
175   if( !( cs.sps->getUseReshaper() && m_sliceReshapeInfo.sliceReshaperEnableFlag ) )
176   {
177     return;
178   }
179   PROFILER_SCOPE_AND_STAGE_EXT( 1, g_timeProfiler, P_RESHAPER, cs, CH_L );
180 
181   const PreCalcValues &pcv = *cs.pcv;
182 
183   const bool firstLine = ln == 0;
184 
185 //  const int lh = frstLine ? pcv.maxCUHeight + ( offset ) : pcv.maxCUHeight;
186 
187   int lw   = pcv.lumaWidth;
188   int yPos = firstLine ? 0 : ln * pcv.maxCUHeight + offset;
189   int lh   = firstLine ? pcv.maxCUHeight + offset : std::min( pcv.lumaHeight - yPos, pcv.maxCUHeight );
190   PelBuf picYuvRec = cs.getRecoBuf( COMPONENT_Y ).subBuf( Position( 0, yPos ), Size( lw, lh ) );
191   picYuvRec.rspSignal( m_invLUT );
192 }
193 
rspCtu(CodingStructure & cs,int col,int ln,const int offset) const194 void Reshape::rspCtu( CodingStructure &cs, int col, int ln, const int offset ) const
195 {
196   if( !( cs.sps->getUseReshaper() && m_sliceReshapeInfo.sliceReshaperEnableFlag ) )
197   {
198     return;
199   }
200 
201   const Slice* slice = cs.getCtuData( col, ln ).cuPtr[0][0]->slice;
202   if( !slice->getLmcsEnabledFlag() )
203 
204   {
205     return;
206   }
207 
208   PROFILER_SCOPE_AND_STAGE_EXT( 1, g_timeProfiler, P_RESHAPER, cs, CH_L );
209 
210   const PreCalcValues &pcv = *cs.pcv;
211 
212   const bool firstLine = ln == 0;
213 
214 //  const int lh = frstLine ? pcv.maxCUHeight + ( offset ) : pcv.maxCUHeight;
215 
216   int xPos = pcv.maxCUWidth * col;
217   int lw   = std::min( pcv.lumaWidth - xPos, pcv.maxCUWidth );
218 
219   int yPos = firstLine ? 0 : ln * pcv.maxCUHeight + offset;
220   int lh   = firstLine ? pcv.maxCUHeight + offset : std::min( pcv.lumaHeight - yPos, pcv.maxCUHeight );
221 
222   PelBuf picYuvRec = cs.getRecoBuf( COMPONENT_Y ).subBuf( Position( xPos, yPos ), Size( lw, lh ) );
223   picYuvRec.rspSignal( m_invLUT );
224 }
225 
226 
227 
228 /** compute chroma residuce scale for TU
229 * \param average luma pred of TU
230 * \return chroma residue scale
231 */
calculateChromaAdj(Pel avgLuma) const232 int  Reshape::calculateChromaAdj(Pel avgLuma) const
233 {
234   int iAdj = m_chromaAdjHelpLUT[getPWLIdxInv(avgLuma)];
235   return(iAdj);
236 }
237 
238 /** compute chroma residuce scale for TU
239 * \param average luma pred of TU
240 * \return chroma residue scale
241 */
calculateChromaAdjVpduNei(TransformUnit & tu,const Position pos)242 int  Reshape::calculateChromaAdjVpduNei(TransformUnit &tu, const Position pos)
243 {
244   CodingStructure &cs = *tu.cu->cs;
245   int xPos = pos.x;
246   int yPos = pos.y;
247   int ctuSize = cs.sps->getCTUSize();
248   int numNeighbor = std::min(64, ctuSize);
249   int numNeighborLog = getLog2(numNeighbor);
250   if (ctuSize == 128)
251   {
252     xPos &= ~63;
253     yPos &= ~63;
254   }
255   else
256   {
257     xPos &= ~( ctuSize - 1 );
258     yPos &= ~( ctuSize - 1 );
259   }
260 
261   if( isVPDUprocessed( xPos, yPos ) )
262   {
263     return getChromaScale();
264   }
265   else
266   {
267     setVPDULoc(xPos, yPos);
268     Position topLeft(xPos, yPos);
269     CodingUnit *topLeftLuma;
270     const CodingUnit *cuAbove, *cuLeft;
271 
272     topLeftLuma = cs.getCU( topLeft, CHANNEL_TYPE_LUMA );
273     cuAbove     = cs.getCURestricted( topLeftLuma->lumaPos().offset( 0, -1 ), *topLeftLuma, CHANNEL_TYPE_LUMA, topLeftLuma->ly() == yPos ? topLeftLuma : topLeftLuma->above );
274     cuLeft      = cs.getCURestricted( topLeftLuma->lumaPos().offset( -1, 0 ), *topLeftLuma, CHANNEL_TYPE_LUMA, topLeftLuma->lx() == xPos ? topLeftLuma : topLeftLuma->left  );
275 
276     xPos = topLeftLuma->lumaPos().x;
277     yPos = topLeftLuma->lumaPos().y;
278 
279     CompArea lumaArea = CompArea(COMPONENT_Y, topLeftLuma->lumaPos(), topLeftLuma->lumaSize());
280     PelBuf piRecoY = cs.picture->getRecoBuf(lumaArea);
281     ptrdiff_t strideY = piRecoY.stride;
282     int chromaScale = (1 << CSCALE_FP_PREC);
283     int lumaValue = -1;
284 
285     Pel* recSrc0 = piRecoY.bufAt(0, 0);
286     const uint32_t picH = tu.cu->cs->picture->lheight();
287     const uint32_t picW = tu.cu->cs->picture->lwidth();
288     const Pel   valueDC = 1 << (tu.cu->sps->getBitDepth(CHANNEL_TYPE_LUMA) - 1);
289     int32_t recLuma = 0;
290     int pelnum = 0;
291     if (cuLeft != nullptr)
292     {
293       for (int i = 0; i < numNeighbor; i++)
294       {
295         int k = (yPos + i) >= picH ? (picH - yPos - 1) : i;
296         recLuma += recSrc0[-1 + k * strideY];
297         pelnum++;
298       }
299     }
300     if (cuAbove != nullptr)
301     {
302       for (int i = 0; i < numNeighbor; i++)
303       {
304         int k = (xPos + i) >= picW ? (picW - xPos - 1) : i;
305         recLuma += recSrc0[-strideY + k];
306         pelnum++;
307       }
308     }
309     if (pelnum == numNeighbor)
310     {
311       lumaValue = (recLuma + (1 << (numNeighborLog - 1))) >> numNeighborLog;
312     }
313     else if (pelnum == (numNeighbor << 1))
314     {
315       lumaValue = (recLuma + (1 << numNeighborLog)) >> (numNeighborLog + 1);
316     }
317     else
318     {
319       CHECK(pelnum != 0, "");
320       lumaValue = valueDC;
321     }
322     chromaScale = calculateChromaAdj(lumaValue);
323     setChromaScale(chromaScale);
324     return(chromaScale);
325   }
326 }
327 /** find inx of PWL for inverse mapping
328 * \param average luma pred of TU
329 * \return idx of PWL for inverse mapping
330 */
getPWLIdxInv(int lumaVal) const331 int Reshape::getPWLIdxInv(int lumaVal) const
332 {
333   int idxS = 0;
334   for (idxS = m_sliceReshapeInfo.reshaperModelMinBinIdx; (idxS <= m_sliceReshapeInfo.reshaperModelMaxBinIdx); idxS++)
335   {
336     if (lumaVal < m_reshapePivot[idxS + 1])     break;
337   }
338   return std::min(idxS, PIC_CODE_CW_BINS-1);
339 }
340 
341 /**
342 -copy Slice reshaper info structure
343 \param   tInfo describing the target Slice reshaper info structure
344 \param   sInfo describing the source Slice reshaper info structure
345 */
copySliceReshaperInfo(SliceReshapeInfo & tInfo,SliceReshapeInfo & sInfo)346 void Reshape::copySliceReshaperInfo(SliceReshapeInfo& tInfo, SliceReshapeInfo& sInfo)
347 {
348   tInfo.sliceReshaperModelPresentFlag = sInfo.sliceReshaperModelPresentFlag;
349   if (sInfo.sliceReshaperModelPresentFlag)
350   {
351     tInfo.reshaperModelMaxBinIdx = sInfo.reshaperModelMaxBinIdx;
352     tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
353     memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
354     tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
355     tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
356   }
357   tInfo.sliceReshaperEnableFlag = sInfo.sliceReshaperEnableFlag;
358   if (sInfo.sliceReshaperEnableFlag)
359     tInfo.enableChromaAdj = sInfo.enableChromaAdj;
360   else
361     tInfo.enableChromaAdj = 0;
362 }
363 
364 /** Construct reshaper from syntax
365 * \param void
366 * \return void
367 */
constructReshaper()368 void Reshape::constructReshaper()
369 {
370   int pwlFwdLUTsize = PIC_CODE_CW_BINS;
371   int pwlFwdBinLen = m_reshapeLUTSize / PIC_CODE_CW_BINS;
372 
373   for (int i = 0; i < m_sliceReshapeInfo.reshaperModelMinBinIdx; i++)
374     m_binCW[i] = 0;
375   for (int i = m_sliceReshapeInfo.reshaperModelMaxBinIdx + 1; i < PIC_CODE_CW_BINS; i++)
376     m_binCW[i] = 0;
377   for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++)
378     m_binCW[i] = (uint16_t)(m_sliceReshapeInfo.reshaperModelBinCWDelta[i] + (int)m_initCW);
379 
380   for (int i = 0; i < pwlFwdLUTsize; i++)
381   {
382     m_reshapePivot[i + 1] = m_reshapePivot[i] + m_binCW[i];
383     m_inputPivot[i + 1] = m_inputPivot[i] + m_initCW;
384     m_fwdScaleCoef[i] = ((int32_t)m_binCW[i] * (1 << FP_PREC) + (1 << (getLog2(pwlFwdBinLen) - 1))) >> getLog2(pwlFwdBinLen);
385     if (m_binCW[i] == 0)
386     {
387       m_invScaleCoef[i] = 0;
388       m_chromaAdjHelpLUT[i] = 1 << CSCALE_FP_PREC;
389     }
390     else
391     {
392       m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]);
393       m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / ( m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset ) );
394     }
395   }
396   for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++)
397   {
398     int idxY = lumaSample / m_initCW;
399     int tempVal = m_reshapePivot[idxY] + ((m_fwdScaleCoef[idxY] * (lumaSample - m_inputPivot[idxY]) + (1 << (FP_PREC - 1))) >> FP_PREC);
400     m_fwdLUT[lumaSample] = Clip3((Pel)0, (Pel)((1 << m_lumaBD) - 1), (Pel)(tempVal));
401 
402     int idxYInv = getPWLIdxInv(lumaSample);
403     int invSample = m_inputPivot[idxYInv] + ((m_invScaleCoef[idxYInv] * (lumaSample - m_reshapePivot[idxYInv]) + (1 << (FP_PREC - 1))) >> FP_PREC);
404     m_invLUT[lumaSample] = Clip3((Pel)0, (Pel)((1 << m_lumaBD) - 1), (Pel)(invSample));
405   }
406 }
407 
408 }
409