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