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