Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#include <cstdint>
#include <tuple>
#include <vector>

#include "task/include/task.hpp"

namespace pikhotskiy_r_vertical_gauss_filter {

struct Matrix {
int width = 0;
int height = 0;
std::vector<std::uint8_t> data;
};

using InType = Matrix;
using OutType = Matrix;
using TestType = std::tuple<InType, OutType>;
using BaseTask = ppc::task::Task<InType, OutType>;

} // namespace pikhotskiy_r_vertical_gauss_filter
9 changes: 9 additions & 0 deletions tasks/pikhotskiy_r_vertical_gauss_filter/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"student": {
"first_name": "Роман",
"group_number": "3823Б1ФИ1",
"last_name": "Пихотский",
"middle_name": "Владимирович",
"task_number": "1"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once

#include <cstdint>
#include <vector>

#include "pikhotskiy_r_vertical_gauss_filter/common/include/common.hpp"
#include "task/include/task.hpp"

namespace pikhotskiy_r_vertical_gauss_filter {

class PikhotskiyRVerticalGaussFilterSEQ : public BaseTask {
public:
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
return ppc::task::TypeOfTask::kSEQ;
}
explicit PikhotskiyRVerticalGaussFilterSEQ(const InType &in);

private:
bool ValidationImpl() override;
bool PreProcessingImpl() override;
bool RunImpl() override;
bool PostProcessingImpl() override;
void RunVerticalPassForStripe(int x_begin, int x_end);
void RunHorizontalPassForStripe(int x_begin, int x_end);

int width_ = 0;
int height_ = 0;
int stripe_width_ = 1;
std::vector<std::uint8_t> source_;
std::vector<int> vertical_buffer_;
std::vector<std::uint8_t> result_buffer_;
};

} // namespace pikhotskiy_r_vertical_gauss_filter
121 changes: 121 additions & 0 deletions tasks/pikhotskiy_r_vertical_gauss_filter/seq/src/ops_seq.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#include "pikhotskiy_r_vertical_gauss_filter/seq/include/ops_seq.hpp"

#include <algorithm>
#include <cstddef>
#include <cstdint>

#include "pikhotskiy_r_vertical_gauss_filter/common/include/common.hpp"

