Skip to content

Commit 9d3b317

Browse files
committed
Version 0.7 with Deep Denoise and add scripts
1 parent beac997 commit 9d3b317

20 files changed

Lines changed: 590 additions & 1 deletion

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ See [release notes](./releases.md) for version details.
1616
|[![Thumbnail](./docs/images/nonlinearstretchexample_thumb.png)](./docs/images/nonlinearstretchexample.png)|[Non-Linear Stretch](./docs/nonLinearStretch.md)|Helps tweak contrast by increasing foreground and decreasing background with progressive masks.
1717
|¯\\_(ツ)_|[Generate Decon Support](./docs/generateDeconSupport.md)|Creates the masks I use for deconvolution.|
1818
|[![Thumbnail](./docs/images/pillars_thumb.png)](./docs/images/pillars.png)|[Hubble Palette to RGB](./docs/applyHubbleToRGB.md)|Applies the "Hubble palette" to images.|
19+
|[![Thumbnail](./docs/images/denoiseexample_thumb.png)](./docs/images/denoiseexample_thumb.png)|[Deep Denoise](./docs/deepDenoise.md)|Removes noise from linear (non-stretched) images.|
1920

2021
## Installation
2122

deepDenoise.js

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
3+
deepDenoise.js: Denoise a linear (non-stretched) image.
4+
================================================================================
5+
This script implements my workflow for deconvolution. It will:
6+
7+
1. Create an extracted and stretched luminance mask modified for denoise
8+
2. Apply a multiscale linear transform to the luminance channel
9+
2. Apply a multiscale linear transform to the chrominance channel
10+
11+
================================================================================
12+
*
13+
*
14+
* Copyright Jeremy Likness, 2021
15+
*
16+
* License: https://github.com/DeepSkyWorkflows/DeepSkyWorkflowScripts/LICENSE
17+
*
18+
* Source: https://github.com/DeepSkyWorkflows/DeepSkyWorkflowScripts/
19+
*
20+
*
21+
* mailTo:deepskyworkflows@gmail.com
22+
*/
23+
24+
#feature-id DeepSkyWorkflows > DeepDenoise
25+
26+
#define TITLE "Deep Denoise"
27+
#define DEBUG_DD false
28+
#define FEATURE "deepDenoise"
29+
30+
#include "deepSkyCommon.js"
31+
#include "./deepDenoise/engine.js"
32+
#include "./deepDenoise/ui.js"
33+
34+
(function (ds) {
35+
36+
ds.debug.register(FEATURE, DEBUG_DD);
37+
ds.debug[FEATURE].debugLn(TITLE, 'debugging is on.');
38+
39+
ds.features.register(FEATURE, TITLE, denoiseEngine, { masks: {} });
40+
41+
ds.debug[FEATURE].debugLn(
42+
'Registered feature: ',
43+
JSON.stringify(ds.features[FEATURE]));
44+
45+
let bootstrap = ds.engine.bootstrap([
46+
{
47+
setting: "maskStrength",
48+
dataType: DataType_Int16,
49+
defaultValue: 5,
50+
precision: 0,
51+
label: "Strength of luminance mask:",
52+
tooltip: "A higher strength will protect more structures.",
53+
range: { low: 1, high: 10 }
54+
},
55+
{
56+
setting: "applyToLuminance",
57+
dataType: DataType_Boolean,
58+
defaultValue: true,
59+
label: "Apply to luminance",
60+
tooltip: "Choose whether or not to run denoise against luminance."
61+
},
62+
{
63+
setting: "lumMaxThreshold",
64+
dataType: DataType_Int16,
65+
defaultValue: 6,
66+
precision: 0,
67+
label: "Threshold for luminance denoise:",
68+
tooltip: "A higher level will remove more structures as noise.",
69+
range: { low: 1, high: 40 }
70+
},
71+
{
72+
setting: "lumNumLayers",
73+
dataType: DataType_Int16,
74+
defaultValue: 6,
75+
precision: 0,
76+
label: "Number of layers (out of 8) of luminance to apply denoise to:",
77+
tooltip: "Number of wavelet layers affected.",
78+
range: { low: 1, high: 8 }
79+
},
80+
{
81+
setting: "lumMaxAmount",
82+
dataType: DataType_Double,
83+
defaultValue: 0.5,
84+
label: "Max amount to apply:",
85+
precision: 2,
86+
tooltip: "Choose the maximum percentage of luminance denoise to apply.",
87+
range: { low: 0.1, high: 0.99 }
88+
},
89+
{
90+
setting: "applyToChrominance",
91+
dataType: DataType_Boolean,
92+
defaultValue: true,
93+
label: "Apply to chrominance",
94+
tooltip: "Choose whether or not to run denoise against chrominance."
95+
},
96+
{
97+
setting: "chromMaxThreshold",
98+
dataType: DataType_Int16,
99+
defaultValue: 6,
100+
precision: 0,
101+
label: "Threshold for chrominance denoise:",
102+
tooltip: "A higher level will remove more structures as noise.",
103+
range: { low: 1, high: 40 }
104+
},
105+
{
106+
setting: "chromNumLayers",
107+
dataType: DataType_Int16,
108+
defaultValue: 6,
109+
precision: 0,
110+
label: "Number of layers (out of 8) of chrominance to apply denoise to:",
111+
tooltip: "Number of wavelet layers affected.",
112+
range: { low: 1, high: 8 }
113+
},
114+
{
115+
setting: "chromMaxAmount",
116+
dataType: DataType_Double,
117+
defaultValue: 0.5,
118+
label: "Max amount to apply:",
119+
precision: 2,
120+
tooltip: "Choose the maximum percentage of chrominance denoise to apply.",
121+
range: { low: 0.1, high: 0.99 }
122+
},
123+
{
124+
setting: "applyLuminanceFirst",
125+
dataType: DataType_Boolean,
126+
defaultValue: true,
127+
label: "Apply to luminance first?",
128+
tooltip: "Choose whether or not to run denoise against luminance first or chrominance."
129+
}
130+
],
131+
ds.features[FEATURE].engine.doDenoise,
132+
function () {
133+
ds.debug[FEATURE].debugLn('New dialog requested.');
134+
return ds.ui.createDialog(denoiseDialog);
135+
},
136+
FEATURE,
137+
false);
138+
139+
bootstrap();
140+
ds.features[FEATURE].engine.validate();
141+
})(deepSky);

