diff --git a/tasks/omp/kulagin_a_gauss_filter_vert/func_tests/main.cpp b/tasks/omp/kulagin_a_gauss_filter_vert/func_tests/main.cpp new file mode 100644 index 000000000..7b533d3f0 --- /dev/null +++ b/tasks/omp/kulagin_a_gauss_filter_vert/func_tests/main.cpp @@ -0,0 +1,48 @@ +// Copyright 2024 Kulagin Aleksandr +#include +#include + +#include "omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp" + +void test_case(const size_t& w, const size_t& h, const float& sigma, std::vector (*generator)(const size_t&, const size_t&)) { + // Create data + std::vector img = generator(w, h); + std::vector res(w * h); + std::vector kernel = kulagin_a_gauss::generate_kernel(sigma); + std::vector out(w * h); + kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(img.data())); + taskDataSeq->inputs.emplace_back(reinterpret_cast(kernel.data())); + taskDataSeq->inputs_count.emplace_back(w); + taskDataSeq->inputs_count.emplace_back(h); + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(w); + taskDataSeq->outputs_count.emplace_back(h); + + // Create Task + FilterGaussVerticalTaskOMPKulagin myTask(taskDataSeq); + ASSERT_EQ(myTask.validation(), true); + ASSERT_EQ(myTask.pre_processing(), true); + ASSERT_EQ(myTask.run(), true); + ASSERT_EQ(myTask.post_processing(), true); + for (size_t i = 0; i < w * h; i++) { + for (size_t j = 0; j < 3; j++) { + uint32_t res_ch = kulagin_a_gauss::get_color_channel(res[i], j); + uint32_t out_ch = kulagin_a_gauss::get_color_channel(out[i], j); + ASSERT_EQ(((res_ch >= out_ch) ? (res_ch - out_ch) : (out_ch - res_ch)) <= 1, true); + } + } +} + +TEST(kulagin_a_gauss_filter_vert_omp, test1) { test_case(100, 100, 0.0f, kulagin_a_gauss::generator1); } + +TEST(kulagin_a_gauss_filter_vert_omp, test2) { test_case(100, 200, 0.0f, kulagin_a_gauss::generator1); } + +TEST(kulagin_a_gauss_filter_vert_omp, test3) { test_case(200, 100, 0.0f, kulagin_a_gauss::generator1); } + +TEST(kulagin_a_gauss_filter_vert_omp, test4) { test_case(100, 100, 4.0f, kulagin_a_gauss::generator1); } + +TEST(kulagin_a_gauss_filter_vert_omp, test5) { test_case(101, 101, 2.0f, kulagin_a_gauss::generator1); } diff --git a/tasks/omp/kulagin_a_gauss_filter_vert/include/common.hpp b/tasks/omp/kulagin_a_gauss_filter_vert/include/common.hpp new file mode 100644 index 000000000..07c33237d --- /dev/null +++ b/tasks/omp/kulagin_a_gauss_filter_vert/include/common.hpp @@ -0,0 +1,150 @@ +// Copyright 2024 Kulagin Aleksandr +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace kulagin_a_gauss { +static const uint32_t default_alpha = 0x0; + +inline const uint32_t& get_color(const size_t& w, const size_t&, const uint32_t* img, const size_t& px, + const size_t& py) { + return img[px + py * w]; +} + +inline uint32_t& get_color(const size_t& w, const size_t&, uint32_t* img, const size_t& px, const size_t& py) { + return img[px + py * w]; +} + +inline uint32_t get_color_channel(const uint32_t& col, const uint32_t& i) noexcept { return (col >> (i * 8)) & (0xff); } + +template +inline void put_color_channel(uint32_t& col, const uint32_t& ch, const uint32_t& i) noexcept { + if (!is_channel_clean) { + col &= ~(0xff << (i * 8)) & ~(~default_alpha << 24); + } + col |= ((ch & 0xff) << (i * 8)); +} + +template +inline void apply_filter_pixel(const size_t& w, const size_t& h, const uint32_t* img, const float* kernel, + uint32_t* res_img, const size_t& px, const size_t& py) { + float res_pxl[3] = {0.0f, 0.0f, 0.0f}; + uint32_t i; + for (size_t l = 0; l < 3; l++) { + for (size_t k = 0; k < 3; k++) { + size_t dx = px + k - 1; + size_t dy = py + l - 1; + if (check_border) { + if (px == 0 && k == 0) { + dx = 0; + } else if (dx == w) { + dx--; + } + if (py == 0 && l == 0) { + dy = 0; + } else if (dy == h) { + dy--; + } + } + const uint32_t& tmp_col = get_color(w, h, img, dx, dy); + for (i = 0; i < 3; i++) { + res_pxl[i] += (static_cast(get_color_channel(tmp_col, i))) * kernel[k + l * 3]; + } + } + } + for (i = 0; i < 3; i++) { + put_color_channel(get_color(w, h, res_img, px, py), std::clamp(res_pxl[i], 0, 255), i); + } +} + +template +inline void apply_filter_line(const size_t& w, const size_t& h, const uint32_t* img, const float* kernel, + uint32_t* res_img, const size_t& x) { + size_t y; + apply_filter_pixel(w, h, img, kernel, res_img, x, 0); + for (y = 1; y < h - 1; y++) { + apply_filter_pixel(w, h, img, kernel, res_img, x, y); + } + if (h != 1) { + apply_filter_pixel(w, h, img, kernel, res_img, x, h - 1); + } +} + +inline void apply_filter(const size_t& w, const size_t& h, const uint32_t* img, const float* kernel, + uint32_t* img_res) { + kulagin_a_gauss::apply_filter_line(w, h, img, kernel, img_res, 0); + for (size_t x = 1; x < w - 1; x++) { + kulagin_a_gauss::apply_filter_line(w, h, img, kernel, img_res, x); + } + if (w != 1) { + kulagin_a_gauss::apply_filter_line(w, h, img, kernel, img_res, w - 1); + } +} + +inline std::vector generator1(const size_t& w, const size_t& h) { + std::vector img(w * h); + for (size_t i = 0; i < w * h; i++) { + uint32_t col = 0; + for (size_t j = 0; j < 3; j++) { + put_color_channel(col, (i * (j + 1)) % 256, j); + } + img[i] = col; + } + return img; +} + +inline std::vector generate_kernel() { return std::vector(3 * 3, 1.0f / 9.0f); } + +inline std::vector generate_kernel(const float& sigma) { + if (std::abs(sigma) <= std::numeric_limits::epsilon() || std::isinf(sigma)) { + // if sigma is 0 + return generate_kernel(); + } + std::vector kernel(3 * 3); + float norm = 0.0f; + for (size_t i = 0; i < 3; i++) { + const size_t x = i % 2 + 1; + for (size_t j = 0; j < 3; j++) { + const size_t y = j % 2 + 1; + kernel[i + 3 * j] = expf(-static_cast(x * x + y * y) / (2.0f * sigma * sigma)); + norm += kernel[i + 3 * j]; + } + } + for (size_t i = 0; i < 9; i++) { + kernel[i] /= norm; + } + return kernel; +} + +inline std::string generate_path_to_img(const std::string& task, const std::string& task_name, + const std::string& filename) { + return (std::filesystem::path() / PATH_TO_PPC_PROJECT / "tasks" / task / task_name / "data" / filename).string(); +} + +inline bool get_img_from_file(const std::string& path, std::vector& img, size_t& w, size_t& h) { + std::ifstream f(path, std::ios::binary | std::ios::in); + if (!f) { + return false; + } + uint32_t _w; + uint32_t _h; + f.read(reinterpret_cast(&_w), sizeof(uint32_t)); + f.read(reinterpret_cast(&_h), sizeof(uint32_t)); + w = _w; + h = _h; + img.resize(w * h); + for (size_t i = 0; i < w * h; i++) { + f.read(reinterpret_cast(&img[i]), sizeof(uint32_t)); + } + f.close(); + return f.good(); +} +} // namespace kulagin_a_gauss diff --git a/tasks/omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp b/tasks/omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp new file mode 100644 index 000000000..022c1b6e2 --- /dev/null +++ b/tasks/omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp @@ -0,0 +1,21 @@ +// Copyright 2024 Kulagin Aleksandr +#pragma once + +#include "core/task/include/task.hpp" +#include "omp/kulagin_a_gauss_filter_vert/include/common.hpp" + +class FilterGaussVerticalTaskOMPKulagin : public ppc::core::Task { + public: + explicit FilterGaussVerticalTaskOMPKulagin(std::shared_ptr taskData_) + : Task(std::move(taskData_)) {} + bool pre_processing() override; + bool validation() override; + bool run() override; + bool post_processing() override; + + private: + uint32_t* img{}; + size_t w{}, h{}; + float* kernel{}; + std::unique_ptr img_res; +}; diff --git a/tasks/omp/kulagin_a_gauss_filter_vert/perf_tests/main.cpp b/tasks/omp/kulagin_a_gauss_filter_vert/perf_tests/main.cpp new file mode 100644 index 000000000..4c9d428e8 --- /dev/null +++ b/tasks/omp/kulagin_a_gauss_filter_vert/perf_tests/main.cpp @@ -0,0 +1,99 @@ +// Copyright 2024 Kulagin Aleksandr +#include + +#include "core/perf/include/perf.hpp" +#include "omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp" + +TEST(kulagin_a_gauss_filter_vert_seq, test_pipeline_run) { + // Create data + size_t w = 1500; + size_t h = 1500; + float sigma = 2.0f; + std::vector img = kulagin_a_gauss::generator1(w, h); + std::vector kernel = kulagin_a_gauss::generate_kernel(sigma); + std::vector out(w * h); + std::vector res(w * h); + kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(img.data())); + taskDataSeq->inputs.emplace_back(reinterpret_cast(kernel.data())); + taskDataSeq->inputs_count.emplace_back(w); + taskDataSeq->inputs_count.emplace_back(h); + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(w); + taskDataSeq->outputs_count.emplace_back(h); + + // Create Task + auto myTask = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(myTask); + perfAnalyzer->pipeline_run(perfAttr, perfResults); + ppc::core::Perf::print_perf_statistic(perfResults); + + for (size_t i = 0; i < w * h; i++) { + ASSERT_EQ(res[i], out[i]); + } +} + +TEST(kulagin_a_gauss_filter_vert_seq, test_task_run) { + // Create data + size_t w = 1500; + size_t h = 1500; + float sigma = 2.0f; + std::vector img = kulagin_a_gauss::generator1(w, h); + std::vector kernel = kulagin_a_gauss::generate_kernel(sigma); + std::vector out(w * h); + std::vector res(w * h); + kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); + + // Create TaskData + std::shared_ptr taskDataSeq = std::make_shared(); + taskDataSeq->inputs.emplace_back(reinterpret_cast(img.data())); + taskDataSeq->inputs.emplace_back(reinterpret_cast(kernel.data())); + taskDataSeq->inputs_count.emplace_back(w); + taskDataSeq->inputs_count.emplace_back(h); + taskDataSeq->outputs.emplace_back(reinterpret_cast(out.data())); + taskDataSeq->outputs_count.emplace_back(w); + taskDataSeq->outputs_count.emplace_back(h); + + // Create Task + auto myTask = std::make_shared(taskDataSeq); + + // Create Perf attributes + auto perfAttr = std::make_shared(); + perfAttr->num_running = 10; + const auto t0 = std::chrono::high_resolution_clock::now(); + perfAttr->current_timer = [&] { + auto current_time_point = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(current_time_point - t0).count(); + return static_cast(duration) * 1e-9; + }; + + // Create and init perf results + auto perfResults = std::make_shared(); + + // Create Perf analyzer + auto perfAnalyzer = std::make_shared(myTask); + perfAnalyzer->task_run(perfAttr, perfResults); + ppc::core::Perf::print_perf_statistic(perfResults); + + for (size_t i = 0; i < w * h; i++) { + ASSERT_EQ(res[i], out[i]); + } +} diff --git a/tasks/omp/kulagin_a_gauss_filter_vert/src/ops_omp.cpp b/tasks/omp/kulagin_a_gauss_filter_vert/src/ops_omp.cpp new file mode 100644 index 000000000..60fd705e1 --- /dev/null +++ b/tasks/omp/kulagin_a_gauss_filter_vert/src/ops_omp.cpp @@ -0,0 +1,65 @@ +// Copyright 2024 Kulagin Aleksandr +#include "omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp" + +#include +#include +#include + +bool FilterGaussVerticalTaskOMPKulagin::pre_processing() { + internal_order_test(); + try { + // Init value for input and output + img = reinterpret_cast(taskData->inputs[0]); + kernel = reinterpret_cast(taskData->inputs[1]); + w = taskData->inputs_count[0]; + h = taskData->inputs_count[1]; + img_res = std::make_unique(w * h); + } catch (std::exception& e) { + std::cout << e.what() << '\n'; + return false; + } catch (...) { + std::cout << "Something went very terrible!\n"; + return false; + } + return true; +} + +bool FilterGaussVerticalTaskOMPKulagin::validation() { + internal_order_test(); + // Check count elements of output + return taskData->inputs_count.size() == 2 && taskData->inputs.size() == 2 && taskData->outputs.size() == 1 && + taskData->inputs_count[0] > 0 && taskData->inputs_count[1] > 0 && + taskData->inputs_count == taskData->outputs_count && taskData->inputs[0] != nullptr && + taskData->inputs[1] != nullptr; +} + +bool FilterGaussVerticalTaskOMPKulagin::run() { + internal_order_test(); + try { + #pragma omp for schedule(static) + for (size_t x = 0; x < w; x++) { + kulagin_a_gauss::apply_filter_line(w, h, img, kernel, img_res.get(), x); + } + } catch (std::exception& e) { + std::cout << e.what() << '\n'; + return false; + } catch (...) { + std::cout << "Something went very terrible!\n"; + return false; + } + return true; +} + +bool FilterGaussVerticalTaskOMPKulagin::post_processing() { + internal_order_test(); + try { + std::memcpy(reinterpret_cast(taskData->outputs[0]), img_res.get(), w * h * sizeof(uint32_t)); + } catch (std::exception& e) { + std::cout << e.what() << '\n'; + return false; + } catch (...) { + std::cout << "Something went very terrible!\n"; + return false; + } + return true; +}