namespace pikhotskiy_r_vertical_gauss_filter {

namespace {
constexpr int kKernelNorm = 16;
constexpr int kStripeDivider = 8;

constexpr int ClampIndex(int value, int upper_bound) noexcept {
if (upper_bound <= 0) {
return 0;
}
if (value < 0) {
return 0;
}
if (value >= upper_bound) {
return upper_bound - 1;
}
return value;
}

constexpr std::size_t ToLinearIndex(int x_pos, int y_pos, int width) noexcept {
return (static_cast<std::size_t>(y_pos) * static_cast<std::size_t>(width)) + static_cast<std::size_t>(x_pos);
}

std::uint8_t NormalizeAndRoundUp(int sum) {
return static_cast<std::uint8_t>((sum + (kKernelNorm - 1)) / kKernelNorm);
}
} // namespace

PikhotskiyRVerticalGaussFilterSEQ::PikhotskiyRVerticalGaussFilterSEQ(const InType &in) {
SetTypeOfTask(GetStaticTypeOfTask());
GetInput() = in;
GetOutput() = OutType{};
}

bool PikhotskiyRVerticalGaussFilterSEQ::ValidationImpl() {
const auto &in = GetInput();

if (in.width <= 0 || in.height <= 0) {
return false;
}
const auto expected_size = static_cast<std::size_t>(in.width) * static_cast<std::size_t>(in.height);
return in.data.size() == expected_size;
}

bool PikhotskiyRVerticalGaussFilterSEQ::PreProcessingImpl() {
const auto &in = GetInput();
width_ = in.width;
height_ = in.height;
stripe_width_ = std::max(1, width_ / kStripeDivider);

source_ = in.data;
vertical_buffer_.assign(source_.size(), 0);
result_buffer_.assign(source_.size(), 0);
return true;
}

bool PikhotskiyRVerticalGaussFilterSEQ::RunImpl() {
const auto expected_size = static_cast<std::size_t>(width_) * static_cast<std::size_t>(height_);
if (width_ <= 0 || height_ <= 0 || source_.size() != expected_size || vertical_buffer_.size() != expected_size ||
result_buffer_.size() != expected_size) {
return false;
}

for (int x_begin = 0; x_begin < width_; x_begin += stripe_width_) {
const int x_end = std::min(width_, x_begin + stripe_width_);
RunVerticalPassForStripe(x_begin, x_end);
}

for (int x_begin = 0; x_begin < width_; x_begin += stripe_width_) {
const int x_end = std::min(width_, x_begin + stripe_width_);
RunHorizontalPassForStripe(x_begin, x_end);
}

return true;
}

bool PikhotskiyRVerticalGaussFilterSEQ::PostProcessingImpl() {
GetOutput().width = width_;
GetOutput().height = height_;
GetOutput().data = result_buffer_;
return true;
}

void PikhotskiyRVerticalGaussFilterSEQ::RunVerticalPassForStripe(int x_begin, int x_end) {
for (int row = 0; row < height_; ++row) {
const int row_top = ClampIndex(row - 1, height_);
const int row_bottom = ClampIndex(row + 1, height_);

for (int col = x_begin; col < x_end; ++col) {
const std::size_t center = ToLinearIndex(col, row, width_);
const std::size_t top = ToLinearIndex(col, row_top, width_);
const std::size_t bottom = ToLinearIndex(col, row_bottom, width_);
vertical_buffer_[center] =
static_cast<int>(source_[top]) + (2 * static_cast<int>(source_[center])) + static_cast<int>(source_[bottom]);
}
}
}

void PikhotskiyRVerticalGaussFilterSEQ::RunHorizontalPassForStripe(int x_begin, int x_end) {
for (int row = 0; row < height_; ++row) {
for (int col = x_begin; col < x_end; ++col) {
const int col_left = ClampIndex(col - 1, width_);
const int col_right = ClampIndex(col + 1, width_);
const std::size_t center = ToLinearIndex(col, row, width_);
const std::size_t left = ToLinearIndex(col_left, row, width_);
const std::size_t right = ToLinearIndex(col_right, row, width_);
const int weighted_sum = vertical_buffer_[left] + (2 * vertical_buffer_[center]) + vertical_buffer_[right];
result_buffer_[center] = NormalizeAndRoundUp(weighted_sum);
}
}
}

} // namespace pikhotskiy_r_vertical_gauss_filter
10 changes: 10 additions & 0 deletions tasks/pikhotskiy_r_vertical_gauss_filter/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"tasks": {
"all": "enabled",
"omp": "enabled",
"seq": "enabled",
"stl": "enabled",
"tbb": "enabled"
},
"tasks_type": "threads"
}
124 changes: 124 additions & 0 deletions tasks/pikhotskiy_r_vertical_gauss_filter/tests/functional/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#include <gtest/gtest.h>

#include <array>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

#include "pikhotskiy_r_vertical_gauss_filter/common/include/common.hpp"
#include "pikhotskiy_r_vertical_gauss_filter/seq/include/ops_seq.hpp"
#include "util/include/func_test_util.hpp"
#include "util/include/util.hpp"

