OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <string.h> | 5 #include <string.h> |
6 #include <time.h> | 6 #include <time.h> |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 EXPECT_EQ(r1[x], r2[x]); | 317 EXPECT_EQ(r1[x], r2[x]); |
318 } | 318 } |
319 r1 += result_c.rowBytes(); | 319 r1 += result_c.rowBytes(); |
320 r2 += result_sse.rowBytes(); | 320 r2 += result_sse.rowBytes(); |
321 } | 321 } |
322 } | 322 } |
323 } | 323 } |
324 } | 324 } |
325 } | 325 } |
326 | 326 |
| 327 TEST(Convolver, SeparableSingleConvolution) { |
| 328 static const int kImgWidth = 1024; |
| 329 static const int kImgHeight = 1024; |
| 330 static const int kChannelCount = 3; |
| 331 static const int kStrideSlack = 22; |
| 332 ConvolutionFilter1D filter; |
| 333 const float box[5] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f }; |
| 334 filter.AddFilter(0, box, 5); |
| 335 |
| 336 // Allocate a source image and set to 0. |
| 337 const int src_row_stride = kImgWidth * kChannelCount + kStrideSlack; |
| 338 int src_byte_count = src_row_stride * kImgHeight; |
| 339 std::vector<unsigned char> input; |
| 340 const int signal_x = kImgWidth / 2; |
| 341 const int signal_y = kImgHeight / 2; |
| 342 input.resize(src_byte_count, 0); |
| 343 // The image has a single impulse pixel in channel 1, smack in the middle. |
| 344 const int non_zero_pixel_index = |
| 345 signal_y * src_row_stride + signal_x * kChannelCount + 1; |
| 346 input[non_zero_pixel_index] = 255; |
| 347 |
| 348 // Destination will be a single channel image with stide matching width. |
| 349 const int dest_row_stride = kImgWidth; |
| 350 const int dest_byte_count = dest_row_stride * kImgHeight; |
| 351 std::vector<unsigned char> output; |
| 352 output.resize(dest_byte_count); |
| 353 |
| 354 // Apply convolution in X. |
| 355 SingleChannelConvolveX1D(&input[0], src_row_stride, 1, kChannelCount, |
| 356 filter, SkISize::Make(kImgWidth, kImgHeight), |
| 357 &output[0], dest_row_stride, 0, 1, false); |
| 358 for (int x = signal_x - 2; x <= signal_x + 2; ++x) |
| 359 EXPECT_GT(output[signal_y * dest_row_stride + x], 0); |
| 360 |
| 361 EXPECT_EQ(output[signal_y * dest_row_stride + signal_x - 3], 0); |
| 362 EXPECT_EQ(output[signal_y * dest_row_stride + signal_x + 3], 0); |
| 363 |
| 364 // Apply convolution in Y. |
| 365 SingleChannelConvolveY1D(&input[0], src_row_stride, 1, kChannelCount, |
| 366 filter, SkISize::Make(kImgWidth, kImgHeight), |
| 367 &output[0], dest_row_stride, 0, 1, false); |
| 368 for (int y = signal_y - 2; y <= signal_y + 2; ++y) |
| 369 EXPECT_GT(output[y * dest_row_stride + signal_x], 0); |
| 370 |
| 371 EXPECT_EQ(output[(signal_y - 3) * dest_row_stride + signal_x], 0); |
| 372 EXPECT_EQ(output[(signal_y + 3) * dest_row_stride + signal_x], 0); |
| 373 |
| 374 EXPECT_EQ(output[signal_y * dest_row_stride + signal_x - 1], 0); |
| 375 EXPECT_EQ(output[signal_y * dest_row_stride + signal_x + 1], 0); |
| 376 |
| 377 // The main point of calling this is to invoke the routine on input without |
| 378 // padding. |
| 379 std::vector<unsigned char> output2; |
| 380 output2.resize(dest_byte_count); |
| 381 SingleChannelConvolveX1D(&output[0], dest_row_stride, 0, 1, |
| 382 filter, SkISize::Make(kImgWidth, kImgHeight), |
| 383 &output2[0], dest_row_stride, 0, 1, false); |
| 384 // This should be a result of 2D convolution. |
| 385 for (int x = signal_x - 2; x <= signal_x + 2; ++x) { |
| 386 for (int y = signal_y - 2; y <= signal_y + 2; ++y) |
| 387 EXPECT_GT(output2[y * dest_row_stride + x], 0); |
| 388 } |
| 389 EXPECT_EQ(output2[0], 0); |
| 390 EXPECT_EQ(output2[dest_row_stride - 1], 0); |
| 391 EXPECT_EQ(output2[dest_byte_count - 1], 0); |
| 392 } |
| 393 |
| 394 TEST(Convolver, SeparableSingleConvolutionEdges) { |
| 395 // The purpose of this test is to check if the implementation treats correctly |
| 396 // edges of the image. |
| 397 static const int kImgWidth = 600; |
| 398 static const int kImgHeight = 800; |
| 399 static const int kChannelCount = 3; |
| 400 static const int kStrideSlack = 22; |
| 401 static const int kChannel = 1; |
| 402 ConvolutionFilter1D filter; |
| 403 const float box[5] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f }; |
| 404 filter.AddFilter(0, box, 5); |
| 405 |
| 406 // Allocate a source image and set to 0. |
| 407 int src_row_stride = kImgWidth * kChannelCount + kStrideSlack; |
| 408 int src_byte_count = src_row_stride * kImgHeight; |
| 409 std::vector<unsigned char> input(src_byte_count); |
| 410 |
| 411 // Draw a frame around the image. |
| 412 for (int i = 0; i < src_byte_count; ++i) { |
| 413 int row = i / src_row_stride; |
| 414 int col = i % src_row_stride / kChannelCount; |
| 415 int channel = i % src_row_stride % kChannelCount; |
| 416 if (channel != kChannel || col > kImgWidth) { |
| 417 input[i] = 255; |
| 418 } else if (row == 0 || col == 0 || |
| 419 col == kImgWidth - 1 || row == kImgHeight - 1) { |
| 420 input[i] = 100; |
| 421 } else if (row == 1 || col == 1 || |
| 422 col == kImgWidth - 2 || row == kImgHeight - 2) { |
| 423 input[i] = 200; |
| 424 } else { |
| 425 input[i] = 0; |
| 426 } |
| 427 } |
| 428 |
| 429 // Destination will be a single channel image with stide matching width. |
| 430 int dest_row_stride = kImgWidth; |
| 431 int dest_byte_count = dest_row_stride * kImgHeight; |
| 432 std::vector<unsigned char> output; |
| 433 output.resize(dest_byte_count); |
| 434 |
| 435 // Apply convolution in X. |
| 436 SingleChannelConvolveX1D(&input[0], src_row_stride, 1, kChannelCount, |
| 437 filter, SkISize::Make(kImgWidth, kImgHeight), |
| 438 &output[0], dest_row_stride, 0, 1, false); |
| 439 |
| 440 // Sadly, comparison is not as simple as retaining all values. |
| 441 int invalid_values = 0; |
| 442 const unsigned char first_value = output[0]; |
| 443 EXPECT_TRUE(std::abs(100 - first_value) <= 1); |
| 444 for (int i = 0; i < dest_row_stride; ++i) { |
| 445 if (output[i] != first_value) |
| 446 ++invalid_values; |
| 447 } |
| 448 EXPECT_EQ(0, invalid_values); |
| 449 |
| 450 int test_row = 22; |
| 451 EXPECT_NEAR(output[test_row * dest_row_stride], 100, 1); |
| 452 EXPECT_NEAR(output[test_row * dest_row_stride + 1], 80, 1); |
| 453 EXPECT_NEAR(output[test_row * dest_row_stride + 2], 60, 1); |
| 454 EXPECT_NEAR(output[test_row * dest_row_stride + 3], 40, 1); |
| 455 EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 1], 100, 1); |
| 456 EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 2], 80, 1); |
| 457 EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 3], 60, 1); |
| 458 EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 4], 40, 1); |
| 459 |
| 460 SingleChannelConvolveY1D(&input[0], src_row_stride, 1, kChannelCount, |
| 461 filter, SkISize::Make(kImgWidth, kImgHeight), |
| 462 &output[0], dest_row_stride, 0, 1, false); |
| 463 |
| 464 int test_column = 42; |
| 465 EXPECT_NEAR(output[test_column], 100, 1); |
| 466 EXPECT_NEAR(output[test_column + dest_row_stride], 80, 1); |
| 467 EXPECT_NEAR(output[test_column + dest_row_stride * 2], 60, 1); |
| 468 EXPECT_NEAR(output[test_column + dest_row_stride * 3], 40, 1); |
| 469 |
| 470 EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 1)], 100, 1); |
| 471 EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 2)], 80, 1); |
| 472 EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 3)], 60, 1); |
| 473 EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 4)], 40, 1); |
| 474 } |
| 475 |
327 } // namespace skia | 476 } // namespace skia |
OLD | NEW |