Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rawdenoise: change force by frequency and channel #1752

Conversation

rawfiner
Copy link
Collaborator

This pull request adds a GUI to rawdenoise similar to the one of the equalizer.

It does NOT change the underlying algorithm, but allows the user to finely control the denoising:

  • the user can change the force band by band. For instance, this is useful if user wants to preserve fine-grain noise
  • the user can change the force band by band for each channel. As color channels are usually not noisy in the same way (coarseness of the noise and amount of noise), this allows the user to adjust the denoising accordingly (this may produce an image with some pixels of wrong color, but it is easily fixed with a denoiseprofile instance with wavelet mode and blendmode color).

I think this gives much more power to the user, without adding complexity in the algorithm.
(almost all the code change in this pull request are for the GUI.)

@TurboGit TurboGit added the feature: enhancement current features to improve label Oct 16, 2018
@rawfiner
Copy link
Collaborator Author

example_rawdenoise
Here is an example of what we can do with it. In both images, there is a denoise profile in wavelet mode and blendmode color. Left is with these changes, by playing with the curve, and right is without. As we can see, the noise is much more fine grain on the left, whereas on the right is is coarse.
The changes made allow to preserve fine grain details while reducing coarse grain noise if the user needs it.

@aurelienpierre
Copy link
Member

I have run it for 2 days, no issue.

@TurboGit
Copy link
Member

Same remark here (see #1753), without tweaking the graphic, do we get the same result than before?

@rawfiner
Copy link
Collaborator Author

rawfiner commented Oct 16, 2018

Same remark here (see #1753), without tweaking the graphic, do we get the same result than before?

Yes, we get the same result.
The way it works is the same as in #1753.

This part of the code handles the "all" curve:
for(int i = 0; i < DT_IOP_RAWDENOISE_BANDS; i++)
{
// scale the value from [0,1] to [0,16],
// and makes the "0.5" neutral value become 1
float threshold_exp_4 = data->force[rawdenoise_all][DT_IOP_RAWDENOISE_BANDS - i - 1];
threshold_exp_4 *= threshold_exp_4;
threshold_exp_4 *= threshold_exp_4;
noise_all[i] = noise_all[i] * threshold_exp_4 * 16.0;
}

The point gathered from the curve is squared twice. Thus 0.5 (the neutral value of the curve) is mapped to 1/16. We multiply this by 16, which maps our 0.5 to 1.
Then, this number is used to multiply the noise threshold of the scale, and thus, if the "all" curve is flat the noise threshold does not change.

The code bellow does the same thing for each "channel" (we do not really have channel before demosaic, but each pixel has one of the 3 channels. Thus we use the green curve on "green" pixels, red curve on "red" pixels etc..)

float threshold_exp_4;
switch(c)
{
case 0:
threshold_exp_4 = data->force[rawdenoise_R][DT_IOP_RAWDENOISE_BANDS - i - 1];
break;
case 3:
threshold_exp_4 = data->force[rawdenoise_B][DT_IOP_RAWDENOISE_BANDS - i - 1];
break;
default:
threshold_exp_4 = data->force[rawdenoise_G][DT_IOP_RAWDENOISE_BANDS - i - 1];
break;
}
threshold_exp_4 *= threshold_exp_4;
threshold_exp_4 *= threshold_exp_4;
noise[i] = noise_all[i] * threshold_exp_4 * 16.0;
}

The switch case is slightly different for xtrans code, as in bayer code c==1 an d c==2 both corresponds to green, whereas in xtrans code, c directly corresponds to the pixel channel (0 for R, 1 for G, and 2 for B)

@rawfiner
Copy link
Collaborator Author

Sorry for the code formatting in the quotes, I am not sure yet how to refer to code from https://github.com/darktable-org/darktable/pull/1752/files

@TurboGit TurboGit self-assigned this Oct 16, 2018
@TurboGit
Copy link
Member

I've tested it, this is really nice. But please if you read this test it before it is merged. I'd like to have this in 2.6, so more testing would be nice.

@TurboGit TurboGit added this to the 2.6 milestone Oct 16, 2018
@TurboGit TurboGit self-requested a review October 16, 2018 16:34
@aurelienpierre
Copy link
Member

I'm playing with it as well. Works great so far.

@rawfiner
Copy link
Collaborator Author

I have just found a bug for bayer: the colors are not always with same index. This means that with current implementation, sometimes the blue slider modify the green, sometimes the blue, etc.
I will fix this as soon as possible.

c does not correspond to a color, but to the number of an "origin" pixel in the top left 2x2 square.
We have to convert it to a color.
@rawfiner
Copy link
Collaborator Author

The bug should be fixed now. I misinterpreted a variable, thinking it was representing a color, whereas it represented "somewhat" a coordinate in the top left 2x2 square of pixels.

@jpg54
Copy link

jpg54 commented Oct 17, 2018

I have been using the new RawFiner feature in the Rawdenoise module for some time. I like that he introduced in darktable the experiments he did on noise processing with dtStyles and shared. I would like to see them in the next version 2.6.

#define DT_IOP_RAWDENOISE_RES 64
#define DT_IOP_RAWDENOISE_BANDS 5

typedef enum dt_iop_rawdenoise_channel_t {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The { should be under typedef.
All entries should be capitalized and with DT_ as prefix:

DT_RAWDENOISE_ALL = 0,
...

{
if(old_version == 1 && new_version == 2)
{
dt_iop_rawdenoise_params_t *o = (dt_iop_rawdenoise_params_t *)old_params;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment about legacy_param in #1753

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is lot simpler to solve as this new version is only the second one!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment to explain how it works.
I used this as I saw this was the way it was done in denoiseprofile and nlmeans for example.

But if you prefer, I can define a new struct so that the changes are more explicit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, fine I read as if you were copying the would struct with memcpy() and that you were reading past the old struct. I don't this that now! Good to me, you are copying the field one by one.

src/iop/rawdenoise.c Show resolved Hide resolved
for(int k = 0; k < DT_IOP_RAWDENOISE_BANDS; k++)
(void)dt_draw_curve_add_point(d->curve[ch], default_params->x[ch][k], default_params->y[ch][k]);
}
self->commit_params(self, self->default_params, pipe, piece); // TODO necessary?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is necessary! That's the only way to passe the params between pixelpipe and module params.

g_signal_connect(G_OBJECT(c->channel_tabs), "switch_page", G_CALLBACK(rawdenoise_tab_switch), self);

c->channel = dt_conf_get_int("plugins/darkroom/rawdenoise/gui_channel");
int ch = (int)c->channel;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can probably be made const?

@rawfiner
Copy link
Collaborator Author

Thank you for the code review ! :-)

{
if(old_version == 1 && new_version == 2)
{
dt_iop_rawdenoise_params_t *o = (dt_iop_rawdenoise_params_t *)old_params;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, fine I read as if you were copying the would struct with memcpy() and that you were reading past the old struct. I don't this that now! Good to me, you are copying the field one by one.

@TurboGit
Copy link
Member

All good now too. Thanks.

@TurboGit TurboGit merged commit b90f23f into darktable-org:master Oct 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: enhancement current features to improve
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants