Allow the standard deviation for GetSampleValuesForFrame to be 0

Setting the standard deviation to 0 is valid and should be interpreted
as directly using the sample value at the coordinates without weighting.
This is made explicit in the documentation for the Corruption Detection
extension:
http://www.webrtc.org/experiments/rtp-hdrext/corruption-detection

Also, change stddev to std_dev in halton_frame_sampler files.

Bug: webrtc:358039777
Change-Id: Id5aa4110194f7f2b2fe9914c94304c90afd64198
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/363300
Auto-Submit: Fanny Linderborg <linderborg@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43070}
This commit is contained in:
Fanny Linderborg 2024-09-23 13:11:19 +02:00 committed by WebRTC LUCI CQ
parent 6f90609fca
commit 5a294aeea3
3 changed files with 79 additions and 41 deletions

View File

@ -101,21 +101,21 @@ double GetFilteredElement(int width,
const uint8_t* data,
int row,
int column,
double stddev) {
double std_dev) {
RTC_CHECK_GE(row, 0);
RTC_CHECK_LT(row, height);
RTC_CHECK_GE(column, 0);
RTC_CHECK_LT(column, width);
RTC_CHECK_GE(stride, width);
RTC_CHECK_GE(stddev, 0.0);
RTC_CHECK_GE(std_dev, 0.0);
if (stddev == 0.0) {
if (std_dev == 0.0) {
return data[row * stride + column];
}
const double kCutoff = 0.2;
const int kMaxDistance =
std::ceil(sqrt(-2.0 * std::log(kCutoff) * std::pow(stddev, 2.0))) - 1;
std::ceil(sqrt(-2.0 * std::log(kCutoff) * std::pow(std_dev, 2.0))) - 1;
RTC_CHECK_GE(kMaxDistance, 0);
if (kMaxDistance == 0) {
return data[row * stride + column];
@ -129,7 +129,7 @@ double GetFilteredElement(int width,
c < std::min(column + kMaxDistance + 1, width); ++c) {
double weight =
std::exp(-1.0 * (std::pow(row - r, 2) + std::pow(column - c, 2)) /
(2.0 * std::pow(stddev, 2)));
(2.0 * std::pow(std_dev, 2)));
element_sum += data[r * stride + c] * weight;
total_weight += weight;
}
@ -142,7 +142,7 @@ std::vector<FilteredSample> GetSampleValuesForFrame(
std::vector<HaltonFrameSampler::Coordinates> sample_coordinates,
int scaled_width,
int scaled_height,
double stddev_gaussian_blur) {
double std_dev_gaussian_blur) {
// Validate input.
if (i420_frame_buffer == nullptr) {
RTC_LOG(LS_WARNING) << "The framebuffer must not be nullptr";
@ -167,10 +167,10 @@ std::vector<FilteredSample> GetSampleValuesForFrame(
<< scaled_width << ", height=" << scaled_height << ".\n";
return {};
}
if (stddev_gaussian_blur <= 0.0) {
RTC_LOG(LS_WARNING) << "The standard deviation for the Gaussian blur must "
"be larger than 0: "
<< stddev_gaussian_blur << ".\n";
if (std_dev_gaussian_blur < 0.0) {
RTC_LOG(LS_WARNING)
<< "The standard deviation for the Gaussian blur must not be negative: "
<< std_dev_gaussian_blur << ".\n";
return {};
}
@ -211,7 +211,7 @@ std::vector<FilteredSample> GetSampleValuesForFrame(
value_for_coordinate = GetFilteredElement(
scaled_i420_buffer->width(), scaled_i420_buffer->height(),
scaled_i420_buffer->StrideY(), scaled_i420_buffer->DataY(), row,
column, stddev_gaussian_blur);
column, std_dev_gaussian_blur);
filtered_samples.push_back(
{.value = value_for_coordinate, .plane = ImagePlane::kLuma});
} else if (row < scaled_i420_buffer->ChromaHeight()) {
@ -220,7 +220,7 @@ std::vector<FilteredSample> GetSampleValuesForFrame(
value_for_coordinate = GetFilteredElement(
scaled_i420_buffer->ChromaWidth(), scaled_i420_buffer->ChromaHeight(),
scaled_i420_buffer->StrideU(), scaled_i420_buffer->DataU(), row,
column, stddev_gaussian_blur);
column, std_dev_gaussian_blur);
filtered_samples.push_back(
{.value = value_for_coordinate, .plane = ImagePlane::kChroma});
} else {
@ -230,7 +230,7 @@ std::vector<FilteredSample> GetSampleValuesForFrame(
value_for_coordinate = GetFilteredElement(
scaled_i420_buffer->ChromaWidth(), scaled_i420_buffer->ChromaHeight(),
scaled_i420_buffer->StrideV(), scaled_i420_buffer->DataV(), row,
column, stddev_gaussian_blur);
column, std_dev_gaussian_blur);
filtered_samples.push_back(
{.value = value_for_coordinate, .plane = ImagePlane::kChroma});
}

View File

@ -64,14 +64,14 @@ class HaltonFrameSampler {
// 1. Scale the frame buffer to the resolution given by `scaled_width` and
// `scaled_height`.
// 2. Scale the `sample_coordinates` to the frame's resolution.
// 3. Apply the Gaussian filtering given by `stddev_gaussian_blur`.
// 3. Apply the Gaussian filtering given by `std_dev_gaussian_blur`.
// 4. Fetch the values at the scaled coordinates in the filtered frame.
std::vector<FilteredSample> GetSampleValuesForFrame(
scoped_refptr<I420BufferInterface> i420_frame_buffer,
std::vector<HaltonFrameSampler::Coordinates> sample_coordinates,
int scaled_width,
int scaled_height,
double stddev_gaussian_blur);
double std_dev_gaussian_blur);
double GetFilteredElement(int width,
int height,
@ -79,7 +79,7 @@ double GetFilteredElement(int width,
const uint8_t* data,
int row,
int column,
double stddev);
double std_dev);
} // namespace webrtc

View File

@ -35,7 +35,7 @@ using Coordinates = HaltonFrameSampler::Coordinates;
// Defaults for sampling tests.
const int kDefaultScaledWidth = 4;
const int kDefaultScaledHeight = 4;
const double kDefaultStddevGaussianBlur = 0.02;
const double kDefaultStdDevGaussianBlur = 0.02;
#if GTEST_HAS_DEATH_TEST
// Defaults for blurring tests.
@ -46,7 +46,7 @@ const uint8_t kDefaultData[kDefaultWidth * kDefaultHeight] = {
20, 196, 250, 115, 139, 39, 99, 197, 21, 166, 254, 28, 227, 54, 64, 46};
const int kDefaultRow = 3;
const int kDefaultColumn = 2;
const double kDefaultStddev = 1.12;
const double kDefaultStdDev = 1.12;
#endif // GTEST_HAS_DEATH_TEST
scoped_refptr<I420Buffer> MakeDefaultI420FrameBuffer() {
@ -81,10 +81,10 @@ TEST(GaussianFilteringTest, ShouldReturnFilteredValueWhenInputIsValid) {
21, 166, 254, 28, 227, 54, 64, 46};
const int kRow = 3;
const int kColumn = 2;
const double kStddev = 1.12;
const double kStdDev = 1.12;
EXPECT_THAT(GetFilteredElement(kWidth, kHeight, kStride, kData, kRow, kColumn,
kStddev),
kStdDev),
DoubleEq(103.9558797428683));
}
@ -97,10 +97,10 @@ TEST(GaussianFilteringTest,
21, 166, 254, 28, 227, 54, 64, 46};
const int kRow = 3;
const int kColumn = 2;
const double kStddev = 0.0;
const double kStdDev = 0.0;
EXPECT_THAT(GetFilteredElement(kWidth, kHeight, kStride, kData, kRow, kColumn,
kStddev),
kStdDev),
DoubleEq(64.0));
}
@ -108,37 +108,37 @@ TEST(GaussianFilteringTest,
TEST(GaussianFilteringTest, ShouldCrashWhenRowIsNegative) {
EXPECT_DEATH(
GetFilteredElement(kDefaultWidth, kDefaultHeight, kDefaultStride,
kDefaultData, -1, kDefaultColumn, kDefaultStddev),
kDefaultData, -1, kDefaultColumn, kDefaultStdDev),
_);
}
TEST(GaussianFilteringTest, ShouldCrashWhenRowIsOutOfRange) {
EXPECT_DEATH(
GetFilteredElement(kDefaultWidth, 4, kDefaultStride, kDefaultData, 4,
kDefaultColumn, kDefaultStddev),
kDefaultColumn, kDefaultStdDev),
_);
}
TEST(GaussianFilteringTest, ShouldCrashWhenColumnIsNegative) {
EXPECT_DEATH(
GetFilteredElement(kDefaultWidth, kDefaultHeight, kDefaultStride,
kDefaultData, kDefaultRow, -1, kDefaultStddev),
kDefaultData, kDefaultRow, -1, kDefaultStdDev),
_);
}
TEST(GaussianFilteringTest, ShouldCrashWhenColumnIsOutOfRange) {
EXPECT_DEATH(GetFilteredElement(4, kDefaultHeight, kDefaultStride,
kDefaultData, kDefaultRow, 4, kDefaultStddev),
kDefaultData, kDefaultRow, 4, kDefaultStdDev),
_);
}
TEST(GaussianFilteringTest, ShouldCrashWhenStrideIsSmallerThanWidth) {
EXPECT_DEATH(GetFilteredElement(4, kDefaultHeight, 3, kDefaultData,
kDefaultRow, kDefaultColumn, kDefaultStddev),
kDefaultRow, kDefaultColumn, kDefaultStdDev),
_);
}
TEST(GaussianFilteringTest, ShouldCrashWhenStddevIsNegative) {
TEST(GaussianFilteringTest, ShouldCrashWhenStdDevIsNegative) {
EXPECT_DEATH(
GetFilteredElement(kDefaultWidth, kDefaultHeight, kDefaultStride,
kDefaultData, kDefaultRow, kDefaultColumn, -1.0),
@ -166,7 +166,7 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(GetSampleValuesForFrame(nullptr, kDefaultSampleCoordinates,
kDefaultScaledWidth, kDefaultScaledHeight,
kDefaultStddevGaussianBlur),
kDefaultStdDevGaussianBlur),
IsEmpty());
}
@ -177,7 +177,7 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(
GetSampleValuesForFrame(kDefaultI420Buffer, {}, kDefaultScaledWidth,
kDefaultScaledHeight, kDefaultStddevGaussianBlur),
kDefaultScaledHeight, kDefaultStdDevGaussianBlur),
IsEmpty());
}
@ -193,7 +193,7 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(GetSampleValuesForFrame(kDefaultI420Buffer, kSampleCoordinates,
kDefaultScaledWidth, kDefaultScaledHeight,
kDefaultStddevGaussianBlur),
kDefaultStdDevGaussianBlur),
IsEmpty());
}
@ -206,7 +206,7 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(
GetSampleValuesForFrame(kDefaultI420Buffer, kDefaultSampleCoordinates, 0,
kDefaultScaledHeight, kDefaultStddevGaussianBlur),
kDefaultScaledHeight, kDefaultStdDevGaussianBlur),
IsEmpty());
}
@ -219,12 +219,12 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(GetSampleValuesForFrame(
kDefaultI420Buffer, kDefaultSampleCoordinates,
kDefaultScaledWidth, 0, kDefaultStddevGaussianBlur),
kDefaultScaledWidth, 0, kDefaultStdDevGaussianBlur),
IsEmpty());
}
TEST(HaltonFrameSamplerGaussianFilteringTest,
ShouldReturnEmptyListGivenInvalidInputStddevZero) {
ShouldReturnEmptyListGivenInvalidInputStdDevNegative) {
const scoped_refptr<I420Buffer> kDefaultI420Buffer =
MakeDefaultI420FrameBuffer();
const std::vector<Coordinates> kDefaultSampleCoordinates =
@ -232,10 +232,48 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
EXPECT_THAT(
GetSampleValuesForFrame(kDefaultI420Buffer, kDefaultSampleCoordinates,
kDefaultScaledWidth, kDefaultScaledHeight, 0.0),
kDefaultScaledWidth, kDefaultScaledHeight, -1.0),
IsEmpty());
}
TEST(HaltonFrameSamplerGaussianFilteringTest,
ShouldReturnGivenValueWhenStdDevZero) {
// 4x4 i420 frame data.
const int kLumaWidth = 4;
const int kLumaHeight = 4;
const int kChromaWidth = 2;
const uint8_t kYContent[16] = {20, 196, 250, 115, 139, 39, 99, 197,
21, 166, 254, 28, 227, 54, 64, 46};
const uint8_t kUContent[4] = {156, 203, 36, 128};
const uint8_t kVContent[4] = {112, 2, 0, 24};
const scoped_refptr<I420Buffer> kI420Buffer =
I420Buffer::Copy(kLumaWidth, kLumaHeight, kYContent, kLumaWidth,
kUContent, kChromaWidth, kVContent, kChromaWidth);
// Coordinates in all planes.
const std::vector<Coordinates> kSampleCoordinates = {
{.row = 0.2, .column = 0.7},
{.row = 0.5, .column = 0.9},
{.row = 0.3, .column = 0.7},
{.row = 0.8, .column = 0.4}};
// No scaling.
const int kScaledWidth = kLumaWidth;
const int kScaledHeight = kLumaHeight;
EXPECT_THAT(
GetSampleValuesForFrame(kI420Buffer, kSampleCoordinates, kScaledWidth,
kScaledHeight, 0.0),
ElementsAre(AllOf(Field(&FilteredSample::value, DoubleEq(156.0)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),
AllOf(Field(&FilteredSample::value, DoubleEq(2.0)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),
AllOf(Field(&FilteredSample::value, DoubleEq(36.0)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),
AllOf(Field(&FilteredSample::value, DoubleEq(64.0)),
Field(&FilteredSample::plane, ImagePlane::kLuma))));
}
TEST(HaltonFrameSamplerGaussianFilteringTest,
ShouldReturnGivenValueWhenNoScalingOrFilteringIsDefined) {
// 4x4 i420 frame data.
@ -262,11 +300,11 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
const int kScaledHeight = kLumaHeight;
// No filtering.
const double kStddevGaussianBlur = 0.02;
const double kStdDevGaussianBlur = 0.02;
EXPECT_THAT(
GetSampleValuesForFrame(kI420Buffer, kSampleCoordinates, kScaledWidth,
kScaledHeight, kStddevGaussianBlur),
kScaledHeight, kStdDevGaussianBlur),
ElementsAre(AllOf(Field(&FilteredSample::value, DoubleEq(156.0)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),
AllOf(Field(&FilteredSample::value, DoubleEq(2.0)),
@ -303,11 +341,11 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
const int kScaledHeight = 10;
// No filtering.
const double kStddevGaussianBlur = 0.02;
const double kStdDevGaussianBlur = 0.02;
EXPECT_THAT(
GetSampleValuesForFrame(kI420Buffer, kSampleCoordinates, kScaledWidth,
kScaledHeight, kStddevGaussianBlur),
kScaledHeight, kStdDevGaussianBlur),
ElementsAre(AllOf(Field(&FilteredSample::value, DoubleEq(96.0)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),
AllOf(Field(&FilteredSample::value, DoubleEq(30.0)),
@ -344,11 +382,11 @@ TEST(HaltonFrameSamplerGaussianFilteringTest,
const int kScaledHeight = kLumaHeight;
// With filtering (kernel size 2x2).
const double kStddevGaussianBlur = 1.12;
const double kStdDevGaussianBlur = 1.12;
EXPECT_THAT(
GetSampleValuesForFrame(kI420Buffer, kSampleCoordinates, kScaledWidth,
kScaledHeight, kStddevGaussianBlur),
kScaledHeight, kStdDevGaussianBlur),
ElementsAre(
AllOf(Field(&FilteredSample::value, DoubleEq(133.93909141543787)),
Field(&FilteredSample::plane, ImagePlane::kChroma)),