1
2 //
3 // This source file is part of appleseed.
4 // Visit https://appleseedhq.net/ for additional information and resources.
5 //
6 // This software is released under the MIT license.
7 //
8 // Copyright (c) 2018 Esteban Tovagliari, The appleseedhq Organization
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a copy
11 // of this software and associated documentation files (the "Software"), to deal
12 // in the Software without restriction, including without limitation the rights
13 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 // copies of the Software, and to permit persons to whom the Software is
15 // furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included in
18 // all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 // THE SOFTWARE.
27 //
28
29 // Interface header.
30 #include "denoiser.h"
31
32 // appleseed.renderer headers.
33 #include "renderer/global/globallogger.h"
34
35 // appleseed.foundation headers.
36 #include "foundation/image/color.h"
37 #include "foundation/image/image.h"
38 #include "foundation/utility/job/iabortswitch.h"
39
40 // BCD headers.
41 #include "bcd/DeepImage.h"
42 #include "bcd/Denoiser.h"
43 #include "bcd/IDenoiser.h"
44 #include "bcd/MultiscaleDenoiser.h"
45 #include "bcd/SpikeRemovalFilter.h"
46 #include "bcd/Utils.h"
47
48 // Standard headers.
49 #include <cmath>
50 #include <memory>
51 #include <vector>
52
53 using namespace bcd;
54 using namespace foundation;
55 using namespace std;
56
57 namespace renderer
58 {
59
60 namespace
61 {
62
image_to_deepimage(const Image & src,Deepimf & dst)63 void image_to_deepimage(const Image& src, Deepimf& dst)
64 {
65 const CanvasProperties& src_props = src.properties();
66 assert(src_props.m_channel_count == 4);
67
68 dst.resize(
69 static_cast<int>(src_props.m_canvas_width),
70 static_cast<int>(src_props.m_canvas_height),
71 3);
72
73 for (size_t j = 0; j < src_props.m_canvas_height; ++j)
74 {
75 for (size_t i = 0; i < src_props.m_canvas_width; ++i)
76 {
77 Color4f c;
78 src.get_pixel(i, j, c);
79 c.unpremultiply_in_place();
80
81 dst.set(static_cast<int>(j), static_cast<int>(i), 0, c[0]);
82 dst.set(static_cast<int>(j), static_cast<int>(i), 1, c[1]);
83 dst.set(static_cast<int>(j), static_cast<int>(i), 2, c[2]);
84 }
85 }
86 }
87
deepimage_to_image(const Deepimf & src,Image & dst)88 void deepimage_to_image(const Deepimf& src, Image& dst)
89 {
90 const CanvasProperties& dst_props = dst.properties();
91
92 assert(src.getWidth() == dst_props.m_canvas_width);
93 assert(src.getHeight() == dst_props.m_canvas_height);
94 assert(src.getDepth() == 3);
95 assert(dst_props.m_channel_count == 4);
96
97 for (size_t j = 0; j < dst_props.m_canvas_height; ++j)
98 {
99 for (size_t i = 0; i < dst_props.m_canvas_width; ++i)
100 {
101 Color4f c;
102 dst.get_pixel(i, j, c);
103
104 c[0] = src.get(static_cast<int>(j), static_cast<int>(i), 0);
105 c[1] = src.get(static_cast<int>(j), static_cast<int>(i), 1);
106 c[2] = src.get(static_cast<int>(j), static_cast<int>(i), 2);
107
108 c.premultiply_in_place();
109 dst.set_pixel(i, j, c);
110 }
111 }
112 }
113
114 class DenoiserCallbacks
115 : public ICallbacks
116 {
117 public:
DenoiserCallbacks(IAbortSwitch * abort_switch)118 explicit DenoiserCallbacks(IAbortSwitch* abort_switch)
119 : m_abort_switch(abort_switch)
120 {
121 }
122
progress(const float p) const123 void progress(const float p) const override
124 {
125 }
126
isAborted() const127 bool isAborted() const override
128 {
129 if (m_abort_switch)
130 return m_abort_switch->is_aborted();
131
132 return false;
133 }
134
135 private:
logInfo(const char * msg) const136 void logInfo(const char* msg) const override
137 {
138 RENDERER_LOG_INFO("%s", msg);
139 }
140
logWarning(const char * msg) const141 void logWarning(const char* msg) const override
142 {
143 RENDERER_LOG_WARNING("%s", msg);
144 }
145
logError(const char * msg) const146 void logError(const char* msg) const override
147 {
148 RENDERER_LOG_ERROR("%s", msg);
149 }
150
logDebug(const char * msg) const151 void logDebug(const char* msg) const override
152 {
153 RENDERER_LOG_DEBUG("%s", msg);
154 }
155
156 IAbortSwitch* m_abort_switch;
157 };
158
do_denoise_image(Deepimf & src,const Deepimf & num_samples,const Deepimf & histograms,const Deepimf & covariances,const DenoiserOptions & options,IAbortSwitch * abort_switch,Deepimf & dst)159 bool do_denoise_image(
160 Deepimf& src,
161 const Deepimf& num_samples,
162 const Deepimf& histograms,
163 const Deepimf& covariances,
164 const DenoiserOptions& options,
165 IAbortSwitch* abort_switch,
166 Deepimf& dst)
167 {
168 DenoiserInputs inputs;
169 inputs.m_pColors = &src;
170 inputs.m_pNbOfSamples = &num_samples;
171 inputs.m_pHistograms = &histograms;
172 inputs.m_pSampleCovariances = &covariances;
173
174 DenoiserParameters parameters;
175 parameters.m_histogramDistanceThreshold = options.m_histogram_patch_distance_threshold;
176 parameters.m_patchRadius = static_cast<int>(options.m_patch_radius);
177 parameters.m_searchWindowRadius = static_cast<int>(options.m_search_window_radius);
178 parameters.m_minEigenValue = options.m_min_eigenvalue;
179 parameters.m_useRandomPixelOrder = options.m_use_random_pixel_order;
180 parameters.m_markedPixelsSkippingProbability = options.m_marked_pixels_skipping_probability;
181 parameters.m_nbOfCores = static_cast<int>(options.m_num_cores);
182 parameters.m_markInvalidPixels = options.m_mark_invalid_pixels;
183
184 DenoiserOutputs outputs;
185 outputs.m_pDenoisedColors = &dst;
186
187 unique_ptr<IDenoiser> denoiser;
188
189 if (options.m_num_scales > 1)
190 denoiser.reset(new MultiscaleDenoiser(static_cast<int>(options.m_num_scales)));
191 else
192 denoiser.reset(new Denoiser());
193
194 DenoiserCallbacks callbacks(abort_switch);
195 denoiser->setCallbacks(&callbacks);
196
197 denoiser->setInputs(inputs);
198 denoiser->setOutputs(outputs);
199 denoiser->setParameters(parameters);
200
201 if (!denoiser->inputsOutputsAreOk())
202 return false;
203
204 return denoiser->denoise();
205 }
206
207 }
208
denoise_beauty_image(Image & img,Deepimf & num_samples,Deepimf & histograms,Deepimf & covariances,const DenoiserOptions & options,IAbortSwitch * abort_switch)209 bool denoise_beauty_image(
210 Image& img,
211 Deepimf& num_samples,
212 Deepimf& histograms,
213 Deepimf& covariances,
214 const DenoiserOptions& options,
215 IAbortSwitch* abort_switch)
216 {
217 Deepimf src;
218 image_to_deepimage(img, src);
219
220 if (options.m_prefilter_spikes)
221 {
222 SpikeRemovalFilter::filter(
223 src,
224 num_samples,
225 histograms,
226 covariances,
227 options.m_prefilter_threshold_stddev_factor);
228 }
229
230 Deepimf dst(src);
231
232 const bool success =
233 do_denoise_image(
234 src,
235 num_samples,
236 histograms,
237 covariances,
238 options,
239 abort_switch,
240 dst);
241
242 if (success)
243 deepimage_to_image(dst, img);
244
245 return success;
246 }
247
denoise_aov_image(Image & img,const Deepimf & num_samples,const Deepimf & histograms,const Deepimf & covariances,const DenoiserOptions & options,IAbortSwitch * abort_switch)248 bool denoise_aov_image(
249 Image& img,
250 const Deepimf& num_samples,
251 const Deepimf& histograms,
252 const Deepimf& covariances,
253 const DenoiserOptions& options,
254 IAbortSwitch* abort_switch)
255 {
256 Deepimf src;
257 image_to_deepimage(img, src);
258
259 if (options.m_prefilter_spikes)
260 {
261 SpikeRemovalFilter::filter(
262 src,
263 options.m_prefilter_threshold_stddev_factor);
264 }
265
266 Deepimf dst(src);
267
268 const bool success =
269 do_denoise_image(
270 src,
271 num_samples,
272 histograms,
273 covariances,
274 options,
275 abort_switch,
276 dst);
277
278 if (success)
279 deepimage_to_image(dst, img);
280
281 return success;
282 }
283
284 } // namespace renderer
285