namespace pikhotskiy_r_vertical_gauss_filter {

class PikhotskiyRVerticalGaussFilterFuncTests : public ppc::util::BaseRunFuncTests<InType, OutType, TestType> {
public:
static std::string PrintTestParam(const TestType &test_param) {
const auto &input = std::get<0>(test_param);
std::string extra = input.data.empty() ? "empty" : std::to_string(input.data[0]);
return std::to_string(input.width) + "x" + std::to_string(input.height) + "_" + extra;
}

protected:
void SetUp() override {
const auto &params = std::get<static_cast<std::size_t>(ppc::util::GTestParamIndex::kTestParams)>(GetParam());
input_data_ = std::get<0>(params);
expected_data_ = std::get<1>(params);
}

bool CheckTestOutputData(OutType &output_data) final {
return output_data.width == expected_data_.width && output_data.height == expected_data_.height &&
output_data.data == expected_data_.data;
}

InType GetTestInputData() final {
return input_data_;
}

private:
InType input_data_;
OutType expected_data_;
};

TEST_P(PikhotskiyRVerticalGaussFilterFuncTests, RunTest) {
ExecuteTest(GetParam());
}

namespace {

Matrix CreateMatrix(int w, int h, const std::vector<uint8_t> &d) {
Matrix m;
m.width = w;
m.height = h;
m.data = d;
return m;
}

const Matrix kTest1x1 = CreateMatrix(1, 1, {100});
const Matrix kResult1x1 = CreateMatrix(1, 1, {100});

const Matrix kTest2x2 = CreateMatrix(2, 2, {1, 2, 3, 4});
const Matrix kResult2x2 = CreateMatrix(2, 2, {2, 3, 3, 4});

const Matrix kTest3x3 = CreateMatrix(3, 3, std::vector<uint8_t>(9, 16));
const Matrix kResult3x3 = CreateMatrix(3, 3, std::vector<uint8_t>(9, 16));

const std::array<TestType, 3> kTestCases = {std::make_tuple(kTest1x1, kResult1x1),
std::make_tuple(kTest2x2, kResult2x2),
std::make_tuple(kTest3x3, kResult3x3)};

using ParamType = std::tuple<std::function<std::shared_ptr<BaseTask>(InType)>, std::string, TestType>;

std::vector<ParamType> CreateTestParams() {
std::vector<ParamType> params;
params.reserve(kTestCases.size());
for (const auto &test_case : kTestCases) {
params.emplace_back([](const InType &in) -> std::shared_ptr<BaseTask> {
return std::make_shared<PikhotskiyRVerticalGaussFilterSEQ>(in);
}, "seq", test_case);
}
return params;
}

const auto kTestParams = CreateTestParams();
const auto kGtestValues = testing::ValuesIn(kTestParams);
const auto kFuncTestName =
PikhotskiyRVerticalGaussFilterFuncTests::PrintFuncTestName<PikhotskiyRVerticalGaussFilterFuncTests>;

INSTANTIATE_TEST_SUITE_P(ImageTests, PikhotskiyRVerticalGaussFilterFuncTests, kGtestValues, kFuncTestName);

} // namespace

TEST(PikhotskiyRVerticalGaussFilterInvalidTest, ZeroWidth) {
Matrix input;
input.width = 0;
input.height = 5;
input.data.resize(5);
auto task = std::make_shared<PikhotskiyRVerticalGaussFilterSEQ>(input);
EXPECT_FALSE(task->Validation());
}

TEST(PikhotskiyRVerticalGaussFilterInvalidTest, ZeroHeight) {
Matrix input;
input.width = 5;
input.height = 0;
input.data.resize(5);
auto task = std::make_shared<PikhotskiyRVerticalGaussFilterSEQ>(input);
EXPECT_FALSE(task->Validation());
}

TEST(PikhotskiyRVerticalGaussFilterInvalidTest, DataSizeMismatch) {
Matrix input;
input.width = 3;
input.height = 3;
input.data = {1, 2, 3};
auto task = std::make_shared<PikhotskiyRVerticalGaussFilterSEQ>(input);
EXPECT_FALSE(task->Validation());
}

} // namespace pikhotskiy_r_vertical_gauss_filter
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <gtest/gtest.h>

#include <cstddef>
#include <cstdint>
#include <random>
#include <vector>

#include "pikhotskiy_r_vertical_gauss_filter/common/include/common.hpp"
#include "pikhotskiy_r_vertical_gauss_filter/seq/include/ops_seq.hpp"
#include "util/include/perf_test_util.hpp"

namespace pikhotskiy_r_vertical_gauss_filter {

class PikhotskiyRVerticalGaussFilterPerfTests : public ppc::util::BaseRunPerfTests<InType, OutType> {
static constexpr int kImgWidth = 4096;
static constexpr int kImgHeight = 4096;
InType input_img_{};

void SetUp() override {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dist(0, 255);

input_img_.width = kImgWidth;
input_img_.height = kImgHeight;

input_img_.data.resize(static_cast<size_t>(kImgWidth) * static_cast<size_t>(kImgHeight));
for (auto &val : input_img_.data) {
val = static_cast<std::uint8_t>(dist(gen));
}
}

bool CheckTestOutputData(OutType &output) final {
return output.width == input_img_.width && output.height == input_img_.height &&
output.data.size() == input_img_.data.size();
}

InType GetTestInputData() final {
return input_img_;
}
};

TEST_P(PikhotskiyRVerticalGaussFilterPerfTests, RunPerfModes) {
ExecuteTest(GetParam());
}

namespace {

const auto kAllPerfTasks = ppc::util::MakeAllPerfTasks<InType, PikhotskiyRVerticalGaussFilterSEQ>(
PPC_SETTINGS_pikhotskiy_r_vertical_gauss_filter);

const auto kGtestValues = ppc::util::TupleToGTestValues(kAllPerfTasks);

const auto kPerfTestName = PikhotskiyRVerticalGaussFilterPerfTests::CustomPerfTestName;

INSTANTIATE_TEST_SUITE_P(PerfRunTests, PikhotskiyRVerticalGaussFilterPerfTests, kGtestValues, kPerfTestName);

} // namespace

} // namespace pikhotskiy_r_vertical_gauss_filter
Loading