• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Functional tests for morphological filtering operations."""
16
17import numpy as np
18
19from tensorflow.python.framework import config
20from tensorflow.python.framework import constant_op
21from tensorflow.python.framework import errors_impl
22from tensorflow.python.framework import test_util
23from tensorflow.python.ops import gradient_checker_v2
24from tensorflow.python.ops import nn_ops
25import tensorflow.python.ops.nn_grad  # pylint: disable=unused-import
26from tensorflow.python.platform import test
27
28
29class DilationTest(test.TestCase):
30
31  def _VerifyValues(self, image, kernel, strides, rates, padding, out, use_gpu):
32    """Verifies the output values of the dilation function.
33
34    Args:
35      image: Input tensor with shape: [batch, in_height, in_width, channels].
36      kernel: Filter tensor with shape: [filter_height, filter_width, channels].
37      strides: Output strides, specified as [stride_height, stride_width].
38      rates: Atrous rates, specified as [rate_height, rate_width].
39      padding: Padding type.
40      out: Expected output.
41      use_gpu: Whether we are running on GPU.
42    """
43    strides = [1] + strides + [1]
44    rates = [1] + rates + [1]
45
46    with self.cached_session(use_gpu=use_gpu):
47      out_tensor = nn_ops.dilation2d(
48          constant_op.constant(image),
49          constant_op.constant(kernel),
50          strides=strides,
51          rates=rates,
52          padding=padding,
53          name="dilation2d")
54      self.assertAllClose(out, self.evaluate(out_tensor))
55
56  def _testDilationValidPadding(self, use_gpu):
57    # [1, 2, 2, 1]
58    image = [[[[.1], [.2]], [[.3], [.4]]]]
59    # [2, 2, 1]
60    kernel = [[[.4], [.3]], [[.1], [.0]]]
61    # [1, 1, 1, 1]
62    out = [[[[.5]]]]
63    self._VerifyValues(
64        image,
65        kernel,
66        strides=[1, 1],
67        rates=[1, 1],
68        padding="VALID",
69        out=out,
70        use_gpu=use_gpu)
71
72  def _testDilationSamePadding(self, use_gpu):
73    # [1, 2, 2, 1]
74    image = [[[[.1], [.2]], [[.3], [.4]]]]
75    # [2, 2, 1]
76    kernel = [[[.4], [.3]], [[.1], [.0]]]
77    # [1, 2, 2, 1]
78    out = [[[[.5], [.6]], [[.7], [.8]]]]
79    self._VerifyValues(
80        image,
81        kernel,
82        strides=[1, 1],
83        rates=[1, 1],
84        padding="SAME",
85        out=out,
86        use_gpu=use_gpu)
87
88  def _testDilationSamePaddingDepth(self, use_gpu):
89    # [1, 2, 2, 3]
90    image = [[[[.1, .2, .0], [.2, .3, .1]], [[.3, .4, .2], [.4, .5, .3]]]]
91    # [2, 2, 3]
92    kernel = [[[.4, .5, .3], [.3, .4, .2]], [[.1, .2, .0], [.0, .1, -.1]]]
93    # [1, 2, 2, 3]
94    out = [[[[.5, .7, .3], [.6, .8, .4]], [[.7, .9, .5], [.8, 1., .6]]]]
95    self._VerifyValues(
96        image,
97        kernel,
98        strides=[1, 1],
99        rates=[1, 1],
100        padding="SAME",
101        out=out,
102        use_gpu=use_gpu)
103
104  def _testDilationSamePaddingBatch(self, use_gpu):
105    # [2, 2, 2, 1]
106    image = [[[[.1], [.2]], [[.3], [.4]]], [[[.2], [.3]], [[.4], [.5]]]]
107    # [2, 2, 1]
108    kernel = [[[.4], [.3]], [[.1], [.0]]]
109    # [2, 2, 2, 1]
110    out = [[[[.5], [.6]], [[.7], [.8]]], [[[.6], [.7]], [[.8], [.9]]]]
111    self._VerifyValues(
112        image,
113        kernel,
114        strides=[1, 1],
115        rates=[1, 1],
116        padding="SAME",
117        out=out,
118        use_gpu=use_gpu)
119
120  def _testDilationValidPaddingNonSquareWindow(self, use_gpu):
121    # [1, 2, 2, 1]
122    image = [[[[.1], [.2]], [[.3], [.4]]]]
123    # [1, 2, 1]
124    kernel = [[[.4], [.3]]]
125    # [1, 2, 1, 1]
126    out = [[[[.5]], [[.7]]]]
127    self._VerifyValues(
128        image,
129        kernel,
130        strides=[1, 1],
131        rates=[1, 1],
132        padding="VALID",
133        out=out,
134        use_gpu=use_gpu)
135
136  def _testDilationSamePaddingRate(self, use_gpu):
137    # [1, 3, 3, 1]
138    image = [[[[.1], [.2], [.3]], [[.4], [.5], [.6]], [[.7], [.8], [.9]]]]
139    # [2, 2, 1]
140    kernel = [[[.4], [.3]], [[.1], [.2]]]
141    # Because rate = 2.0, the effective kernel is [3, 3, 1]:
142    # kernel_eff = [[[.4], [.0], [.3]],
143    #               [[.0], [.0], [.0]],
144    #               [[.1], [.0], [.2]]]
145    # [1, 3, 3, 1]
146    out = [[[[.7], [.8], [.6]], [[1.0], [1.1], [.9]], [[.8], [.9], [.9]]]]
147    self._VerifyValues(
148        image,
149        kernel,
150        strides=[1, 1],
151        rates=[2, 2],
152        padding="SAME",
153        out=out,
154        use_gpu=use_gpu)
155
156  def _testDilationValidPaddingUnevenStride(self, use_gpu):
157    # [1, 3, 3, 1]
158    image = [[[[.1], [.2], [.3], [.4]], [[.5], [.6], [.7], [.8]],
159              [[.9], [1.0], [1.1], [1.2]]]]
160    # [2, 2, 1]
161    kernel = [[[.4], [.3]], [[.1], [.2]]]
162    # [1, 2, 2, 1]
163    out = [[[[.8], [1.0]], [[1.2], [1.4]]]]
164    self._VerifyValues(
165        image,
166        kernel,
167        strides=[1, 2],
168        rates=[1, 1],
169        padding="VALID",
170        out=out,
171        use_gpu=use_gpu)
172
173  def testDilation(self):
174    for use_gpu in True, False:
175      self._testDilationValidPadding(use_gpu)
176      self._testDilationSamePadding(use_gpu)
177      self._testDilationSamePaddingDepth(use_gpu)
178      self._testDilationSamePaddingBatch(use_gpu)
179      self._testDilationValidPaddingNonSquareWindow(use_gpu)
180      self._testDilationSamePaddingRate(use_gpu)
181      self._testDilationValidPaddingUnevenStride(use_gpu)
182
183  def _ConstructAndTestGradient(self, image_shape, kernel_shape, strides, rates,
184                                padding, use_gpu):
185    """Verifies the gradients of the dilation function.
186
187    Args:
188      image_shape: Input shape, [batch, in_height, in_width, channels].
189      kernel_shape: Filter shape, [filter_height, filter_width, channels].
190      strides: Output strides, specified as [stride_height, stride_width].
191      rates: Atrous rates, specified as [rate_height, rate_width].
192      padding: Padding type.
193      use_gpu: Whether we are running on GPU.
194    """
195    assert image_shape[3] == kernel_shape[2]
196
197    np.random.seed(1)  # Make it reproducible.
198    image = np.random.random_sample(image_shape).astype(np.float32)
199    kernel = np.random.random_sample(kernel_shape).astype(np.float32)
200
201    strides = [1] + strides + [1]
202    rates = [1] + rates + [1]
203
204    image_tensor = constant_op.constant(image, shape=image_shape, name="input")
205    kernel_tensor = constant_op.constant(
206        kernel, shape=kernel_shape, name="filter")
207
208    def compute_dilation2d(image_tensor, kernel_tensor):
209      return nn_ops.dilation2d(
210          image_tensor,
211          kernel_tensor,
212          strides=strides,
213          rates=rates,
214          padding=padding,
215          name="dilation2d")
216
217    with test_util.device(use_gpu=use_gpu):
218      with self.cached_session():
219        # Small delta is necessary for argmax to remain the same.
220        err1 = gradient_checker_v2.max_error(
221            *gradient_checker_v2.compute_gradient(
222                lambda x: compute_dilation2d(x, kernel_tensor), [image_tensor]))
223        err2 = gradient_checker_v2.max_error(
224            *gradient_checker_v2.compute_gradient(
225                lambda x: compute_dilation2d(image_tensor, x), [kernel_tensor]))
226        err = max(err1, err2)
227
228    print("Dilation gradient error = %f" % err)
229    self.assertLess(err, 1e-4)
230
231  def _testDilationGradValidPadding_1x1x1(self, use_gpu):
232    self._ConstructAndTestGradient(
233        image_shape=[1, 3, 3, 1],
234        kernel_shape=[1, 1, 1],
235        strides=[1, 1],
236        rates=[1, 1],
237        padding="VALID",
238        use_gpu=use_gpu)
239
240  def _testDilationGradDeterminismError(self, use_gpu):
241    if use_gpu and test.is_gpu_available(cuda_only=True):
242      try:
243        config.enable_op_determinism()
244        with self.assertRaisesRegexp(
245            errors_impl.UnimplementedError, "Determinism is not yet supported "
246            "for Dilation2DBackpropInput."):
247          self._ConstructAndTestGradient(
248              image_shape=[1, 3, 3, 1],
249              kernel_shape=[1, 1, 1],
250              strides=[1, 1],
251              rates=[1, 1],
252              padding="VALID",
253              use_gpu=use_gpu)
254      finally:
255        config.disable_op_determinism()
256    else:
257      try:
258        config.enable_op_determinism()
259        self._ConstructAndTestGradient(
260            image_shape=[1, 3, 3, 1],
261            kernel_shape=[1, 1, 1],
262            strides=[1, 1],
263            rates=[1, 1],
264            padding="VALID",
265            use_gpu=use_gpu)
266      finally:
267        config.disable_op_determinism()
268
269  def _testDilationGradSamePadding_1x1x1(self, use_gpu):
270    self._ConstructAndTestGradient(
271        image_shape=[1, 3, 3, 1],
272        kernel_shape=[1, 1, 1],
273        strides=[1, 1],
274        rates=[1, 1],
275        padding="SAME",
276        use_gpu=use_gpu)
277
278  def _testDilationGradSamePadding_1x1x2(self, use_gpu):
279    self._ConstructAndTestGradient(
280        image_shape=[1, 3, 3, 2],
281        kernel_shape=[1, 1, 2],
282        strides=[1, 1],
283        rates=[1, 1],
284        padding="SAME",
285        use_gpu=use_gpu)
286
287  def _testDilationGradValidPadding_2x2x1(self, use_gpu):
288    self._ConstructAndTestGradient(
289        image_shape=[1, 3, 3, 1],
290        kernel_shape=[2, 2, 1],
291        strides=[1, 1],
292        rates=[1, 1],
293        padding="VALID",
294        use_gpu=use_gpu)
295
296  def _testDilationGradSamePadding_2x2x1(self, use_gpu):
297    self._ConstructAndTestGradient(
298        image_shape=[1, 3, 3, 1],
299        kernel_shape=[2, 2, 1],
300        strides=[1, 1],
301        rates=[1, 1],
302        padding="SAME",
303        use_gpu=use_gpu)
304
305  def _testDilationGradSamePaddingBatch_2x2x1(self, use_gpu):
306    self._ConstructAndTestGradient(
307        image_shape=[4, 3, 3, 1],
308        kernel_shape=[2, 2, 1],
309        strides=[1, 1],
310        rates=[1, 1],
311        padding="SAME",
312        use_gpu=use_gpu)
313
314  def _testDilationGradSamePadding_2x2x4(self, use_gpu):
315    self._ConstructAndTestGradient(
316        image_shape=[1, 3, 3, 4],
317        kernel_shape=[2, 2, 4],
318        strides=[1, 1],
319        rates=[1, 1],
320        padding="SAME",
321        use_gpu=use_gpu)
322
323  def testDilationGrad(self):
324    for use_gpu in True, False:
325      self._testDilationGradDeterminismError(use_gpu)
326      self._testDilationGradValidPadding_1x1x1(use_gpu)
327      self._testDilationGradSamePadding_1x1x1(use_gpu)
328      self._testDilationGradSamePadding_1x1x2(use_gpu)
329      self._testDilationGradValidPadding_2x2x1(use_gpu)
330      self._testDilationGradSamePadding_2x2x1(use_gpu)
331      self._testDilationGradSamePaddingBatch_2x2x1(use_gpu)
332      self._testDilationGradSamePadding_2x2x4(use_gpu)
333
334
335class ErosionTest(test.TestCase):
336
337  def _VerifyValues(self, image, kernel, strides, rates, padding, out, use_gpu):
338    """Verifies the output values of the erosion function.
339
340    Args:
341      image: Input tensor with shape: [batch, in_height, in_width, channels].
342      kernel: Filter tensor with shape: [filter_height, filter_width, channels].
343      strides: Output strides, specified as [stride_height, stride_width].
344      rates: Atrous rates, specified as [rate_height, rate_width].
345      padding: Padding type.
346      out: Expected output.
347      use_gpu: Whether we are running on GPU.
348    """
349    strides = [1] + strides + [1]
350    rates = [1] + rates + [1]
351
352    with self.cached_session(use_gpu=use_gpu):
353      out_tensor = nn_ops.erosion2d(
354          constant_op.constant(image),
355          constant_op.constant(kernel),
356          strides=strides,
357          rates=rates,
358          padding=padding,
359          name="erosion2d")
360      self.assertAllClose(out, self.evaluate(out_tensor))
361
362  def _testErosionValidPadding(self, use_gpu):
363    # [1, 2, 2, 1]
364    image = [[[[.1], [.2]], [[.3], [.4]]]]
365    # [2, 2, 1]
366    kernel = [[[.4], [.3]], [[.1], [.0]]]
367    # [1, 1, 1, 1]
368    out = [[[[.0]]]]
369    self._VerifyValues(
370        image,
371        kernel,
372        strides=[1, 1],
373        rates=[1, 1],
374        padding="VALID",
375        out=out,
376        use_gpu=use_gpu)
377
378  def _testErosionSamePadding(self, use_gpu):
379    # [1, 2, 2, 1]
380    image = [[[[.1], [.2]], [[.3], [.4]]]]
381    # [2, 2, 1]
382    kernel = [[[.4], [.3]], [[.1], [.0]]]
383    # [1, 2, 2, 1]
384    out = [[[[.0], [.1]], [[.3], [.4]]]]
385    self._VerifyValues(
386        image,
387        kernel,
388        strides=[1, 1],
389        rates=[1, 1],
390        padding="SAME",
391        out=out,
392        use_gpu=use_gpu)
393
394  def _testErosionSamePaddingDepth(self, use_gpu):
395    # [1, 2, 2, 3]
396    image = [[[[.1, .2, .0], [.2, .3, .1]], [[.3, .4, .2], [.4, .5, .3]]]]
397    # [2, 2, 3]
398    kernel = [[[.4, .5, .3], [.3, .4, .2]], [[.1, .2, .0], [.0, .1, -.1]]]
399    # [1, 2, 2, 3]
400    out = [[[[.0, .0, .0], [.1, .1, .1]], [[.3, .3, .3], [.4, .4, .4]]]]
401    self._VerifyValues(
402        image,
403        kernel,
404        strides=[1, 1],
405        rates=[1, 1],
406        padding="SAME",
407        out=out,
408        use_gpu=use_gpu)
409
410  def _testErosionSamePaddingBatch(self, use_gpu):
411    # [2, 2, 2, 1]
412    image = [[[[.1], [.2]], [[.3], [.4]]], [[[.2], [.3]], [[.4], [.5]]]]
413    # [2, 2, 1]
414    kernel = [[[.4], [.3]], [[.1], [.0]]]
415    # [2, 2, 2, 1]
416    out = [[[[.0], [.1]], [[.3], [.4]]], [[[.1], [.2]], [[.4], [.5]]]]
417    self._VerifyValues(
418        image,
419        kernel,
420        strides=[1, 1],
421        rates=[1, 1],
422        padding="SAME",
423        out=out,
424        use_gpu=use_gpu)
425
426  def _testErosionValidPaddingNonSquareWindow(self, use_gpu):
427    # [1, 2, 2, 1]
428    image = [[[[.1], [.2]], [[.3], [.4]]]]
429    # [1, 2, 1]
430    kernel = [[[.4], [.3]]]
431    # [1, 2, 1, 1]
432    out = [[[[-.2]], [[.0]]]]
433    self._VerifyValues(
434        image,
435        kernel,
436        strides=[1, 1],
437        rates=[1, 1],
438        padding="VALID",
439        out=out,
440        use_gpu=use_gpu)
441
442  def _testErosionSamePaddingRate(self, use_gpu):
443    # [1, 3, 3, 1]
444    image = [[[[.1], [.2], [.3]], [[.4], [.5], [.6]], [[.7], [.8], [.9]]]]
445    # [2, 2, 1]
446    kernel = [[[.4], [.3]], [[.1], [.2]]]
447    # Because rate = 2.0, the effective kernel is [3, 3, 1]:
448    # kernel_eff = [[[.4], [.0], [.3]],
449    #               [[.0], [.0], [.0]],
450    #               [[.1], [.0], [.2]]]
451    # [1, 3, 3, 1]
452    out = [[[[.1], [.1], [.2]], [[0.1], [-.1], [.0]], [[.4], [.2], [.3]]]]
453    self._VerifyValues(
454        image,
455        kernel,
456        strides=[1, 1],
457        rates=[2, 2],
458        padding="SAME",
459        out=out,
460        use_gpu=use_gpu)
461
462  def _testErosionValidPaddingUnevenStride(self, use_gpu):
463    # [1, 3, 3, 1]
464    image = [[[[.1], [.2], [.3], [.4]], [[.5], [.6], [.7], [.8]],
465              [[.9], [1.0], [1.1], [1.2]]]]
466    # [2, 2, 1]
467    kernel = [[[.4], [.3]], [[.1], [.2]]]
468    # [1, 2, 2, 1]
469    out = [[[[-.1], [.1]], [[.3], [.5]]]]
470    self._VerifyValues(
471        image,
472        kernel,
473        strides=[1, 2],
474        rates=[1, 1],
475        padding="VALID",
476        out=out,
477        use_gpu=use_gpu)
478
479  def testErosion(self):
480    for use_gpu in True, False:
481      self._testErosionValidPadding(use_gpu)
482      self._testErosionSamePadding(use_gpu)
483      self._testErosionSamePaddingDepth(use_gpu)
484      self._testErosionSamePaddingBatch(use_gpu)
485      self._testErosionValidPaddingNonSquareWindow(use_gpu)
486      self._testErosionSamePaddingRate(use_gpu)
487      self._testErosionValidPaddingUnevenStride(use_gpu)
488
489  def _ConstructAndTestGradient(self, image_shape, kernel_shape, strides, rates,
490                                padding, use_gpu):
491    """Verifies the gradients of the erosion function.
492
493    Args:
494      image_shape: Input shape, [batch, in_height, in_width, channels].
495      kernel_shape: Filter shape, [filter_height, filter_width, channels].
496      strides: Output strides, specified as [stride_height, stride_width].
497      rates: Atrous rates, specified as [rate_height, rate_width].
498      padding: Padding type.
499      use_gpu: Whether we are running on GPU.
500    """
501    assert image_shape[3] == kernel_shape[2]
502
503    np.random.seed(1)  # Make it reproducible.
504    image = np.random.random_sample(image_shape).astype(np.float32)
505    kernel = np.random.random_sample(kernel_shape).astype(np.float32)
506
507    strides = [1] + strides + [1]
508    rates = [1] + rates + [1]
509
510    image_tensor = constant_op.constant(image, shape=image_shape, name="input")
511    kernel_tensor = constant_op.constant(
512        kernel, shape=kernel_shape, name="filter")
513
514    def compute_erosion2d(image_tensor, kernel_tensor):
515      return nn_ops.erosion2d(
516          image_tensor,
517          kernel_tensor,
518          strides=strides,
519          rates=rates,
520          padding=padding,
521          name="erosion2d")
522
523    with test_util.device(use_gpu=use_gpu):
524      with self.cached_session():
525        # Small delta is necessary for argmax to remain the same.
526        err1 = gradient_checker_v2.max_error(
527            *gradient_checker_v2.compute_gradient(
528                lambda x: compute_erosion2d(x, kernel_tensor), [image_tensor]))
529        err2 = gradient_checker_v2.max_error(
530            *gradient_checker_v2.compute_gradient(
531                lambda x: compute_erosion2d(image_tensor, x), [kernel_tensor]))
532        err = max(err1, err2)
533
534    print("Erosion gradient error = %f" % err)
535    self.assertLess(err, 1e-4)
536
537  def _testErosionGradValidPadding_1x1x1(self, use_gpu):
538    self._ConstructAndTestGradient(
539        image_shape=[1, 3, 3, 1],
540        kernel_shape=[1, 1, 1],
541        strides=[1, 1],
542        rates=[1, 1],
543        padding="VALID",
544        use_gpu=use_gpu)
545
546  def _testErosionGradSamePadding_1x1x1(self, use_gpu):
547    self._ConstructAndTestGradient(
548        image_shape=[1, 3, 3, 1],
549        kernel_shape=[1, 1, 1],
550        strides=[1, 1],
551        rates=[1, 1],
552        padding="SAME",
553        use_gpu=use_gpu)
554
555  def _testErosionGradSamePadding_1x1x2(self, use_gpu):
556    self._ConstructAndTestGradient(
557        image_shape=[1, 3, 3, 2],
558        kernel_shape=[1, 1, 2],
559        strides=[1, 1],
560        rates=[1, 1],
561        padding="SAME",
562        use_gpu=use_gpu)
563
564  def _testErosionGradValidPadding_2x2x1(self, use_gpu):
565    self._ConstructAndTestGradient(
566        image_shape=[1, 3, 3, 1],
567        kernel_shape=[2, 2, 1],
568        strides=[1, 1],
569        rates=[1, 1],
570        padding="VALID",
571        use_gpu=use_gpu)
572
573  def _testErosionGradSamePadding_2x2x1(self, use_gpu):
574    self._ConstructAndTestGradient(
575        image_shape=[1, 3, 3, 1],
576        kernel_shape=[2, 2, 1],
577        strides=[1, 1],
578        rates=[1, 1],
579        padding="SAME",
580        use_gpu=use_gpu)
581
582  def _testErosionGradSamePaddingBatch_2x2x1(self, use_gpu):
583    self._ConstructAndTestGradient(
584        image_shape=[4, 3, 3, 1],
585        kernel_shape=[2, 2, 1],
586        strides=[1, 1],
587        rates=[1, 1],
588        padding="SAME",
589        use_gpu=use_gpu)
590
591  def _testErosionGradSamePadding_2x2x4(self, use_gpu):
592    self._ConstructAndTestGradient(
593        image_shape=[1, 3, 3, 4],
594        kernel_shape=[2, 2, 4],
595        strides=[1, 1],
596        rates=[1, 1],
597        padding="SAME",
598        use_gpu=use_gpu)
599
600  def testErosionGrad(self):
601    for use_gpu in True, False:
602      self._testErosionGradValidPadding_1x1x1(use_gpu)
603      self._testErosionGradSamePadding_1x1x1(use_gpu)
604      self._testErosionGradSamePadding_1x1x2(use_gpu)
605      self._testErosionGradValidPadding_2x2x1(use_gpu)
606      self._testErosionGradSamePadding_2x2x1(use_gpu)
607      self._testErosionGradSamePaddingBatch_2x2x1(use_gpu)
608      self._testErosionGradSamePadding_2x2x4(use_gpu)
609
610
611if __name__ == "__main__":
612  test.main()
613