Cycles: Implement denoising option for reducing noise in the rendered image
[blender.git] / intern / cycles / kernel / filter / filter_nlm_gpu.h
1 /*
2  * Copyright 2011-2017 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 CCL_NAMESPACE_BEGIN
18
19 ccl_device_inline void kernel_filter_nlm_calc_difference(int x, int y,
20                                                          int dx, int dy,
21                                                          ccl_global float ccl_restrict_ptr weightImage,
22                                                          ccl_global float ccl_restrict_ptr varianceImage,
23                                                          ccl_global float *differenceImage,
24                                                          int4 rect, int w,
25                                                          int channel_offset,
26                                                          float a, float k_2)
27 {
28         float diff = 0.0f;
29         int numChannels = channel_offset? 3 : 1;
30         for(int c = 0; c < numChannels; c++) {
31                 float cdiff = weightImage[c*channel_offset + y*w+x] - weightImage[c*channel_offset + (y+dy)*w+(x+dx)];
32                 float pvar = varianceImage[c*channel_offset + y*w+x];
33                 float qvar = varianceImage[c*channel_offset + (y+dy)*w+(x+dx)];
34                 diff += (cdiff*cdiff - a*(pvar + min(pvar, qvar))) / (1e-8f + k_2*(pvar+qvar));
35         }
36         if(numChannels > 1) {
37                 diff *= 1.0f/numChannels;
38         }
39         differenceImage[y*w+x] = diff;
40 }
41
42 ccl_device_inline void kernel_filter_nlm_blur(int x, int y,
43                                               ccl_global float ccl_restrict_ptr differenceImage,
44                                               ccl_global float *outImage,
45                                               int4 rect, int w, int f)
46 {
47         float sum = 0.0f;
48         const int low = max(rect.y, y-f);
49         const int high = min(rect.w, y+f+1);
50         for(int y1 = low; y1 < high; y1++) {
51                 sum += differenceImage[y1*w+x];
52         }
53         sum *= 1.0f/(high-low);
54         outImage[y*w+x] = sum;
55 }
56
57 ccl_device_inline void kernel_filter_nlm_calc_weight(int x, int y,
58                                                      ccl_global float ccl_restrict_ptr differenceImage,
59                                                      ccl_global float *outImage,
60                                                      int4 rect, int w, int f)
61 {
62         float sum = 0.0f;
63         const int low = max(rect.x, x-f);
64         const int high = min(rect.z, x+f+1);
65         for(int x1 = low; x1 < high; x1++) {
66                 sum += differenceImage[y*w+x1];
67         }
68         sum *= 1.0f/(high-low);
69         outImage[y*w+x] = expf(-max(sum, 0.0f));
70 }
71
72 ccl_device_inline void kernel_filter_nlm_update_output(int x, int y,
73                                                        int dx, int dy,
74                                                        ccl_global float ccl_restrict_ptr differenceImage,
75                                                        ccl_global float ccl_restrict_ptr image,
76                                                        ccl_global float *outImage,
77                                                        ccl_global float *accumImage,
78                                                        int4 rect, int w, int f)
79 {
80         float sum = 0.0f;
81         const int low = max(rect.x, x-f);
82         const int high = min(rect.z, x+f+1);
83         for(int x1 = low; x1 < high; x1++) {
84                 sum += differenceImage[y*w+x1];
85         }
86         sum *= 1.0f/(high-low);
87         if(outImage) {
88                 accumImage[y*w+x] += sum;
89                 outImage[y*w+x] += sum*image[(y+dy)*w+(x+dx)];
90         }
91         else {
92                 accumImage[y*w+x] = sum;
93         }
94 }
95
96 ccl_device_inline void kernel_filter_nlm_construct_gramian(int fx, int fy,
97                                                            int dx, int dy,
98                                                            ccl_global float ccl_restrict_ptr differenceImage,
99                                                            ccl_global float ccl_restrict_ptr buffer,
100                                                            ccl_global float *color_pass,
101                                                            ccl_global float *variance_pass,
102                                                            ccl_global float ccl_restrict_ptr transform,
103                                                            ccl_global int *rank,
104                                                            ccl_global float *XtWX,
105                                                            ccl_global float3 *XtWY,
106                                                            int4 rect,
107                                                            int4 filter_rect,
108                                                            int w, int h, int f,
109                                                            int pass_stride,
110                                                            int localIdx)
111 {
112         int y = fy + filter_rect.y;
113         int x = fx + filter_rect.x;
114         const int low = max(rect.x, x-f);
115         const int high = min(rect.z, x+f+1);
116         float sum = 0.0f;
117         for(int x1 = low; x1 < high; x1++) {
118                 sum += differenceImage[y*w+x1];
119         }
120         float weight = sum * (1.0f/(high - low));
121
122         int storage_ofs = fy*filter_rect.z + fx;
123         transform += storage_ofs;
124         rank += storage_ofs;
125         XtWX += storage_ofs;
126         XtWY += storage_ofs;
127
128         kernel_filter_construct_gramian(x, y,
129                                         filter_rect.z*filter_rect.w,
130                                         dx, dy, w, h,
131                                         pass_stride,
132                                         buffer,
133                                         color_pass, variance_pass,
134                                         transform, rank,
135                                         weight, XtWX, XtWY,
136                                         localIdx);
137 }
138
139 ccl_device_inline void kernel_filter_nlm_normalize(int x, int y,
140                                                    ccl_global float *outImage,
141                                                    ccl_global float ccl_restrict_ptr accumImage,
142                                                    int4 rect, int w)
143 {
144         outImage[y*w+x] /= accumImage[y*w+x];
145 }
146
147 CCL_NAMESPACE_END