source: SHVCSoftware/trunk/source/Lib/TLibEncoder/WeightPredAnalysis.cpp @ 583

Last change on this file since 583 was 494, checked in by seregin, 11 years ago

reintegrate branch SHM-4.0-dev

  • Property svn:eol-style set to native
File size: 17.0 KB
RevLine 
[313]1/* The copyright in this software is being made available under the BSD
2 * License, included below. This software may be subject to other third party
3 * and contributor rights, including patent rights, and no such rights are
[494]4 * granted under this license.
[313]5 *
6 * Copyright (c) 2010-2013, ITU/ISO/IEC
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions are met:
11 *
12 *  * Redistributions of source code must retain the above copyright notice,
13 *    this list of conditions and the following disclaimer.
14 *  * Redistributions in binary form must reproduce the above copyright notice,
15 *    this list of conditions and the following disclaimer in the documentation
16 *    and/or other materials provided with the distribution.
17 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
18 *    be used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/** \file     WeightPredAnalysis.cpp
35    \brief    weighted prediction encoder class
36*/
37
38#include "../TLibCommon/TypeDef.h"
39#include "../TLibCommon/TComSlice.h"
40#include "../TLibCommon/TComPic.h"
41#include "../TLibCommon/TComPicYuv.h"
42#include "WeightPredAnalysis.h"
43
44#define ABS(a)    ((a) < 0 ? - (a) : (a))
45#define DTHRESH (0.99)
46
47WeightPredAnalysis::WeightPredAnalysis()
48{
49  m_weighted_pred_flag = false;
50  m_weighted_bipred_flag = false;
51  for ( Int iList =0 ; iList<2 ; iList++ )
52  {
[494]53    for ( Int iRefIdx=0 ; iRefIdx<MAX_NUM_REF ; iRefIdx++ )
[313]54    {
55      for ( Int comp=0 ; comp<3 ;comp++ )
56      {
57        wpScalingParam  *pwp   = &(m_wp[iList][iRefIdx][comp]);
58        pwp->bPresentFlag      = false;
59        pwp->uiLog2WeightDenom = 0;
60        pwp->iWeight           = 1;
61        pwp->iOffset           = 0;
62      }
63    }
64  }
65}
66
67/** calculate AC and DC values for current original image
68 * \param TComSlice *slice
69 * \returns Void
70 */
71Bool  WeightPredAnalysis::xCalcACDCParamSlice(TComSlice *slice)
72{
73  //===== calculate AC/DC value =====
74  TComPicYuv*   pPic = slice->getPic()->getPicYuvOrg();
75  Int   iSample  = 0;
[494]76#if O0194_WEIGHTED_PREDICTION_CGS
77  // Define here to assign the parameter of "iSample"
78  wpACDCParam weightACDCParam[3];
79#endif
[313]80
81  // calculate DC/AC value for Y
82  Pel*  pOrg    = pPic->getLumaAddr();
83  Int64  iOrgDCY = xCalcDCValueSlice(slice, pOrg, &iSample);
84  Int64  iOrgNormDCY = ((iOrgDCY+(iSample>>1)) / iSample);
85  pOrg = pPic->getLumaAddr();
86  Int64  iOrgACY  = xCalcACValueSlice(slice, pOrg, iOrgNormDCY);
[494]87#if O0194_WEIGHTED_PREDICTION_CGS
88  weightACDCParam[0].iSamples = iSample;
89#endif
[313]90
91  // calculate DC/AC value for Cb
92  pOrg = pPic->getCbAddr();
93  Int64  iOrgDCCb = xCalcDCValueUVSlice(slice, pOrg, &iSample);
94  Int64  iOrgNormDCCb = ((iOrgDCCb+(iSample>>1)) / (iSample));
95  pOrg = pPic->getCbAddr();
96  Int64  iOrgACCb  = xCalcACValueUVSlice(slice, pOrg, iOrgNormDCCb);
[494]97#if O0194_WEIGHTED_PREDICTION_CGS
98  weightACDCParam[1].iSamples = iSample;
99#endif
[313]100
101  // calculate DC/AC value for Cr
102  pOrg = pPic->getCrAddr();
103  Int64  iOrgDCCr = xCalcDCValueUVSlice(slice, pOrg, &iSample);
104  Int64  iOrgNormDCCr = ((iOrgDCCr+(iSample>>1)) / (iSample));
105  pOrg = pPic->getCrAddr();
106  Int64  iOrgACCr  = xCalcACValueUVSlice(slice, pOrg, iOrgNormDCCr);
[494]107#if O0194_WEIGHTED_PREDICTION_CGS
108  weightACDCParam[2].iSamples = iSample;
109#endif
[313]110
[494]111#if !O0194_WEIGHTED_PREDICTION_CGS
[313]112  wpACDCParam weightACDCParam[3];
[494]113#endif
[313]114  weightACDCParam[0].iAC = iOrgACY;
115  weightACDCParam[0].iDC = iOrgNormDCY;
116  weightACDCParam[1].iAC = iOrgACCb;
117  weightACDCParam[1].iDC = iOrgNormDCCb;
118  weightACDCParam[2].iAC = iOrgACCr;
119  weightACDCParam[2].iDC = iOrgNormDCCr;
120
121  slice->setWpAcDcParam(weightACDCParam);
122  return (true);
123}
124
125/** store weighted_pred_flag and weighted_bipred_idc values
126 * \param weighted_pred_flag
127 * \param weighted_bipred_idc
128 * \returns Void
129 */
130Void  WeightPredAnalysis::xStoreWPparam(Bool weighted_pred_flag, Bool weighted_bipred_flag)
131{
132  m_weighted_pred_flag = weighted_pred_flag;
133  m_weighted_bipred_flag = weighted_bipred_flag;
134}
135
136/** restore weighted_pred_flag and weighted_bipred_idc values
137 * \param TComSlice *slice
138 * \returns Void
139 */
140Void  WeightPredAnalysis::xRestoreWPparam(TComSlice *slice)
141{
142  slice->getPPS()->setUseWP(m_weighted_pred_flag);
143  slice->getPPS()->setWPBiPred(m_weighted_bipred_flag);
144}
145
146/** check weighted pred or non-weighted pred
147 * \param TComSlice *slice
148 * \returns Void
149 */
150Void  WeightPredAnalysis::xCheckWPEnable(TComSlice *slice)
151{
152  Int iPresentCnt = 0;
153  for ( Int iList=0 ; iList<2 ; iList++ )
154  {
[494]155    for ( Int iRefIdx=0 ; iRefIdx<MAX_NUM_REF ; iRefIdx++ )
[313]156    {
[494]157      for ( Int iComp=0 ; iComp<3 ;iComp++ )
[313]158      {
159        wpScalingParam  *pwp = &(m_wp[iList][iRefIdx][iComp]);
160        iPresentCnt += (Int)pwp->bPresentFlag;
161      }
162    }
163  }
164
165  if(iPresentCnt==0)
166  {
167    slice->getPPS()->setUseWP(false);
168    slice->getPPS()->setWPBiPred(false);
169    for ( Int iList=0 ; iList<2 ; iList++ )
170    {
[494]171      for ( Int iRefIdx=0 ; iRefIdx<MAX_NUM_REF ; iRefIdx++ )
[313]172      {
[494]173        for ( Int iComp=0 ; iComp<3 ;iComp++ )
[313]174        {
175          wpScalingParam  *pwp = &(m_wp[iList][iRefIdx][iComp]);
176          pwp->bPresentFlag      = false;
177          pwp->uiLog2WeightDenom = 0;
178          pwp->iWeight           = 1;
179          pwp->iOffset           = 0;
180        }
181      }
182    }
183    slice->setWpScaling( m_wp );
184  }
185}
186
187/** estimate wp tables for explicit wp
188 * \param TComSlice *slice
189 * \returns Bool
190 */
191Bool  WeightPredAnalysis::xEstimateWPParamSlice(TComSlice *slice)
192{
193  Int iDenom  = 6;
194  Bool validRangeFlag = false;
195
196  if(slice->getNumRefIdx(REF_PIC_LIST_0)>3)
197  {
198    iDenom  = 7;
199  }
200
201  do
202  {
203    validRangeFlag = xUpdatingWPParameters(slice, m_wp, iDenom);
204    if (!validRangeFlag)
205    {
206      iDenom--; // decrement to satisfy the range limitation
207    }
208  } while (validRangeFlag == false);
209
210  // selecting whether WP is used, or not
211  xSelectWP(slice, m_wp, iDenom);
[494]212
[313]213  slice->setWpScaling( m_wp );
214
215  return (true);
216}
217
218/** update wp tables for explicit wp w.r.t ramge limitation
219 * \param TComSlice *slice
220 * \returns Bool
221 */
222Bool WeightPredAnalysis::xUpdatingWPParameters(TComSlice *slice, wpScalingParam weightPredTable[2][MAX_NUM_REF][3], Int log2Denom)
223{
224  Int numPredDir = slice->isInterP() ? 1 : 2;
225  for ( Int refList = 0; refList < numPredDir; refList++ )
226  {
227    RefPicList  eRefPicList = ( refList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
228    for ( Int refIdxTemp = 0; refIdxTemp < slice->getNumRefIdx(eRefPicList); refIdxTemp++ )
229    {
230      wpACDCParam *currWeightACDCParam, *refWeightACDCParam;
231      slice->getWpAcDcParam(currWeightACDCParam);
232      slice->getRefPic(eRefPicList, refIdxTemp)->getSlice(0)->getWpAcDcParam(refWeightACDCParam);
[494]233#if O0194_WEIGHTED_PREDICTION_CGS
234      UInt currLayerId = slice->getLayerId();
235      UInt refLayerId  = slice->getRefPic(eRefPicList, refIdxTemp)->getLayerId();
236      Bool validILRPic = slice->getRefPic(eRefPicList, refIdxTemp)->isILR( currLayerId ) && refLayerId == 0;
[313]237
[494]238      if( validILRPic )
239      {
240        refWeightACDCParam = (wpACDCParam *)g_refWeightACDCParam;
241      }
242#endif
243
[313]244      for ( Int comp = 0; comp < 3; comp++ )
245      {
246        Int bitDepth = comp ? g_bitDepthC : g_bitDepthY;
247        Int realLog2Denom = log2Denom + bitDepth-8;
248        Int realOffset = ((Int)1<<(realLog2Denom-1));
249
250        // current frame
251        Int64 currDC = currWeightACDCParam[comp].iDC;
252        Int64 currAC = currWeightACDCParam[comp].iAC;
253        // reference frame
254        Int64 refDC = refWeightACDCParam[comp].iDC;
255        Int64 refAC = refWeightACDCParam[comp].iAC;
[494]256#if O0194_WEIGHTED_PREDICTION_CGS
257        if( validILRPic )
258        {
259          refAC = ( refAC * currWeightACDCParam[comp].iSamples ) /refWeightACDCParam[comp].iSamples;
260#if O0194_JOINT_US_BITSHIFT
261          refAC <<= (g_bitDepthYLayer[currLayerId]-g_bitDepthYLayer[refLayerId]);
262          refDC <<= (g_bitDepthYLayer[currLayerId]-g_bitDepthYLayer[refLayerId]);
263#endif
264        }
265#endif
[313]266
267        // calculating iWeight and iOffset params
268        Double dWeight = (refAC==0) ? (Double)1.0 : Clip3( -16.0, 15.0, ((Double)currAC / (Double)refAC) );
269        Int weight = (Int)( 0.5 + dWeight * (Double)(1<<log2Denom) );
270        Int offset = (Int)( ((currDC<<log2Denom) - ((Int64)weight * refDC) + (Int64)realOffset) >> realLog2Denom );
[494]271#if O0194_WEIGHTED_PREDICTION_CGS
272        if( !validILRPic )
273        {
274          dWeight = 1;
275          offset  = 0;
276        }
277        weight = (Int)( 0.5 + dWeight * (Double)(1<<log2Denom) );
278#endif
[313]279
280        // Chroma offset range limitation
281        if(comp)
282        {
283          Int pred = ( 128 - ( ( 128*weight)>>(log2Denom) ) );
284          Int deltaOffset = Clip3( -512, 511, (offset - pred) );    // signed 10bit
285          offset = Clip3( -128, 127, (deltaOffset + pred) );        // signed 8bit
286        }
287        // Luma offset range limitation
288        else
289        {
290          offset = Clip3( -128, 127, offset);
291        }
292
293        // Weighting factor limitation
294        Int defaultWeight = (1<<log2Denom);
295        Int deltaWeight = (defaultWeight - weight);
296        if(deltaWeight > 127 || deltaWeight < -128)
297          return (false);
[494]298#if O0194_WEIGHTED_PREDICTION_CGS
299        // make sure the reference frames other than ILR are not using weighted prediction
300        else
301        if( !validILRPic )
302        {
303          continue;
304        }
305#endif
[313]306
307        m_wp[refList][refIdxTemp][comp].bPresentFlag = true;
308        m_wp[refList][refIdxTemp][comp].iWeight = (Int)weight;
309        m_wp[refList][refIdxTemp][comp].iOffset = (Int)offset;
310        m_wp[refList][refIdxTemp][comp].uiLog2WeightDenom = (Int)log2Denom;
311      }
312    }
313  }
314  return (true);
315}
316
[494]317/** select whether weighted pred enables or not.
[313]318 * \param TComSlice *slice
319 * \param wpScalingParam
320 * \param iDenom
321 * \returns Bool
322 */
323Bool WeightPredAnalysis::xSelectWP(TComSlice *slice, wpScalingParam weightPredTable[2][MAX_NUM_REF][3], Int iDenom)
324{
325  TComPicYuv*   pPic = slice->getPic()->getPicYuvOrg();
326  Int iWidth  = pPic->getWidth();
327  Int iHeight = pPic->getHeight();
328  Int iDefaultWeight = ((Int)1<<iDenom);
329  Int iNumPredDir = slice->isInterP() ? 1 : 2;
330
331  for ( Int iRefList = 0; iRefList < iNumPredDir; iRefList++ )
332  {
333    Int64 iSADWP = 0, iSADnoWP = 0;
334    RefPicList  eRefPicList = ( iRefList ? REF_PIC_LIST_1 : REF_PIC_LIST_0 );
335    for ( Int iRefIdxTemp = 0; iRefIdxTemp < slice->getNumRefIdx(eRefPicList); iRefIdxTemp++ )
336    {
337      Pel*  pOrg    = pPic->getLumaAddr();
338      Pel*  pRef    = slice->getRefPic(eRefPicList, iRefIdxTemp)->getPicYuvRec()->getLumaAddr();
339      Int   iOrgStride = pPic->getStride();
340      Int   iRefStride = slice->getRefPic(eRefPicList, iRefIdxTemp)->getPicYuvRec()->getStride();
341
342      // calculate SAD costs with/without wp for luma
343      iSADWP   = this->xCalcSADvalueWP(g_bitDepthY, pOrg, pRef, iWidth, iHeight, iOrgStride, iRefStride, iDenom, weightPredTable[iRefList][iRefIdxTemp][0].iWeight, weightPredTable[iRefList][iRefIdxTemp][0].iOffset);
344      iSADnoWP = this->xCalcSADvalueWP(g_bitDepthY, pOrg, pRef, iWidth, iHeight, iOrgStride, iRefStride, iDenom, iDefaultWeight, 0);
345
346      pOrg = pPic->getCbAddr();
347      pRef = slice->getRefPic(eRefPicList, iRefIdxTemp)->getPicYuvRec()->getCbAddr();
348      iOrgStride = pPic->getCStride();
349      iRefStride = slice->getRefPic(eRefPicList, iRefIdxTemp)->getPicYuvRec()->getCStride();
350
351      // calculate SAD costs with/without wp for chroma cb
352      iSADWP   += this->xCalcSADvalueWP(g_bitDepthC, pOrg, pRef, iWidth>>1, iHeight>>1, iOrgStride, iRefStride, iDenom, weightPredTable[iRefList][iRefIdxTemp][1].iWeight, weightPredTable[iRefList][iRefIdxTemp][1].iOffset);
353      iSADnoWP += this->xCalcSADvalueWP(g_bitDepthC, pOrg, pRef, iWidth>>1, iHeight>>1, iOrgStride, iRefStride, iDenom, iDefaultWeight, 0);
354
355      pOrg = pPic->getCrAddr();
356      pRef = slice->getRefPic(eRefPicList, iRefIdxTemp)->getPicYuvRec()->getCrAddr();
357
358      // calculate SAD costs with/without wp for chroma cr
359      iSADWP   += this->xCalcSADvalueWP(g_bitDepthC, pOrg, pRef, iWidth>>1, iHeight>>1, iOrgStride, iRefStride, iDenom, weightPredTable[iRefList][iRefIdxTemp][2].iWeight, weightPredTable[iRefList][iRefIdxTemp][2].iOffset);
360      iSADnoWP += this->xCalcSADvalueWP(g_bitDepthC, pOrg, pRef, iWidth>>1, iHeight>>1, iOrgStride, iRefStride, iDenom, iDefaultWeight, 0);
361
362      Double dRatio = ((Double)iSADWP / (Double)iSADnoWP);
363      if(dRatio >= (Double)DTHRESH)
364      {
365        for ( Int iComp = 0; iComp < 3; iComp++ )
366        {
367          weightPredTable[iRefList][iRefIdxTemp][iComp].bPresentFlag = false;
368          weightPredTable[iRefList][iRefIdxTemp][iComp].iOffset = (Int)0;
369          weightPredTable[iRefList][iRefIdxTemp][iComp].iWeight = (Int)iDefaultWeight;
370          weightPredTable[iRefList][iRefIdxTemp][iComp].uiLog2WeightDenom = (Int)iDenom;
371        }
372      }
373    }
374  }
375  return (true);
376}
377
[494]378/** calculate DC value of original image for luma.
[313]379 * \param TComSlice *slice
380 * \param Pel *pPel
381 * \param Int *iSample
382 * \returns Int64
383 */
384Int64 WeightPredAnalysis::xCalcDCValueSlice(TComSlice *slice, Pel *pPel, Int *iSample)
385{
386  TComPicYuv* pPic = slice->getPic()->getPicYuvOrg();
387  Int iStride = pPic->getStride();
388
389  *iSample = 0;
390  Int iWidth  = pPic->getWidth();
391  Int iHeight = pPic->getHeight();
392  *iSample = iWidth*iHeight;
393  Int64 iDC = xCalcDCValue(pPel, iWidth, iHeight, iStride);
394
395  return (iDC);
396}
397
[494]398/** calculate AC value of original image for luma.
[313]399 * \param TComSlice *slice
400 * \param Pel *pPel
401 * \param Int iDC
402 * \returns Int64
403 */
404Int64 WeightPredAnalysis::xCalcACValueSlice(TComSlice *slice, Pel *pPel, Int64 iDC)
405{
406  TComPicYuv* pPic = slice->getPic()->getPicYuvOrg();
407  Int iStride = pPic->getStride();
408
409  Int iWidth  = pPic->getWidth();
410  Int iHeight = pPic->getHeight();
411  Int64 iAC = xCalcACValue(pPel, iWidth, iHeight, iStride, iDC);
412
413  return (iAC);
414}
415
[494]416/** calculate DC value of original image for chroma.
[313]417 * \param TComSlice *slice
418 * \param Pel *pPel
419 * \param Int *iSample
420 * \returns Int64
421 */
422Int64 WeightPredAnalysis::xCalcDCValueUVSlice(TComSlice *slice, Pel *pPel, Int *iSample)
423{
424  TComPicYuv* pPic = slice->getPic()->getPicYuvOrg();
425  Int iCStride = pPic->getCStride();
426
427  *iSample = 0;
428  Int iWidth  = pPic->getWidth()>>1;
429  Int iHeight = pPic->getHeight()>>1;
430  *iSample = iWidth*iHeight;
431  Int64 iDC = xCalcDCValue(pPel, iWidth, iHeight, iCStride);
432
433  return (iDC);
434}
435
[494]436/** calculate AC value of original image for chroma.
[313]437 * \param TComSlice *slice
438 * \param Pel *pPel
439 * \param Int iDC
440 * \returns Int64
441 */
442Int64 WeightPredAnalysis::xCalcACValueUVSlice(TComSlice *slice, Pel *pPel, Int64 iDC)
443{
444  TComPicYuv* pPic = slice->getPic()->getPicYuvOrg();
445  Int iCStride = pPic->getCStride();
446
447  Int iWidth  = pPic->getWidth()>>1;
448  Int iHeight = pPic->getHeight()>>1;
449  Int64 iAC = xCalcACValue(pPel, iWidth, iHeight, iCStride, iDC);
450
451  return (iAC);
452}
453
[494]454/** calculate DC value.
[313]455 * \param Pel *pPel
456 * \param Int iWidth
457 * \param Int iHeight
458 * \param Int iStride
459 * \returns Int64
460 */
461Int64 WeightPredAnalysis::xCalcDCValue(Pel *pPel, Int iWidth, Int iHeight, Int iStride)
462{
463  Int x, y;
464  Int64 iDC = 0;
465  for( y = 0; y < iHeight; y++ )
466  {
467    for( x = 0; x < iWidth; x++ )
468    {
469      iDC += (Int)( pPel[x] );
470    }
471    pPel += iStride;
472  }
473  return (iDC);
474}
475
[494]476/** calculate AC value.
[313]477 * \param Pel *pPel
478 * \param Int iWidth
479 * \param Int iHeight
480 * \param Int iStride
481 * \param Int iDC
482 * \returns Int64
483 */
484Int64 WeightPredAnalysis::xCalcACValue(Pel *pPel, Int iWidth, Int iHeight, Int iStride, Int64 iDC)
485{
486  Int x, y;
487  Int64 iAC = 0;
488  for( y = 0; y < iHeight; y++ )
489  {
490    for( x = 0; x < iWidth; x++ )
491    {
492      iAC += abs( (Int)pPel[x] - (Int)iDC );
493    }
494    pPel += iStride;
495  }
496  return (iAC);
497}
498
[494]499/** calculate SAD values for both WP version and non-WP version.
[313]500 * \param Pel *pOrgPel
501 * \param Pel *pRefPel
502 * \param Int iWidth
503 * \param Int iHeight
504 * \param Int iOrgStride
505 * \param Int iRefStride
506 * \param Int iDenom
507 * \param Int iWeight
508 * \param Int iOffset
509 * \returns Int64
510 */
511Int64 WeightPredAnalysis::xCalcSADvalueWP(Int bitDepth, Pel *pOrgPel, Pel *pRefPel, Int iWidth, Int iHeight, Int iOrgStride, Int iRefStride, Int iDenom, Int iWeight, Int iOffset)
512{
513  Int x, y;
514  Int64 iSAD = 0;
515  Int64 iSize   = iWidth*iHeight;
516  Int64 iRealDenom = iDenom + bitDepth-8;
517  for( y = 0; y < iHeight; y++ )
518  {
519    for( x = 0; x < iWidth; x++ )
520    {
521      iSAD += ABS(( ((Int64)pOrgPel[x]<<(Int64)iDenom) - ( (Int64)pRefPel[x] * (Int64)iWeight + ((Int64)iOffset<<iRealDenom) ) ) );
522    }
523    pOrgPel += iOrgStride;
524    pRefPel += iRefStride;
525  }
526  return (iSAD/iSize);
527}
528
529
Note: See TracBrowser for help on using the repository browser.