/src/vvdec/source/Lib/CommonLib/MatrixIntraPrediction.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) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC 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 | | /** \file MatrixIntraPrediction.cpp |
44 | | \brief matrix-based intra prediction class |
45 | | */ |
46 | | |
47 | | #include "MatrixIntraPrediction.h" |
48 | | #include "dtrace_next.h" |
49 | | |
50 | | #include "UnitTools.h" |
51 | | #include "MipData.h" |
52 | | |
53 | | namespace vvdec |
54 | | { |
55 | | |
56 | | namespace Mip |
57 | | { |
58 | | PredictorMIP::PredictorMIP(): |
59 | 0 | m_blockSize( 0, 0 ), |
60 | 0 | m_sizeId( 0 ), |
61 | 0 | m_reducedBdrySize( 0 ), |
62 | 0 | m_reducedPredSize( 0 ), |
63 | 0 | m_upsmpFactorHor( 0 ), |
64 | 0 | m_upsmpFactorVer( 0 ) |
65 | 0 | { |
66 | 0 | } |
67 | | |
68 | | void PredictorMIP::deriveBoundaryData(const CPelBuf& src, const Area& block, const int bitDepth) |
69 | 0 | { |
70 | | // Step 1: Save block size and calculate dependent values |
71 | 0 | initPredBlockParams(block); |
72 | | |
73 | | // Step 2: Get the input data (left and top reference samples) |
74 | | // m_refSamplesTop.resize(block.width); |
75 | 0 | for (int x = 0; x < block.width; x++) |
76 | 0 | { |
77 | 0 | m_refSamplesTop[x] = src.at(x + 1, 0); |
78 | 0 | } |
79 | | |
80 | | // m_refSamplesLeft.resize(block.height); |
81 | 0 | for (int y = 0; y < block.height; y++) |
82 | 0 | { |
83 | 0 | m_refSamplesLeft[y] = src.at(0, y + 1); |
84 | 0 | } |
85 | | |
86 | | // Step 3: Compute the reduced boundary via Haar-downsampling (input for the prediction) |
87 | | // m_reducedBoundary .resize( m_reducedBoundarySize.width + m_reducedBoundarySize.height ); |
88 | | // m_reducedBoundaryTransposed.resize( m_reducedBoundarySize.width + m_reducedBoundarySize.height ); |
89 | | |
90 | | // m_reducedBoundary .resize( inputSize ); |
91 | | // m_reducedBoundaryTransposed.resize( inputSize ); |
92 | | |
93 | 0 | Pel* const topReduced = m_reducedBoundary; |
94 | 0 | boundaryDownsampling1D( topReduced, m_refSamplesTop, block.width, m_reducedBdrySize ); |
95 | | |
96 | 0 | Pel* const leftReduced = m_reducedBoundary + m_reducedBdrySize; |
97 | 0 | boundaryDownsampling1D( leftReduced, m_refSamplesLeft, block.height, m_reducedBdrySize ); |
98 | | |
99 | 0 | Pel* const leftReducedTransposed = m_reducedBoundaryTransposed; |
100 | 0 | Pel* const topReducedTransposed = m_reducedBoundaryTransposed + m_reducedBdrySize; |
101 | 0 | for( int x = 0; x < m_reducedBdrySize; x++ ) |
102 | 0 | { |
103 | 0 | topReducedTransposed[x] = topReduced[x]; |
104 | 0 | } |
105 | 0 | for( int y = 0; y < m_reducedBdrySize; y++ ) |
106 | 0 | { |
107 | 0 | leftReducedTransposed[y] = leftReduced[y]; |
108 | 0 | } |
109 | | |
110 | | // Step 4: Rebase the reduced boundary |
111 | 0 | const int inputSize = 2 * m_reducedBdrySize; |
112 | | |
113 | 0 | m_inputOffset = m_reducedBoundary[0]; |
114 | 0 | m_inputOffsetTransp = m_reducedBoundaryTransposed[0]; |
115 | | |
116 | 0 | const bool hasFirstCol = (m_sizeId < 2); |
117 | 0 | m_reducedBoundary [0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffset ) : 0; // first column of matrix not needed for large blocks |
118 | 0 | m_reducedBoundaryTransposed[0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffsetTransp) : 0; |
119 | 0 | for (int i = 1; i < inputSize; i++) |
120 | 0 | { |
121 | 0 | m_reducedBoundary [i] -= m_inputOffset; |
122 | 0 | m_reducedBoundaryTransposed[i] -= m_inputOffsetTransp; |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | void PredictorMIP::getPrediction(Pel* const result, const int modeIdx, const bool transpose, const int bitDepth) |
127 | 0 | { |
128 | 0 | const bool needUpsampling = ( m_upsmpFactorHor > 1 ) || ( m_upsmpFactorVer > 1 ); |
129 | |
|
130 | 0 | const uint8_t* matrix = getMatrixData( modeIdx ); |
131 | |
|
132 | 0 | Pel bufReducedPred[MIP_MAX_REDUCED_OUTPUT_SAMPLES]; |
133 | 0 | Pel* const reducedPred = needUpsampling ? bufReducedPred : result; |
134 | 0 | const Pel* const reducedBoundary = transpose ? m_reducedBoundaryTransposed : m_reducedBoundary; |
135 | 0 | computeReducedPred( reducedPred, reducedBoundary, matrix, transpose, bitDepth ); |
136 | 0 | if( needUpsampling ) |
137 | 0 | { |
138 | 0 | predictionUpsampling( result, reducedPred ); |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | | |
143 | | void PredictorMIP::initPredBlockParams(const Size& block) |
144 | 0 | { |
145 | 0 | m_blockSize = block; |
146 | | // init size index |
147 | 0 | m_sizeId = getMipSizeId( m_blockSize ); |
148 | | |
149 | | // init reduced boundary size |
150 | 0 | m_reducedBdrySize = (m_sizeId == 0) ? 2 : 4; |
151 | | |
152 | | // init reduced prediction size |
153 | 0 | m_reducedPredSize = ( m_sizeId < 2 ) ? 4 : 8; |
154 | | |
155 | | |
156 | | // init upsampling factors |
157 | 0 | m_upsmpFactorHor = m_blockSize.width / m_reducedPredSize; |
158 | 0 | m_upsmpFactorVer = m_blockSize.height / m_reducedPredSize; |
159 | |
|
160 | 0 | CHECKD( (m_upsmpFactorHor < 1) || ((m_upsmpFactorHor & (m_upsmpFactorHor - 1)) != 0), "Need power of two horizontal upsampling factor." ); |
161 | 0 | CHECKD( (m_upsmpFactorVer < 1) || ((m_upsmpFactorVer & (m_upsmpFactorVer - 1)) != 0), "Need power of two vertical upsampling factor." ); |
162 | 0 | } |
163 | | |
164 | | |
165 | | |
166 | | void PredictorMIP::boundaryDownsampling1D(Pel* reducedDst, const Pel* const fullSrc, const SizeType srcLen, const SizeType dstLen) |
167 | 0 | { |
168 | 0 | if (dstLen < srcLen) |
169 | 0 | { |
170 | | // Create reduced boundary by downsampling |
171 | 0 | const SizeType downsmpFactor = srcLen / dstLen; |
172 | 0 | const int log2DownsmpFactor = getLog2(downsmpFactor); |
173 | 0 | const int roundingOffset = (1 << (log2DownsmpFactor - 1)); |
174 | |
|
175 | 0 | SizeType srcIdx = 0; |
176 | 0 | for( SizeType dstIdx = 0; dstIdx < dstLen; dstIdx++ ) |
177 | 0 | { |
178 | 0 | int sum = 0; |
179 | 0 | for( int k = 0; k < downsmpFactor; k++ ) |
180 | 0 | { |
181 | 0 | sum += fullSrc[srcIdx++]; |
182 | 0 | } |
183 | 0 | reducedDst[dstIdx] = (sum + roundingOffset) >> log2DownsmpFactor; |
184 | 0 | } |
185 | 0 | } |
186 | 0 | else |
187 | 0 | { |
188 | 0 | memcpy( reducedDst, fullSrc, dstLen * sizeof( Pel ) ); |
189 | 0 | } |
190 | 0 | } |
191 | | |
192 | | |
193 | | void PredictorMIP::predictionUpsampling1D( Pel* const dst, const Pel* const src, const Pel* const bndry, |
194 | | const SizeType srcSizeUpsmpDim, const SizeType srcSizeOrthDim, |
195 | | const SizeType srcStep, const SizeType srcStride, |
196 | | const SizeType dstStep, const SizeType dstStride, |
197 | | const SizeType bndryStep, |
198 | | const unsigned int upsmpFactor ) |
199 | 0 | { |
200 | 0 | const Pel log2UpsmpFactor = getLog2( upsmpFactor ); |
201 | 0 | CHECKD( upsmpFactor <= 1, "Upsampling factor must be at least 2." ); |
202 | 0 | const int roundingOffset = 1 << ( log2UpsmpFactor - 1 ); |
203 | |
|
204 | 0 | const Pel* srcLine = src; |
205 | 0 | Pel* dstLine = dst; |
206 | 0 | const Pel* bndryLine = bndry + bndryStep - 1; |
207 | |
|
208 | 0 | for( int k = 0; k < srcSizeOrthDim; k++ ) |
209 | 0 | { |
210 | 0 | const Pel* before = bndryLine; |
211 | 0 | const Pel* behind = srcLine; |
212 | 0 | Pel* currDst = dstLine; |
213 | |
|
214 | 0 | for( int j = 0; j < srcSizeUpsmpDim; j++ ) |
215 | 0 | { |
216 | 0 | Pel valBehind = *behind; |
217 | 0 | Pel valBefore = *before; |
218 | 0 | Pel valDiff = valBehind - valBefore; |
219 | 0 | Pel scaledVal = ( valBefore << log2UpsmpFactor ) + roundingOffset; |
220 | |
|
221 | 0 | for( int i = 0; i < upsmpFactor; i++ ) |
222 | 0 | { |
223 | 0 | scaledVal += valDiff; |
224 | 0 | *currDst = scaledVal >> log2UpsmpFactor; |
225 | 0 | currDst += dstStep; |
226 | 0 | } |
227 | |
|
228 | 0 | before = behind; |
229 | 0 | behind += srcStep; |
230 | 0 | } |
231 | |
|
232 | 0 | srcLine += srcStride; |
233 | 0 | dstLine += dstStride; |
234 | 0 | bndryLine += bndryStep; |
235 | 0 | } |
236 | 0 | } |
237 | | |
238 | | |
239 | | void PredictorMIP::predictionUpsampling( Pel* const dst, const Pel* const src ) const |
240 | 0 | { |
241 | 0 | const Pel* verSrc = src; |
242 | 0 | SizeType verSrcStep = m_blockSize.width; |
243 | | |
244 | 0 | if( m_upsmpFactorHor > 1 ) |
245 | 0 | { |
246 | 0 | Pel* const horDst = dst + (m_upsmpFactorVer - 1) * m_blockSize.width; |
247 | 0 | verSrc = horDst; |
248 | 0 | verSrcStep *= m_upsmpFactorVer; |
249 | | |
250 | 0 | predictionUpsampling1D( horDst, src, m_refSamplesLeft, |
251 | 0 | m_reducedPredSize, m_reducedPredSize, |
252 | 0 | 1, m_reducedPredSize, 1, verSrcStep, |
253 | 0 | m_upsmpFactorVer, m_upsmpFactorHor ); |
254 | 0 | } |
255 | | |
256 | 0 | if( m_upsmpFactorVer > 1 ) |
257 | 0 | { |
258 | 0 | predictionUpsampling1D( dst, verSrc, m_refSamplesTop, |
259 | 0 | m_reducedPredSize, m_blockSize.width, |
260 | 0 | verSrcStep, 1, m_blockSize.width, 1, |
261 | 0 | 1, m_upsmpFactorVer ); |
262 | 0 | } |
263 | 0 | } |
264 | | |
265 | | const uint8_t* PredictorMIP::getMatrixData(const int modeIdx) const |
266 | 0 | { |
267 | 0 | switch( m_sizeId ) |
268 | 0 | { |
269 | 0 | case 0: return &mipMatrix4x4[modeIdx][0][0]; |
270 | | |
271 | 0 | case 1: return &mipMatrix8x8[modeIdx][0][0]; |
272 | | |
273 | 0 | case 2: return &mipMatrix16x16[modeIdx][0][0]; |
274 | | |
275 | 0 | default: THROW_FATAL( "Invalid mipSizeId" ); |
276 | 0 | } |
277 | 0 | } |
278 | | |
279 | | void PredictorMIP::computeReducedPred( Pel* const result, const Pel* const input, const uint8_t* matrix, const bool transpose, const int bitDepth ) |
280 | 0 | { |
281 | 0 | const int inputSize = 2 * m_reducedBdrySize; |
282 | | |
283 | | // use local buffer for transposed result |
284 | 0 | Pel resBufTransposed[MIP_MAX_REDUCED_OUTPUT_SAMPLES]; |
285 | 0 | Pel* const resPtr = ( transpose ) ? resBufTransposed : result; |
286 | |
|
287 | 0 | int sum = 0; |
288 | 0 | for( int i = 0; i < inputSize; i++ ) |
289 | 0 | { |
290 | 0 | sum += input[i]; |
291 | 0 | } |
292 | 0 | const int offset = ( 1 << ( MIP_SHIFT_MATRIX - 1 ) ) - MIP_OFFSET_MATRIX * sum; |
293 | 0 | CHECK( inputSize != 4 * ( inputSize >> 2 ), "Error, input size not divisible by four" ); |
294 | |
|
295 | 0 | const uint8_t* weight = matrix; |
296 | 0 | const int inputOffset = transpose ? m_inputOffsetTransp : m_inputOffset; |
297 | |
|
298 | 0 | const bool redSize = ( m_sizeId == 2 ); |
299 | 0 | int posRes = 0; |
300 | 0 | for( int y = 0; y < m_reducedPredSize; y++ ) |
301 | 0 | { |
302 | 0 | for( int x = 0; x < m_reducedPredSize; x++ ) |
303 | 0 | { |
304 | 0 | int tmp0 = redSize ? 0 : ( input[0] * weight[0] ); |
305 | 0 | int tmp1 = input[1] * weight[1 - redSize]; |
306 | 0 | int tmp2 = input[2] * weight[2 - redSize]; |
307 | 0 | int tmp3 = input[3] * weight[3 - redSize]; |
308 | 0 | for( int i = 4; i < inputSize; i += 4 ) |
309 | 0 | { |
310 | 0 | tmp0 += input[i ] * weight[i - redSize]; |
311 | 0 | tmp1 += input[i + 1] * weight[i + 1 - redSize]; |
312 | 0 | tmp2 += input[i + 2] * weight[i + 2 - redSize]; |
313 | 0 | tmp3 += input[i + 3] * weight[i + 3 - redSize]; |
314 | 0 | } |
315 | 0 | resPtr[posRes++] = ClipBD<int>( ( ( tmp0 + tmp1 + tmp2 + tmp3 + offset ) >> MIP_SHIFT_MATRIX ) + inputOffset, bitDepth ); |
316 | |
|
317 | 0 | weight += inputSize - redSize; |
318 | 0 | } |
319 | 0 | } |
320 | |
|
321 | 0 | if( transpose ) |
322 | 0 | { |
323 | 0 | for( int y = 0; y < m_reducedPredSize; y++ ) |
324 | 0 | { |
325 | 0 | for( int x = 0; x < m_reducedPredSize; x++ ) |
326 | 0 | { |
327 | 0 | result[y * m_reducedPredSize + x] = resPtr[x * m_reducedPredSize + y]; |
328 | 0 | } |
329 | 0 | } |
330 | 0 | } |
331 | 0 | } |
332 | | } // namespace Mip |
333 | | |
334 | | MatrixIntraPrediction::MatrixIntraPrediction() |
335 | 0 | { |
336 | 0 | } |
337 | | |
338 | | void MatrixIntraPrediction::prepareInputForPred(const CPelBuf &src, const Area& puArea, const int bitDepth, const ComponentID compId) |
339 | 0 | { |
340 | 0 | m_component = compId; |
341 | |
|
342 | 0 | m_predictorMip.deriveBoundaryData(src, puArea, bitDepth); |
343 | 0 | } |
344 | | |
345 | | void MatrixIntraPrediction::predBlock( const Size &puSize, const int intraMode, PelBuf& dst, const bool transpose, const int bitDepth, const ComponentID compId, Pel* const resultMip ) |
346 | 0 | { |
347 | 0 | CHECK( m_component != compId, "Boundary has not been prepared for this component." ); |
348 | |
|
349 | 0 | m_predictorMip.getPrediction( resultMip, intraMode, transpose, bitDepth ); |
350 | |
|
351 | 0 | for( int y = 0; y < puSize.height; y++ ) |
352 | 0 | { |
353 | 0 | Pel* const resultLine = &resultMip[y * puSize.width]; |
354 | 0 | Pel* dstLine = dst.bufAt( 0, y ); |
355 | |
|
356 | 0 | for( int x = 0; x < puSize.width; x += 4 ) |
357 | 0 | { |
358 | 0 | dstLine[x + 0] = Pel( resultLine[x + 0] ); |
359 | 0 | dstLine[x + 1] = Pel( resultLine[x + 1] ); |
360 | 0 | dstLine[x + 2] = Pel( resultLine[x + 2] ); |
361 | 0 | dstLine[x + 3] = Pel( resultLine[x + 3] ); |
362 | 0 | } |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | | } |