This source file includes following definitions.
- ComputeRowStride
- MakeImpulseImage
- MakeBoxImage
- ComputeBoxSum
- TEST
- TEST
- TEST
- TEST
#include <functional>
#include <numeric>
#include <vector>
#include "base/basictypes.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "skia/ext/convolver.h"
#include "skia/ext/recursive_gaussian_convolution.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkPoint.h"
#include "third_party/skia/include/core/SkRect.h"
namespace {
int ComputeRowStride(int width, int channel_count, int stride_slack) {
return width * channel_count + stride_slack;
}
SkIPoint MakeImpulseImage(std::vector<unsigned char>* image,
int width,
int height,
int channel_index,
int channel_count,
int stride_slack) {
const int src_row_stride = ComputeRowStride(
width, channel_count, stride_slack);
const int src_byte_count = src_row_stride * height;
const int signal_x = width / 2;
const int signal_y = height / 2;
image->resize(src_byte_count, 0);
const int non_zero_pixel_index =
signal_y * src_row_stride + signal_x * channel_count + channel_index;
(*image)[non_zero_pixel_index] = 255;
return SkIPoint::Make(signal_x, signal_y);
}
SkIRect MakeBoxImage(std::vector<unsigned char>* image,
int width,
int height,
int channel_index,
int channel_count,
int stride_slack,
int box_width,
int box_height,
unsigned char value) {
const int src_row_stride = ComputeRowStride(
width, channel_count, stride_slack);
const int src_byte_count = src_row_stride * height;
const SkIRect box = SkIRect::MakeXYWH((width - box_width) / 2,
(height - box_height) / 2,
box_width, box_height);
image->resize(src_byte_count, 0);
for (int y = box.top(); y < box.bottom(); ++y) {
for (int x = box.left(); x < box.right(); ++x)
(*image)[y * src_row_stride + x * channel_count + channel_index] = value;
}
return box;
}
int ComputeBoxSum(const std::vector<unsigned char>& image,
const SkIRect& box,
int image_width) {
int sum = 0;
for (int y = box.top(); y < box.bottom(); ++y) {
for (int x = box.left(); x < box.right(); ++x)
sum += image[y * image_width + x];
}
return sum;
}
}
namespace skia {
TEST(RecursiveGaussian, SmoothingMethodComparison) {
static const int kImgWidth = 512;
static const int kImgHeight = 220;
static const int kChannelIndex = 3;
static const int kChannelCount = 3;
static const int kStrideSlack = 22;
std::vector<unsigned char> input;
SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
MakeImpulseImage(
&input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
kStrideSlack);
const int dest_row_stride = kImgWidth;
const int dest_byte_count = dest_row_stride * kImgHeight;
std::vector<unsigned char> intermediate(dest_byte_count);
std::vector<unsigned char> intermediate2(dest_byte_count);
std::vector<unsigned char> control(dest_byte_count);
std::vector<unsigned char> output(dest_byte_count);
const int src_row_stride = ComputeRowStride(
kImgWidth, kChannelCount, kStrideSlack);
const float kernel_sigma = 2.5f;
ConvolutionFilter1D filter;
SetUpGaussianConvolutionKernel(&filter, kernel_sigma, false);
SingleChannelConvolveX1D(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
filter, image_size,
&intermediate[0], dest_row_stride, 0, 1, false);
SingleChannelConvolveY1D(&intermediate[0], dest_row_stride, 0, 1,
filter, image_size,
&control[0], dest_row_stride, 0, 1, false);
RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION);
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&intermediate2[0], dest_row_stride,
0, 1, false);
SingleChannelRecursiveGaussianX(&intermediate2[0], dest_row_stride, 0, 1,
recursive_filter, image_size,
&output[0], dest_row_stride, 0, 1, false);
EXPECT_NEAR(std::accumulate(intermediate.begin(), intermediate.end(), 0),
std::accumulate(intermediate2.begin(), intermediate2.end(), 0),
50);
int difference_count = 0;
std::vector<unsigned char>::const_iterator i1, i2;
for (i1 = control.begin(), i2 = output.begin();
i1 != control.end(); ++i1, ++i2) {
if ((*i1 != 0) != (*i2 != 0))
difference_count++;
}
EXPECT_LE(difference_count, 44);
}
TEST(RecursiveGaussian, SmoothingImpulse) {
static const int kImgWidth = 200;
static const int kImgHeight = 300;
static const int kChannelIndex = 3;
static const int kChannelCount = 3;
static const int kStrideSlack = 22;
std::vector<unsigned char> input;
SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
const SkIPoint centre_point = MakeImpulseImage(
&input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
kStrideSlack);
const int dest_row_stride = kImgWidth;
const int dest_byte_count = dest_row_stride * kImgHeight;
std::vector<unsigned char> intermediate(dest_byte_count);
std::vector<unsigned char> output(dest_byte_count);
const int src_row_stride = ComputeRowStride(
kImgWidth, kChannelCount, kStrideSlack);
const float kernel_sigma = 5.0f;
RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION);
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&intermediate[0], dest_row_stride,
0, 1, false);
SingleChannelRecursiveGaussianX(&intermediate[0], dest_row_stride, 0, 1,
recursive_filter, image_size,
&output[0], dest_row_stride, 0, 1, false);
const int cx = centre_point.x();
const int cy = centre_point.y();
unsigned char value_x = output[dest_row_stride * cy + cx];
unsigned char value_y = value_x;
EXPECT_GT(value_x, 0);
for (int offset = 0;
offset < std::min(kImgWidth, kImgHeight) && (value_y > 0 || value_x > 0);
++offset) {
EXPECT_EQ(output[dest_row_stride * cy + cx - offset],
output[dest_row_stride * cy + cx + offset]);
EXPECT_LE(output[dest_row_stride * cy + cx - offset], value_x);
value_x = output[dest_row_stride * cy + cx - offset];
EXPECT_EQ(output[dest_row_stride * (cy - offset) + cx],
output[dest_row_stride * (cy + offset) + cx]);
EXPECT_LE(output[dest_row_stride * (cy - offset) + cx], value_y);
value_y = output[dest_row_stride * (cy - offset) + cx];
EXPECT_NEAR(value_x, value_y, 1);
}
std::vector<unsigned char> output2(dest_byte_count);
std::transform(input.begin(), input.end(), input.begin(),
std::bind1st(std::minus<unsigned char>(), 255U));
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&intermediate[0], dest_row_stride,
0, 1, false);
SingleChannelRecursiveGaussianX(&intermediate[0], dest_row_stride, 0, 1,
recursive_filter, image_size,
&output2[0], dest_row_stride, 0, 1, false);
std::vector<unsigned char>::const_iterator i1, i2;
int difference_count = 0;
for (i1 = output.begin(), i2 = output2.begin();
i1 != output.end(); ++i1, ++i2) {
if ((*i1 != 0 && *i2 == 255) && ! (*i1 == 0 && *i2 != 255))
++difference_count;
}
EXPECT_LE(difference_count, 8);
}
TEST(RecursiveGaussian, FirstDerivative) {
static const int kImgWidth = 512;
static const int kImgHeight = 1024;
static const int kChannelIndex = 2;
static const int kChannelCount = 4;
static const int kStrideSlack = 22;
static const int kBoxSize = 400;
std::vector<unsigned char> input;
const SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
const SkIRect box = MakeBoxImage(
&input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
kStrideSlack, kBoxSize, kBoxSize, 200);
const int dest_row_stride = kImgWidth;
const int dest_byte_count = dest_row_stride * kImgHeight;
std::vector<unsigned char> output_x(dest_byte_count);
std::vector<unsigned char> output_y(dest_byte_count);
std::vector<unsigned char> output(dest_byte_count);
const int src_row_stride = ComputeRowStride(
kImgWidth, kChannelCount, kStrideSlack);
const float kernel_sigma = 3.0f;
const int spread = 4 * kernel_sigma;
RecursiveFilter recursive_filter(kernel_sigma,
RecursiveFilter::FIRST_DERIVATIVE);
SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_x[0], dest_row_stride,
0, 1, true);
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_y[0], dest_row_stride,
0, 1, true);
std::vector<unsigned char>::const_iterator ix, iy;
std::vector<unsigned char>::iterator target;
for (target = output.begin(), ix = output_x.begin(), iy = output_y.begin();
target < output.end(); ++target, ++ix, ++iy) {
*target = *ix + *iy;
}
SkIRect inflated_rect(box);
inflated_rect.outset(spread, spread);
SkIRect deflated_rect(box);
deflated_rect.inset(spread, spread);
int image_total = ComputeBoxSum(output,
SkIRect::MakeWH(kImgWidth, kImgHeight),
kImgWidth);
int box_inflated = ComputeBoxSum(output, inflated_rect, kImgWidth);
int box_deflated = ComputeBoxSum(output, deflated_rect, kImgWidth);
EXPECT_EQ(box_deflated, 0);
EXPECT_EQ(image_total, box_inflated);
std::transform(input.begin(), input.end(), input.begin(),
std::bind1st(std::minus<unsigned char>(), 255U));
SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_x[0], dest_row_stride,
0, 1, true);
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_y[0], dest_row_stride,
0, 1, true);
for (target = output.begin(), ix = output_x.begin(), iy = output_y.begin();
target < output.end(); ++target, ++ix, ++iy) {
*target = *ix + *iy;
}
image_total = ComputeBoxSum(output,
SkIRect::MakeWH(kImgWidth, kImgHeight),
kImgWidth);
box_inflated = ComputeBoxSum(output, inflated_rect, kImgWidth);
box_deflated = ComputeBoxSum(output, deflated_rect, kImgWidth);
EXPECT_EQ(box_deflated, 0);
EXPECT_EQ(image_total, box_inflated);
}
TEST(RecursiveGaussian, SecondDerivative) {
static const int kImgWidth = 700;
static const int kImgHeight = 500;
static const int kChannelIndex = 0;
static const int kChannelCount = 2;
static const int kStrideSlack = 42;
static const int kBoxSize = 200;
std::vector<unsigned char> input;
SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
const SkIRect box = MakeBoxImage(
&input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
kStrideSlack, kBoxSize, kBoxSize, 200);
const int dest_row_stride = kImgWidth;
const int dest_byte_count = dest_row_stride * kImgHeight;
std::vector<unsigned char> output_x(dest_byte_count);
std::vector<unsigned char> output_y(dest_byte_count);
std::vector<unsigned char> output(dest_byte_count);
const int src_row_stride = ComputeRowStride(
kImgWidth, kChannelCount, kStrideSlack);
const float kernel_sigma = 5.0f;
const int spread = 8 * kernel_sigma;
RecursiveFilter recursive_filter(kernel_sigma,
RecursiveFilter::SECOND_DERIVATIVE);
SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_x[0], dest_row_stride,
0, 1, true);
SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
kChannelIndex, kChannelCount,
recursive_filter, image_size,
&output_y[0], dest_row_stride,
0, 1, true);
std::vector<unsigned char>::const_iterator ix, iy;
std::vector<unsigned char>::iterator target;
for (target = output.begin(),ix = output_x.begin(), iy = output_y.begin();
target < output.end(); ++target, ++ix, ++iy) {
*target = *ix + *iy;
}
int image_total = ComputeBoxSum(output,
SkIRect::MakeWH(kImgWidth, kImgHeight),
kImgWidth);
int box_inflated = ComputeBoxSum(output,
SkIRect::MakeLTRB(box.left() - spread,
box.top() - spread,
box.right() + spread,
box.bottom() + spread),
kImgWidth);
int box_deflated = ComputeBoxSum(output,
SkIRect::MakeLTRB(box.left() + spread,
box.top() + spread,
box.right() - spread,
box.bottom() - spread),
kImgWidth);
EXPECT_EQ(box_deflated, 0);
EXPECT_EQ(image_total, box_inflated);
}
}