deepDenoise/engine.js

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
/*
2+
3+
engine.js: Denoise functionality.
4+
================================================================================
5+
This script implements my workflow for denoising linear (before stretching) images:
6+
7+
1. Create a luminance mask
8+
2. Adjust with a curves transformation
9+
3. Apply multiscale linear transformation to luminance
10+
4. Apply multiscale linear transformation to chrominance
11+
12+
================================================================================
13+
*
14+
*
15+
* Copyright Jeremy Likness, 2021
16+
*
17+
* License: https://github.com/DeepSkyWorkflows/DeepSkyWorkflowScripts/LICENSE
18+
*
19+
* Source: https://github.com/DeepSkyWorkflows/DeepSkyWorkflowScripts
20+
*
21+
*
22+
* mailTo:deepskyworkflows@gmail.com
23+
*/
24+
25+
let denoiseEngine = (function (ds) {
26+
return {
27+
doExtractLuminance: function () {
28+
const executionState = ds.getExecutionState();
29+
const utils = ds.utilities;
30+
this.engine.showStep("Extracting luminance....");
31+
utils.writeLines('Extracting luminance...');
32+
ds.engine.extractLuminance('_Lum');
33+
var lum = ImageWindow.windowById(executionState.channels[0][1]).mainView;
34+
console.writeln(utils.concatenateStr('Luminance extracted as ', lum.id));
35+
executionState.masks.lum = lum;
36+
},
37+
38+
generateLumMask: function () {
39+
const executionState = ds.getExecutionState();
40+
const utils = ds.utilities;
41+
42+
this.engine.showStep("Creating luminance mask....");
43+
utils.writeLines('Generating luminance mask...');
44+
45+
let dupId = ds.engine.pixMathClone(executionState.masks.lum, '_Mask');
46+
let lumMask = ImageWindow.windowById(dupId).mainView;
47+
executionState.masks.lum.window.forceClose();
48+
delete executionState.masks.lum;
49+
ds.engine.applySTF(lumMask);
50+
ds.engine.applyHistogramTransformation(lumMask);
51+
executionState.masks.lumMask = lumMask;
52+
},
53+
54+
applyCurves: function () {
55+
const executionState = ds.getExecutionState();
56+
const utils = ds.utilities;
57+
this.engine.showStep("Applying curves transformation...");
58+
utils.writeLines("Applying curves transformation...");
59+
var strength = ((executionState.config.prefs.maskStrength * 0.4) / 10.0) + 0.05;
60+
var upper = 1.0 - strength;
61+
var curves = new CurvesTransformation;
62+
curves.K = [ // x, y
63+
[0.00000, strength],
64+
[1.00000, upper]
65+
];
66+
curves.Kt = CurvesTransformation.prototype.AkimaSubsplines;
67+
curves.executeOn(executionState.masks.lumMask);
68+
},
69+
70+
getDenoiseProcess: function (isLum, maxThreshold, numLayers, maxAmount) {
71+
const mlt = new MultiscaleLinearTransform;
72+
let t = maxThreshold, tStep = maxThreshold / numLayers;
73+
let a = maxAmount, aStep = maxAmount / numLayers;
74+
let i = numLayers;
75+
const layers = [];
76+
for (let idx = 0; idx < 8; idx++) {
77+
// enabled, biasEnabled, bias, noiseReductionEnabled, noiseReductionThreshold, noiseReductionAmount, noiseReductionIterations
78+
const denoise = [true, true, 0.000, i > 0, t > 0 ? t : 1, a > 0 ? a : 0.001, i > 0 ? i : 1];
79+
80+
layers.push(denoise);
81+
82+
if (t > 0) {
83+
t -= tStep;
84+
}
85+
if (a > 0) {
86+
a -= aStep;
87+
}
88+
if (i > 0) {
89+
i--;
90+
}
91+
}
92+
mlt.layers = layers;
93+
mlt.transform = MultiscaleLinearTransform.prototype.StarletTransform;
94+
mlt.scaleDelta = 0;
95+
mlt.scalingFunctionData = [
96+
0.25, 0.5, 0.25,
97+
0.5, 1, 0.5,
98+
0.25, 0.5, 0.25
99+
];
100+
mlt.scalingFunctionRowFilter = [
101+
0.5,
102+
1,
103+
0.5
104+
];
105+
mlt.scalingFunctionColFilter = [
106+
0.5,
107+
1,
108+
0.5
109+
];
110+
mlt.scalingFunctionNoiseSigma = [
111+
0.8003, 0.2729, 0.1198,
112+
0.0578, 0.0287, 0.0143,
113+
0.0072, 0.0036, 0.0019,
114+
0.001
115+
];
116+
mlt.scalingFunctionName = "Linear Interpolation (3)";
117+
mlt.largeScaleFunction = MultiscaleLinearTransform.prototype.NoFunction;
118+
mlt.curveBreakPoint = 0.75;
119+
mlt.previewMode = MultiscaleLinearTransform.prototype.Disabled;
120+
mlt.previewLayer = 0;
121+
mlt.toLuminance = isLum;
122+
mlt.toChrominance = !isLum;
123+
mlt.linear = true;
124+
125+
return mlt;
126+
},
127+
128+
applyLum: function () {
129+
const executionState = ds.getExecutionState();
130+
131+
if (executionState.config.prefs.applyToLuminance === false) {
132+
return;
133+
}
134+
135+
this.engine.showStep("Applying denoise to luminance....");
136+
ds.utilities.writeLines("Applying denoise to luminance....");
137+
138+
const mlt = this.engine.getDenoiseProcess(
139+
true,
140+
executionState.config.prefs.lumMaxThreshold,
141+
executionState.config.prefs.lumNumLayers,
142+
executionState.config.prefs.lumMaxAmount);
143+
144+
mlt.executeOn(executionState.view);
145+
},
146+
147+
applyChrome: function () {
148+
const executionState = ds.getExecutionState();
149+
150+
if (executionState.config.prefs.applyToChrominance === false) {
151+
return;
152+
}
153+
154+
this.engine.showStep("Applying denoise to chrominance....");
155+
ds.utilities.writeLines("Applying denoise to chrominance....");
156+
157+
const mlt = this.engine.getDenoiseProcess(
158+
false,
159+
executionState.config.prefs.chromMaxThreshold,
160+
executionState.config.prefs.chromNumLayers,
161+
executionState.config.prefs.chromMaxAmount);
162+
163+
mlt.executeOn(executionState.view);
164+
},
165+
166+
showStep: function (message) {
167+
const executionState = ds.getExecutionState();
168+
const utils = ds.utilities;
169+
170+
executionState.step++;
171+
executionState.updateProgress(
172+
utils.concatenateStr('Step ', executionState.step,
173+
' of ', executionState.steps,
174+
': ', message));
175+
},
176+
177+
doDenoise: function () {
178+
const executionState = ds.getExecutionState();
179+
const utils = ds.utilities;
180+
181+
executionState.updateProgress = executionState.updateProgress ||
182+
function () { };
183+
184+
executionState.step = 0;
185+
executionState.steps = 5;
186+
187+
if (executionState.config.prefs.applyToLuminance === false) {
188+
executionState.steps--;
189+
}
190+
191+
if (executionState.config.prefs.applyToChrominance === false) {
192+
executionState.steps--;
193+
}
194+
195+
this.engine.doExtractLuminance();
196+
this.engine.generateLumMask();
197+
this.engine.applyCurves();
198+
199+
executionState.view.window.mask = executionState.masks.lumMask.window;
200+
executionState.view.window.maskEnabled = true;
201+
executionState.view.window.maskInverted = true;
202+
executionState.view.window.maskVisible = true;
203+
204+
if (executionState.config.prefs.applyLuminanceFirst === true) {
205+
this.engine.applyLum();
206+
this.engine.applyChrome();
207+
}
208+
else {
209+
this.engine.applyChrome();
210+
this.engine.applyLum();
211+
}
212+
213+
executionState.masks.lumMask.window.forceClose();
214+
delete executionState.masks.lumMask;
215+
216+
utils.writeLines('Done.');
217+
},
218+
219+
validate: function () {
220+
const executionState = ds.getExecutionState();
221+
const utils = ds.utilities;
222+
const dialog = ds.getDialog();
223+
if (executionState.view === null || executionState.view === undefined) {
224+
ds.ui.showDialog("Validation failed", "No active view!");
225+
executionState.config.closeOnExit = true;
226+
return false;
227+
}
228+
if (executionState.view.image.numberOfChannels !== 3) {
229+
ds.ui.showDialog("Validation failed", "Active view is not color.");
230+
executionState.config.closeOnExit = true;
231+
return false;
232+
}
233+
return true;
234+
}
235+
};
236+
})(deepSky);

0 commit comments

Comments
 (0)