blob: 395b00c69014633bc476d7145c4a6bb22beca813 [file] [log] [blame]
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "denoising.h"
#include "trace.h"
#include <cstring>
namespace webrtc {
enum { kSubsamplingTime = 0 }; // Down-sampling in time (unit: number of frames)
enum { kSubsamplingWidth = 3 }; // Sub-sampling in width (unit: power of 2)
enum { kSubsamplingHeight = 2 }; // Sub-sampling in height (unit: power of 2)
enum { kDenoiseFiltParam = 179 }; // (Q8) De-noising filter parameter
enum { kDenoiseFiltParamRec = 77 }; // (Q8) 1 - filter parameter
enum { kDenoiseThreshold = 19200 }; // (Q8) De-noising threshold level
VPMDenoising::VPMDenoising() :
_id(0),
_moment1(NULL),
_moment2(NULL)
{
Reset();
}
VPMDenoising::~VPMDenoising()
{
if (_moment1)
{
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
}
WebRtc_Word32
VPMDenoising::ChangeUniqueId(const WebRtc_Word32 id)
{
_id = id;
return VPM_OK;
}
void
VPMDenoising::Reset()
{
_frameSize = 0;
_denoiseFrameCnt = 0;
if (_moment1)
{
delete [] _moment1;
_moment1 = NULL;
}
if (_moment2)
{
delete [] _moment2;
_moment2 = NULL;
}
}
WebRtc_Word32
VPMDenoising::ProcessFrame(WebRtc_UWord8* frame,
const WebRtc_UWord32 width,
const WebRtc_UWord32 height)
{
WebRtc_Word32 thevar;
WebRtc_UWord32 k;
WebRtc_UWord32 jsub, ksub;
WebRtc_Word32 diff0;
WebRtc_UWord32 tmpMoment1;
WebRtc_UWord32 tmpMoment2;
WebRtc_UWord32 tmp;
WebRtc_Word32 numPixelsChanged = 0;
if (frame == NULL)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Null frame pointer");
return VPM_GENERAL_ERROR;
}
if (width == 0 || height == 0)
{
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideoPreocessing, _id, "Invalid frame size");
return VPM_GENERAL_ERROR;
}
/* Size of luminance component */
const WebRtc_UWord32 ysize = height * width;
/* Initialization */
if (ysize != _frameSize)
{
delete [] _moment1;
_moment1 = NULL;
delete [] _moment2;
_moment2 = NULL;
}
_frameSize = ysize;
if (!_moment1)
{
_moment1 = new WebRtc_UWord32[ysize];
memset(_moment1, 0, sizeof(WebRtc_UWord32)*ysize);
}
if (!_moment2)
{
_moment2 = new WebRtc_UWord32[ysize];
memset(_moment2, 0, sizeof(WebRtc_UWord32)*ysize);
}
/* Apply de-noising on each pixel, but update variance sub-sampled */
for (WebRtc_UWord32 i = 0; i < height; i++)
{ // Collect over height
k = i * width;
ksub = ((i >> kSubsamplingHeight) << kSubsamplingHeight) * width;
for (WebRtc_UWord32 j = 0; j < width; j++)
{ // Collect over width
jsub = ((j >> kSubsamplingWidth) << kSubsamplingWidth);
/* Update mean value for every pixel and every frame */
tmpMoment1 = _moment1[k + j];
tmpMoment1 *= kDenoiseFiltParam; // Q16
tmpMoment1 += ((kDenoiseFiltParamRec * ((WebRtc_UWord32)frame[k + j])) << 8);
tmpMoment1 >>= 8; // Q8
_moment1[k + j] = tmpMoment1;
tmpMoment2 = _moment2[ksub + jsub];
if ((ksub == k) && (jsub == j) && (_denoiseFrameCnt == 0))
{
tmp = ((WebRtc_UWord32)frame[k + j] * (WebRtc_UWord32)frame[k + j]);
tmpMoment2 *= kDenoiseFiltParam; // Q16
tmpMoment2 += ((kDenoiseFiltParamRec * tmp)<<8);
tmpMoment2 >>= 8; // Q8
}
_moment2[k + j] = tmpMoment2;
/* Current event = deviation from mean value */
diff0 = ((WebRtc_Word32)frame[k + j] << 8) - _moment1[k + j];
/* Recent events = variance (variations over time) */
thevar = _moment2[k + j];
thevar -= ((_moment1[k + j] * _moment1[k + j]) >> 8);
/***************************************************************************
* De-noising criteria, i.e., when should we replace a pixel by its mean
*
* 1) recent events are minor
* 2) current events are minor
***************************************************************************/
if ((thevar < kDenoiseThreshold)
&& ((diff0 * diff0 >> 8) < kDenoiseThreshold))
{ // Replace with mean
frame[k + j] = (WebRtc_UWord8)(_moment1[k + j] >> 8);
numPixelsChanged++;
}
}
}
/* Update frame counter */
_denoiseFrameCnt++;
if (_denoiseFrameCnt > kSubsamplingTime)
{
_denoiseFrameCnt = 0;
}
return numPixelsChanged;
}
} //namespace