Halide
BoundaryConditions.h
Go to the documentation of this file.
1 #ifndef HALIDE_BOUNDARY_CONDITIONS_H
2 #define HALIDE_BOUNDARY_CONDITIONS_H
3 
4 /** \file
5  * Support for imposing boundary conditions on Halide::Funcs.
6  */
7 
8 #include <vector>
9 
10 #include "Expr.h"
11 #include "Func.h"
12 #include "Lambda.h"
13 
14 namespace Halide {
15 
16 /** namespace to hold functions for imposing boundary conditions on
17  * Halide Funcs.
18  *
19  * All functions in this namespace transform a source Func to a
20  * result Func where the result produces the values of the source
21  * within a given region and a different set of values outside the
22  * given region. A region is an N dimensional box specified by
23  * mins and extents.
24  *
25  * Three areas are defined:
26  * The image is the entire set of values in the region.
27  * The edge is the set of pixels in the image but adjacent
28  * to coordinates that are not
29  * The interior is the image minus the edge (and is undefined
30  * if the extent of any region is 1 or less).
31  *
32  * If the source Func has more dimensions than are specified, the extra ones
33  * are unmodified. Additionally, passing an undefined (default constructed)
34  * 'Expr' for the min and extent of a dimension will keep that dimension
35  * unmodified.
36  *
37  * Numerous options for specifing the outside area are provided,
38  * including replacement with an expression, repeating the edge
39  * samples, mirroring over the edge, and repeating or mirroring the
40  * entire image.
41  *
42  * Using these functions to express your boundary conditions is highly
43  * recommended for correctness and performance. Some of these are hard
44  * to get right. The versions here are both understood by bounds
45  * inference, and also judiciously use the 'likely' intrinsic to minimize
46  * runtime overhead.
47  *
48  */
49 namespace BoundaryConditions {
50 
51 namespace Internal {
52 
53 inline HALIDE_NO_USER_CODE_INLINE void collect_region(Region &collected_args,
54  const Expr &a1, const Expr &a2) {
55  collected_args.push_back(Range(a1, a2));
56 }
57 
58 template<typename... Args>
59 inline HALIDE_NO_USER_CODE_INLINE void collect_region(Region &collected_args,
60  const Expr &a1, const Expr &a2, Args &&... args) {
61  collected_args.push_back(Range(a1, a2));
62  collect_region(collected_args, std::forward<Args>(args)...);
63 }
64 
65 inline const Func &func_like_to_func(const Func &func) {
66  return func;
67 }
68 
69 template<typename T>
71  return lambda(_, func_like(_));
72 }
73 
74 } // namespace Internal
75 
76 /** Impose a boundary condition such that a given expression is returned
77  * everywhere outside the boundary. Generally the expression will be a
78  * constant, though the code currently allows accessing the arguments
79  * of source.
80  *
81  * An ImageParam, Buffer<T>, or similar can be passed instead of a
82  * Func. If this is done and no bounds are given, the boundaries will
83  * be taken from the min and extent methods of the passed
84  * object. Note that objects are taken by mutable ref. Pipelines
85  * capture Buffers via mutable refs, because running a pipeline might
86  * alter the Buffer metadata (e.g. device allocation state).
87  *
88  * (This is similar to setting GL_TEXTURE_WRAP_* to GL_CLAMP_TO_BORDER
89  * and putting value in the border of the texture.)
90  *
91  * You may pass undefined Exprs for dimensions that you do not wish
92  * to bound.
93  */
94 // @{
95 Func constant_exterior(const Func &source, const Tuple &value,
96  const Region &bounds);
97 Func constant_exterior(const Func &source, const Expr &value,
98  const Region &bounds);
99 
100 template<typename T>
101 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Tuple &value, const Region &bounds) {
102  return constant_exterior(Internal::func_like_to_func(func_like), value, bounds);
103 }
104 
105 template<typename T>
106 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Expr &value, const Region &bounds) {
107  return constant_exterior(Internal::func_like_to_func(func_like), value, bounds);
108 }
109 
110 template<typename T>
111 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Tuple &value) {
112  Region object_bounds;
113  for (int i = 0; i < func_like.dimensions(); i++) {
114  object_bounds.push_back({Expr(func_like.dim(i).min()), Expr(func_like.dim(i).extent())});
115  }
116 
117  return constant_exterior(Internal::func_like_to_func(func_like), value, object_bounds);
118 }
119 template<typename T>
120 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Expr &value) {
121  return constant_exterior(func_like, Tuple(value));
122 }
123 
124 template<typename T, typename... Bounds,
125  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
126 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Tuple &value,
127  Bounds &&... bounds) {
128  Region collected_bounds;
129  Internal::collect_region(collected_bounds, std::forward<Bounds>(bounds)...);
130  return constant_exterior(Internal::func_like_to_func(func_like), value, collected_bounds);
131 }
132 template<typename T, typename... Bounds,
133  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
134 HALIDE_NO_USER_CODE_INLINE Func constant_exterior(const T &func_like, const Expr &value,
135  Bounds &&... bounds) {
136  return constant_exterior(func_like, Tuple(value), std::forward<Bounds>(bounds)...);
137 }
138 // @}
139 
140 /** Impose a boundary condition such that the nearest edge sample is returned
141  * everywhere outside the given region.
142  *
143  * An ImageParam, Buffer<T>, or similar can be passed instead of a Func. If this
144  * is done and no bounds are given, the boundaries will be taken from the
145  * min and extent methods of the passed object.
146  *
147  * (This is similar to setting GL_TEXTURE_WRAP_* to GL_CLAMP_TO_EDGE.)
148  *
149  * You may pass undefined Exprs for dimensions that you do not wish
150  * to bound.
151  */
152 // @{
153 Func repeat_edge(const Func &source, const Region &bounds);
154 
155 template<typename T>
156 HALIDE_NO_USER_CODE_INLINE Func repeat_edge(const T &func_like, const Region &bounds) {
157  return repeat_edge(Internal::func_like_to_func(func_like), bounds);
158 }
159 
160 template<typename T>
162  Region object_bounds;
163  for (int i = 0; i < func_like.dimensions(); i++) {
164  object_bounds.push_back({Expr(func_like.dim(i).min()), Expr(func_like.dim(i).extent())});
165  }
166 
167  return repeat_edge(Internal::func_like_to_func(func_like), object_bounds);
168 }
169 
170 template<typename T, typename... Bounds,
171  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
172 HALIDE_ATTRIBUTE_DEPRECATED("Add braces around the bounds like so: {{a, b}, {c, d}}")
173 HALIDE_NO_USER_CODE_INLINE Func repeat_edge(const T &func_like, Bounds &&... bounds) {
174  Region collected_bounds;
175  Internal::collect_region(collected_bounds, std::forward<Bounds>(bounds)...);
176  return repeat_edge(Internal::func_like_to_func(func_like), collected_bounds);
177 }
178 // @}
179 
180 /** Impose a boundary condition such that the entire coordinate space is
181  * tiled with copies of the image abutted against each other.
182  *
183  * An ImageParam, Buffer<T>, or similar can be passed instead of a Func. If this
184  * is done and no bounds are given, the boundaries will be taken from the
185  * min and extent methods of the passed object.
186  *
187  * (This is similar to setting GL_TEXTURE_WRAP_* to GL_REPEAT.)
188  *
189  * You may pass undefined Exprs for dimensions that you do not wish
190  * to bound.
191  */
192 // @{
193 Func repeat_image(const Func &source, const Region &bounds);
194 
195 template<typename T>
196 HALIDE_NO_USER_CODE_INLINE Func repeat_image(const T &func_like, const Region &bounds) {
197  return repeat_image(Internal::func_like_to_func(func_like), bounds);
198 }
199 
200 template<typename T>
202  Region object_bounds;
203  for (int i = 0; i < func_like.dimensions(); i++) {
204  object_bounds.push_back({Expr(func_like.dim(i).min()), Expr(func_like.dim(i).extent())});
205  }
206 
207  return repeat_image(Internal::func_like_to_func(func_like), object_bounds);
208 }
209 
210 template<typename T, typename... Bounds,
211  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
212 HALIDE_ATTRIBUTE_DEPRECATED("Add braces around the bounds like so: {{a, b}, {c, d}}")
213 HALIDE_NO_USER_CODE_INLINE Func repeat_image(const T &func_like, Bounds &&... bounds) {
214  Region collected_bounds;
215  Internal::collect_region(collected_bounds, std::forward<Bounds>(bounds)...);
216  return repeat_image(Internal::func_like_to_func(func_like), collected_bounds);
217 }
218 
219 /** Impose a boundary condition such that the entire coordinate space is
220  * tiled with copies of the image abutted against each other, but mirror
221  * them such that adjacent edges are the same.
222  *
223  * An ImageParam, Buffer<T>, or similar can be passed instead of a Func. If this
224  * is done and no bounds are given, the boundaries will be taken from the
225  * min and extent methods of the passed object.
226  *
227  * (This is similar to setting GL_TEXTURE_WRAP_* to GL_MIRRORED_REPEAT.)
228  *
229  * You may pass undefined Exprs for dimensions that you do not wish
230  * to bound.
231  */
232 // @{
233 Func mirror_image(const Func &source, const Region &bounds);
234 
235 template<typename T>
236 HALIDE_NO_USER_CODE_INLINE Func mirror_image(const T &func_like, const Region &bounds) {
237  return mirror_image(Internal::func_like_to_func(func_like), bounds);
238 }
239 
240 template<typename T>
242  Region object_bounds;
243  for (int i = 0; i < func_like.dimensions(); i++) {
244  object_bounds.push_back({Expr(func_like.dim(i).min()), Expr(func_like.dim(i).extent())});
245  }
246 
247  return mirror_image(Internal::func_like_to_func(func_like), object_bounds);
248 }
249 
250 template<typename T, typename... Bounds,
251  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
252 HALIDE_ATTRIBUTE_DEPRECATED("Add braces around the bounds like so: {{a, b}, {c, d}}")
253 HALIDE_NO_USER_CODE_INLINE Func mirror_image(const T &func_like, Bounds &&... bounds) {
254  Region collected_bounds;
255  Internal::collect_region(collected_bounds, std::forward<Bounds>(bounds)...);
256  return mirror_image(Internal::func_like_to_func(func_like), collected_bounds);
257 }
258 // @}
259 
260 /** Impose a boundary condition such that the entire coordinate space is
261  * tiled with copies of the image abutted against each other, but mirror
262  * them such that adjacent edges are the same and then overlap the edges.
263  *
264  * This produces an error if any extent is 1 or less. (TODO: check this.)
265  *
266  * An ImageParam, Buffer<T>, or similar can be passed instead of a Func. If this
267  * is done and no bounds are given, the boundaries will be taken from the
268  * min and extent methods of the passed object.
269  *
270  * (I do not believe there is a direct GL_TEXTURE_WRAP_* equivalent for this.)
271  *
272  * You may pass undefined Exprs for dimensions that you do not wish
273  * to bound.
274  */
275 // @{
276 Func mirror_interior(const Func &source, const Region &bounds);
277 
278 template<typename T>
279 HALIDE_NO_USER_CODE_INLINE Func mirror_interior(const T &func_like, const Region &bounds) {
280  return mirror_interior(Internal::func_like_to_func(func_like), bounds);
281 }
282 
283 template<typename T>
285  Region object_bounds;
286  for (int i = 0; i < func_like.dimensions(); i++) {
287  object_bounds.push_back({Expr(func_like.dim(i).min()), Expr(func_like.dim(i).extent())});
288  }
289 
290  return mirror_interior(Internal::func_like_to_func(func_like), object_bounds);
291 }
292 
293 template<typename T, typename... Bounds,
294  typename std::enable_if<Halide::Internal::all_are_convertible<Expr, Bounds...>::value>::type * = nullptr>
295 HALIDE_ATTRIBUTE_DEPRECATED("Add braces around the bounds like so: {{a, b}, {c, d}}")
296 HALIDE_NO_USER_CODE_INLINE Func mirror_interior(const T &func_like, Bounds &&... bounds) {
297  Region collected_bounds;
298  Internal::collect_region(collected_bounds, std::forward<Bounds>(bounds)...);
299  return mirror_interior(Internal::func_like_to_func(func_like), collected_bounds);
300 }
301 // @}
302 
303 } // namespace BoundaryConditions
304 
305 } // namespace Halide
306 
307 #endif
Halide::Internal::all_are_convertible
Definition: Util.h:210
Halide::BoundaryConditions::constant_exterior
Func constant_exterior(const Func &source, const Tuple &value, const Region &bounds)
Impose a boundary condition such that a given expression is returned everywhere outside the boundary.
Halide::Region
std::vector< Range > Region
A multi-dimensional box.
Definition: Expr.h:343
Halide::BoundaryConditions::Internal::func_like_to_func
const Func & func_like_to_func(const Func &func)
Definition: BoundaryConditions.h:65
Lambda.h
Halide::Range
A single-dimensional span.
Definition: Expr.h:335
Halide
This file defines the class FunctionDAG, which is our representation of a Halide pipeline,...
Definition: AddAtomicMutex.h:21
Halide::lambda
Func lambda(const Expr &e)
Create a zero-dimensional halide function that returns the given expression.
Definition: Lambda.h:16
Halide::LinkageType::Internal
@ Internal
Not visible externally, similar to 'static' linkage in C.
Func.h
HALIDE_ATTRIBUTE_DEPRECATED
#define HALIDE_ATTRIBUTE_DEPRECATED(x)
Definition: HalideRuntime.h:1544
Halide::BoundaryConditions::mirror_interior
Func mirror_interior(const Func &source, const Region &bounds)
Impose a boundary condition such that the entire coordinate space is tiled with copies of the image a...
Halide::BoundaryConditions::repeat_image
Func repeat_image(const Func &source, const Region &bounds)
Impose a boundary condition such that the entire coordinate space is tiled with copies of the image a...
Expr.h
Halide::Func
A halide function.
Definition: Func.h:667
HALIDE_NO_USER_CODE_INLINE
#define HALIDE_NO_USER_CODE_INLINE
Definition: Util.h:44
Halide::BoundaryConditions::mirror_image
Func mirror_image(const Func &source, const Region &bounds)
Impose a boundary condition such that the entire coordinate space is tiled with copies of the image a...
Halide::Expr
A fragment of Halide syntax.
Definition: Expr.h:256
Halide::BoundaryConditions::repeat_edge
Func repeat_edge(const Func &source, const Region &bounds)
Impose a boundary condition such that the nearest edge sample is returned everywhere outside the give...
Halide::BoundaryConditions::Internal::collect_region
HALIDE_NO_USER_CODE_INLINE void collect_region(Region &collected_args, const Expr &a1, const Expr &a2)
Definition: BoundaryConditions.h:53
Halide::Tuple
Create a small array of Exprs for defining and calling functions with multiple outputs.
Definition: Tuple.h:18