Hello all,
I am relatively new to programming and because the banding produced by
the hqdn3d denoiser is rather annoying for my purposes (Mainly enhancing
material from DSLR cameras while converting to an intermediate codec
with ffmpeg), I have written a simple temporal denoiser that should do
the job.
It takes every pixel, compares it to up to five previous frames and then
outputs the average of all the pixels that are similiar to the input pixel.
The Filter takes three parameters:
Luminance Treshold: How much a previous pixel's Y value may differ from
the current one to be user for averaging.
Chrominance Treshold: Same for U and V channels.
Max Luma: Only Pixels that are darker than the Parameter will be filtered.
The intention of this option is that, because the dominant thermal noise
occurs before the sensor data is converted to gamma corrected space and
so is only visible in darker parts of the image, you may the Parameter
to only filter the parts of the image where noise occurs.
I hope that the filter meets the coding standards of frei0r in some way ;)
Hmm, is it possible to add attachements in the mailing list?
diff --git a/src/Makefile.am b/src/Makefile.am
index b416472..19fae16 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,6 +47,7 @@ plugin_LTLIBRARIES = \
defish0r.la \
delay0r.la \
delaygrab.la \
+ denois0r.la\
difference.la \
distort0r.la \
dither.la \
@@ -192,6 +193,7 @@ baltan_la_SOURCES = filter/baltan/baltan.cpp
bluescreen0r_la_SOURCES = filter/bluescreen0r/bluescreen0r.cpp
brightness_la_SOURCES = filter/brightness/brightness.c
bw0r_la_SOURCES = filter/bw0r/bw0r.c
+denois0r_la_SOURCES = filter/denois0r/denois0r.c
c0rners_la_SOURCES = filter/c0rners/c0rners.c filter/c0rners/interp.h
cartoon_la_SOURCES = filter/cartoon/cartoon.cpp
cluster_la_SOURCES = filter/cluster/cluster.c
diff --git a/src/filter/CMakeLists.txt b/src/filter/CMakeLists.txt
index 31ad4b9..71c8575 100644
--- a/src/filter/CMakeLists.txt
+++ b/src/filter/CMakeLists.txt
@@ -38,6 +38,7 @@ add_subdirectory (defish0r)
add_subdirectory (delay0r)
add_subdirectory (delaygrab)
add_subdirectory (denoise)
+add_subdirectory (denois0r)
add_subdirectory (distort0r)
add_subdirectory (dither)
add_subdirectory (edgeglow)
diff --git a/src/filter/denois0r/CMakeLists.txt b/src/filter/denois0r/CMakeLists.txt
new file mode 100644
index 0000000..cac3281
--- /dev/null
+++ b/src/filter/denois0r/CMakeLists.txt
@@ -0,0 +1,12 @@
+set (SOURCES denois0r.c)
+set (TARGET denois0r)
+
+if (MSVC)
+ set_source_files_properties (denois0r.c PROPERTIES LANGUAGE CXX)
+ set (SOURCES ${SOURCES} ${FREI0R_DEF})
+endif (MSVC)
+
+add_library (${TARGET} MODULE ${SOURCES})
+set_target_properties (${TARGET} PROPERTIES PREFIX "")
+
+install (TARGETS ${TARGET} LIBRARY DESTINATION ${LIBDIR})
diff --git a/src/filter/denois0r/denois0r.c b/src/filter/denois0r/denois0r.c
new file mode 100644
index 0000000..2a3485f
--- /dev/null
+++ b/src/filter/denois0r/denois0r.c
@@ -0,0 +1,234 @@
+/* denois0r.c
+* Copyright (C) 2004 Jean-Sebastien Senecal (js@???)
+* This file is a Frei0r plugin.
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "frei0r.h"
+#include "frei0r_math.h"
+
+typedef struct denois0r_instance
+{
+unsigned int width;
+unsigned int height;
+int tresholdl, tresholdc, maxluma;
+int frame;
+unsigned char lut[256]; /* look-up table */
+float *prev1, *prev2, *prev3, *prev4, *prev5;
+} denois0r_instance_t;
+
+int f0r_init()
+{
+return 1;
+}
+
+void f0r_deinit()
+{ /* no initialization required */ }
+
+void f0r_get_plugin_info(f0r_plugin_info_t* denois0r_info)
+{
+denois0r_info->name = "Denois0r";
+denois0r_info->author = "Björn Sonnenschein";
+denois0r_info->plugin_type = F0R_PLUGIN_TYPE_FILTER;
+denois0r_info->color_model = F0R_COLOR_MODEL_RGBA8888;
+denois0r_info->frei0r_version = FREI0R_MAJOR_VERSION;
+denois0r_info->major_version = 0;
+denois0r_info->minor_version = 2;
+denois0r_info->num_params = 3;
+denois0r_info->explanation = "Temporal Denoiser";
+}
+
+void f0r_get_param_info(f0r_param_info_t* info, int param_index)
+{
+switch(param_index)
+{
+case 0:
+ info->name = "Luminance Treshold";
+ info->type = F0R_PARAM_DOUBLE;
+ info->explanation = "Treshold for luminance noise reduction";
+ break;
+
+case 1:
+ info->name = "Chroma Treshold";
+ info->type = F0R_PARAM_DOUBLE;
+ info->explanation = "Treshold for chrominance noise reduction";
+ break;
+
+case 2:
+ info->name = "Max Luma";
+ info->type = F0R_PARAM_DOUBLE;
+ info->explanation = "Only pixels up to that value will be denoised.";
+ break;
+}
+}
+
+f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
+{
+denois0r_instance_t* inst = (denois0r_instance_t*)calloc(1, sizeof(*inst));
+inst->width = width; inst->height = height;
+inst->tresholdl = 4;
+inst->tresholdc = 4;
+inst->maxluma = 256;
+inst->frame = 0;
+inst->prev1 = (float*)calloc(width*height*4, sizeof(float));
+inst->prev2 = (float*)calloc(width*height*4, sizeof(float));
+inst->prev3 = (float*)calloc(width*height*4, sizeof(float));
+inst->prev4 = (float*)calloc(width*height*4, sizeof(float));
+inst->prev5 = (float*)calloc(width*height*4, sizeof(float));
+return (f0r_instance_t)inst;
+}
+
+void f0r_destruct(f0r_instance_t instance)
+{
+free(instance);
+}
+
+void f0r_set_param_value(f0r_instance_t instance,
+ f0r_param_t param, int param_index)
+{
+assert(instance);
+denois0r_instance_t* inst = (denois0r_instance_t*)instance;
+
+switch(param_index)
+{
+case 0:
+ inst->tresholdl = (int) *((double*)param);
+ break;
+
+case 1:
+ inst->tresholdc = (int) *((double*)param);
+ break;
+
+case 2:
+ inst->maxluma = (int) *((double*)param);
+ break;
+}
+}
+
+void f0r_get_param_value(f0r_instance_t instance,
+ f0r_param_t param, int param_index)
+{
+assert(instance);
+denois0r_instance_t* inst = (denois0r_instance_t*)instance;
+
+switch(param_index)
+{
+case 0:
+ *((double*)param) = (double) (inst->tresholdl );
+ break;
+
+case 1:
+ *((double*)param) = (double) (inst->tresholdc );
+ break;
+
+case 2:
+ *((double*)param) = (double) (inst->maxluma );
+ break;
+}
+}
+
+void f0r_update(f0r_instance_t instance, double time,
+ const uint32_t* inframe, uint32_t* outframe)
+{
+assert(instance);
+denois0r_instance_t* inst = (denois0r_instance_t*)instance;
+unsigned int len = inst->width * inst->height;
+
+unsigned char* dst = (unsigned char*)outframe;
+const unsigned char* src = (unsigned char*)inframe;
+float* prev1 = inst->prev1;
+float* prev2 = inst->prev2;
+float* prev3 = inst->prev3;
+float* prev4 = inst->prev4;
+float* prev5 = inst->prev5;
+int i;
+int tresholdc = inst->tresholdc, tresholdl = inst->tresholdl, treshold[3] = { tresholdl, tresholdc, tresholdc };
+int maxluma = inst->maxluma;
+float yuv[3], yuvout[3];
+int R, G, B;
+
+if (inst->frame <=5)
+ inst->frame++;
+
+while (len--)
+{
+ R = *src++;
+ G = *src++;
+ B = *src++;
+ yuv[0] = 0.299 * R + 0.587 * G + 0.114 * B;
+ yuv[1] = (-0.147) * R - 0.289 * G + 0.436 * B;
+ yuv[2] = 0.615 * R - 0.515 * G - 0.100 * B;
+
+ for (i = 0; i < 3; i++) {
+ if (abs((*prev1 - yuv[i])) < treshold[i] && inst->frame > 1 && yuv[0] <= maxluma) {
+ if (abs((*prev2 - yuv[i])) < treshold[i] && inst->frame > 2) {
+ if (abs((*prev3 - yuv[i])) < treshold[i] && inst->frame > 3) {
+ if (abs((*prev4 - yuv[i])) < treshold[i] && inst->frame > 4) {
+ if (abs((*prev5 - yuv[i])) < treshold[i] && inst->frame > 5)
+ yuvout[i] = (*prev1 + *prev2 + *prev3 + *prev4 + *prev5 + yuv[i])/6 ;
+ else
+ yuvout[i] = (*prev1 + *prev2 + *prev3 + *prev4 + yuv[i])/5 ;
+ }
+ else
+ yuvout[i] = (*prev1 + *prev2 + *prev3 + yuv[i])/4 ;
+ }
+ else
+ yuvout[i] = (*prev1 + *prev2 + yuv[i])/3 ;
+ }
+ else
+ yuvout[i] = (*prev1 + yuv[i]) / 2 ;
+ }
+ else {
+ yuvout[i] = yuv[i];
+ }
+
+ *prev5 = *prev4;
+ *prev4 = *prev3;
+ *prev3 = *prev2;
+ *prev2 = *prev1;
+ *prev1 = yuv[i] ;
+ prev1++; prev2++; prev3++; prev4++; prev5++;
+ }
+
+ R = CLAMP0255 ((yuvout[0] + yuvout[2] * 1.4) );
+ G = CLAMP0255 ((yuvout[0] - yuvout[1] * 0.394 - yuvout[2] * 0.581) );
+ B = CLAMP0255 ((yuvout[0] + yuvout[1] * 2.032) );
+
+ *dst++ = R;
+ *dst++ = G;
+ *dst++ = B;
+
+
+
+/* *dst++ = CLAMP0255((*buffer + *src)/2);
+ *buffer = *src ;
+ buffer++; src++;
+
+ *dst++ = CLAMP0255((*buffer + *src)/2);
+ *buffer = *src ;
+ buffer++; src++;*/
+
+ *dst++ = *src;// copy alpha
+ *prev1 = *src ;
+ prev1++; prev2++; prev3++; prev4++; prev5++; src++;
+}
+
+
+}
+