diff --git a/test/add_noise_test.cc b/test/add_noise_test.cc new file mode 100644 index 000000000..96e3afbee --- /dev/null +++ b/test/add_noise_test.cc @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2016 The WebM 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 +#include "test/clear_system_state.h" +#include "test/register_state_check.h" +#include "third_party/googletest/src/include/gtest/gtest.h" +#include "./vpx_dsp_rtcd.h" +#include "vpx/vpx_integer.h" +#include "vpx_mem/vpx_mem.h" + +namespace { + +// TODO(jimbankoski): make width and height integers not unsigned. +typedef void (*AddNoiseFunc)(unsigned char *start, char *noise, + char blackclamp[16], char whiteclamp[16], + char bothclamp[16], unsigned int width, + unsigned int height, int pitch); + +class AddNoiseTest + : public ::testing::TestWithParam { + public: + virtual void TearDown() { + libvpx_test::ClearSystemState(); + } + virtual ~AddNoiseTest() {} +}; + +double stddev6(char a, char b, char c, char d, char e, char f) { + const double n = (a + b + c + d + e + f) / 6.0; + const double v = ((a - n) * (a - n) + (b - n) * (b - n) + (c - n) * (c - n) + + (d - n) * (d - n) + (e - n) * (e - n) + (f - n) * (f - n)) / + 6.0; + return sqrt(v); +} + +// TODO(jimbankoski): The following 2 functions are duplicated in each codec. +// For now the vp9 one has been copied into the test as is. We should normalize +// these in vpx_dsp and not have 3 copies of these unless there is different +// noise we add for each codec. + +double gaussian(double sigma, double mu, double x) { + return 1 / (sigma * sqrt(2.0 * 3.14159265)) * + (exp(-(x - mu) * (x - mu) / (2 * sigma * sigma))); +} + +int setup_noise(int size_noise, char *noise) { + char char_dist[300]; + const int ai = 4; + const int qi = 24; + const double sigma = ai + .5 + .6 * (63 - qi) / 63.0; + + /* set up a lookup table of 256 entries that matches + * a gaussian distribution with sigma determined by q. + */ + int next = 0; + + for (int i = -32; i < 32; i++) { + int a_i = (int) (0.5 + 256 * gaussian(sigma, 0, i)); + + if (a_i) { + for (int j = 0; j < a_i; j++) { + char_dist[next + j] = (char)(i); + } + + next = next + a_i; + } + } + + for (; next < 256; next++) + char_dist[next] = 0; + + for (int i = 0; i < size_noise; i++) { + noise[i] = char_dist[rand() & 0xff]; // NOLINT + } + + // Returns the most negative value in distribution. + return char_dist[0]; +} + +TEST_P(AddNoiseTest, CheckNoiseAdded) { + DECLARE_ALIGNED(16, char, blackclamp[16]); + DECLARE_ALIGNED(16, char, whiteclamp[16]); + DECLARE_ALIGNED(16, char, bothclamp[16]); + const int width = 64; + const int height = 64; + const int image_size = width * height; + char noise[3072]; + + const int clamp = setup_noise(3072, noise); + for (int i = 0; i < 16; i++) { + blackclamp[i] = -clamp; + whiteclamp[i] = -clamp; + bothclamp[i] = -2 * clamp; + } + + uint8_t *const s = reinterpret_cast(vpx_calloc(image_size, 1)); + memset(s, 99, image_size); + + ASM_REGISTER_STATE_CHECK(GetParam()(s, noise, blackclamp, whiteclamp, + bothclamp, width, height, width)); + + // Check to make sure we don't end up having either the same or no added + // noise either vertically or horizontally. + for (int i = 0; i < image_size - 6 * width - 6; ++i) { + const double hd = stddev6(s[i] - 99, s[i + 1] - 99, s[i + 2] - 99, + s[i + 3] - 99, s[i + 4] - 99, s[i + 5] - 99); + const double vd = stddev6(s[i] - 99, s[i + width] - 99, + s[i + 2 * width] - 99, s[i + 3 * width] - 99, + s[i + 4 * width] - 99, s[i + 5 * width] - 99); + + EXPECT_NE(hd, 0); + EXPECT_NE(vd, 0); + } + + // Initialize pixels in the image to 255 and check for roll over. + memset(s, 255, image_size); + + ASM_REGISTER_STATE_CHECK(GetParam()(s, noise, blackclamp, whiteclamp, + bothclamp, width, height, width)); + + // Check to make sure don't roll over. + for (int i = 0; i < image_size; ++i) { + EXPECT_GT((int)s[i], 10) << "i = " << i; + } + + // Initialize pixels in the image to 0 and check for roll under. + memset(s, 0, image_size); + + ASM_REGISTER_STATE_CHECK(GetParam()(s, noise, blackclamp, whiteclamp, + bothclamp, width, height, width)); + + // Check to make sure don't roll under. + for (int i = 0; i < image_size; ++i) { + EXPECT_LT((int)s[i], 245) << "i = " << i; + } + + vpx_free(s); +} + +// TODO(jimbankoski): Make the c work like assembly so we can enable this. +TEST_P(AddNoiseTest, DISABLED_CheckCvsAssembly) { + DECLARE_ALIGNED(16, char, blackclamp[16]); + DECLARE_ALIGNED(16, char, whiteclamp[16]); + DECLARE_ALIGNED(16, char, bothclamp[16]); + const int width = 64; + const int height = 64; + const int image_size = width * height; + char noise[3072]; + + const int clamp = setup_noise(3072, noise); + for (int i = 0; i < 16; i++) { + blackclamp[i] = -clamp; + whiteclamp[i] = -clamp; + bothclamp[i] = -2 * clamp; + } + + uint8_t *const s = reinterpret_cast(vpx_calloc(image_size, 1)); + uint8_t *const d = reinterpret_cast(vpx_calloc(image_size, 1)); + + memset(s, 99, image_size); + memset(d, 99, image_size); + + ASM_REGISTER_STATE_CHECK(GetParam()(s, noise, blackclamp, whiteclamp, + bothclamp, width, height, width)); + ASM_REGISTER_STATE_CHECK(vpx_plane_add_noise_c(d, noise, blackclamp, + whiteclamp, bothclamp, + width, height, width)); + + for (int i = 0; i < image_size; ++i) { + EXPECT_EQ((int)s[i], (int)d[i]) << "i = " << i; + } + + vpx_free(d); + vpx_free(s); +} + +INSTANTIATE_TEST_CASE_P(C, AddNoiseTest, + ::testing::Values(vpx_plane_add_noise_c)); + +#if HAVE_MMX +INSTANTIATE_TEST_CASE_P(MMX, AddNoiseTest, + ::testing::Values(vpx_plane_add_noise_mmx)); +#endif + +#if HAVE_SSE2 +INSTANTIATE_TEST_CASE_P(SSE2, AddNoiseTest, + ::testing::Values(vpx_plane_add_noise_sse2)); +#endif + +#if HAVE_MSA +INSTANTIATE_TEST_CASE_P(MSA, AddNoiseTest, + ::testing::Values(vpx_plane_add_noise_msa)); +#endif +} // namespace diff --git a/test/test.mk b/test/test.mk index 7c22ca501..390bc2369 100644 --- a/test/test.mk +++ b/test/test.mk @@ -103,6 +103,7 @@ LIBVPX_TEST_SRCS-yes += vp8_boolcoder_test.cc LIBVPX_TEST_SRCS-yes += vp8_fragments_test.cc endif +LIBVPX_TEST_SRCS-$(CONFIG_POSTPROC) += add_noise_test.cc LIBVPX_TEST_SRCS-$(CONFIG_POSTPROC) += pp_filter_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP8_DECODER) += vp8_decrypt_test.cc LIBVPX_TEST_SRCS-$(CONFIG_VP8_ENCODER) += quantize_test.cc diff --git a/vpx_dsp/postproc.c b/vpx_dsp/postproc.c index 1fa0204f4..c337e6e9b 100644 --- a/vpx_dsp/postproc.c +++ b/vpx_dsp/postproc.c @@ -34,8 +34,8 @@ void vpx_plane_add_noise_c(uint8_t *start, char *noise, if (pos[j] < blackclamp[0]) pos[j] = blackclamp[0]; - if (pos[j] > 255 + whiteclamp[0]) - pos[j] = 255 + whiteclamp[0]; + if (pos[j] > 255 - whiteclamp[0]) + pos[j] = 255 - whiteclamp[0]; pos[j] += ref[j]; } diff --git a/vpx_dsp/x86/postproc_mmx.asm b/vpx_dsp/x86/postproc_mmx.asm index 97039750d..8c2623db4 100644 --- a/vpx_dsp/x86/postproc_mmx.asm +++ b/vpx_dsp/x86/postproc_mmx.asm @@ -25,18 +25,20 @@ sym(vpx_plane_add_noise_mmx): push rdi ; end prolog + ; get the clamps in registers + mov rdx, arg(2) ; blackclamp + movq mm3, [rdx] + mov rdx, arg(3) ; whiteclamp + movq mm4, [rdx] + mov rdx, arg(4) ; bothclamp + movq mm5, [rdx] + .addnoise_loop: call sym(LIBVPX_RAND) WRT_PLT mov rcx, arg(1) ;noise and rax, 0xff add rcx, rax - ; we rely on the fact that the clamping vectors are stored contiguously - ; in black/white/both order. Note that we have to reload this here because - ; rdx could be trashed by rand() - mov rdx, arg(2) ; blackclamp - - mov rdi, rcx movsxd rcx, dword arg(5) ;[Width] mov rsi, arg(0) ;Pos @@ -45,9 +47,9 @@ sym(vpx_plane_add_noise_mmx): .addnoise_nextset: movq mm1,[rsi+rax] ; get the source - psubusb mm1, [rdx] ;blackclamp ; clamp both sides so we don't outrange adding noise - paddusb mm1, [rdx+32] ;bothclamp - psubusb mm1, [rdx+16] ;whiteclamp + psubusb mm1, mm3 ; subtract black clamp + paddusb mm1, mm5 ; add both clamp + psubusb mm1, mm4 ; subtract whiteclamp movq mm2,[rdi+rax] ; get the noise for this line paddb mm1,mm2 ; add it in diff --git a/vpx_dsp/x86/postproc_sse2.asm b/vpx_dsp/x86/postproc_sse2.asm index f4bc8932a..ff61b19ba 100644 --- a/vpx_dsp/x86/postproc_sse2.asm +++ b/vpx_dsp/x86/postproc_sse2.asm @@ -27,38 +27,40 @@ sym(vpx_plane_add_noise_sse2): push rdi ; end prolog + ; get the clamps in registers + mov rdx, arg(2) ; blackclamp + movdqu xmm3, [rdx] + mov rdx, arg(3) ; whiteclamp + movdqu xmm4, [rdx] + mov rdx, arg(4) ; bothclamp + movdqu xmm5, [rdx] + .addnoise_loop: call sym(LIBVPX_RAND) WRT_PLT mov rcx, arg(1) ;noise and rax, 0xff add rcx, rax - ; we rely on the fact that the clamping vectors are stored contiguously - ; in black/white/both order. Note that we have to reload this here because - ; rdx could be trashed by rand() - mov rdx, arg(2) ; blackclamp - - - mov rdi, rcx - movsxd rcx, dword arg(5) ;[Width] - mov rsi, arg(0) ;Pos - xor rax,rax + mov rdi, rcx + movsxd rcx, dword arg(5) ;[Width] + mov rsi, arg(0) ;Pos + xor rax,rax .addnoise_nextset: - movdqu xmm1,[rsi+rax] ; get the source + movdqu xmm1,[rsi+rax] ; get the source - psubusb xmm1, [rdx] ;blackclamp ; clamp both sides so we don't outrange adding noise - paddusb xmm1, [rdx+32] ;bothclamp - psubusb xmm1, [rdx+16] ;whiteclamp + psubusb xmm1, xmm3 ; subtract black clamp + paddusb xmm1, xmm5 ; add both clamp + psubusb xmm1, xmm4 ; subtract whiteclamp - movdqu xmm2,[rdi+rax] ; get the noise for this line - paddb xmm1,xmm2 ; add it in - movdqu [rsi+rax],xmm1 ; store the result + movdqu xmm2,[rdi+rax] ; get the noise for this line + paddb xmm1,xmm2 ; add it in + movdqu [rsi+rax],xmm1 ; store the result - add rax,16 ; move to the next line + add rax,16 ; move to the next line - cmp rax, rcx - jl .addnoise_nextset + cmp rax, rcx + jl .addnoise_nextset movsxd rax, dword arg(7) ; Pitch add arg(0), rax ; Start += Pitch @@ -73,7 +75,6 @@ sym(vpx_plane_add_noise_sse2): pop rbp ret - SECTION_RODATA align 16 rd42: