-
Notifications
You must be signed in to change notification settings - Fork 127
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3fa3aa1
commit 7e6e68c
Showing
5 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright 2024 Kulagin Aleksandr | ||
#include <gtest/gtest.h> | ||
#include <omp.h> | ||
|
||
#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<uint32_t> (*generator)(const size_t&, const size_t&)) { | ||
// Create data | ||
std::vector<uint32_t> img = generator(w, h); | ||
std::vector<uint32_t> res(w * h); | ||
std::vector<float> kernel = kulagin_a_gauss::generate_kernel(sigma); | ||
std::vector<uint32_t> out(w * h); | ||
kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); | ||
|
||
// Create TaskData | ||
std::shared_ptr<ppc::core::TaskData> taskDataSeq = std::make_shared<ppc::core::TaskData>(); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(img.data())); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(kernel.data())); | ||
taskDataSeq->inputs_count.emplace_back(w); | ||
taskDataSeq->inputs_count.emplace_back(h); | ||
taskDataSeq->outputs.emplace_back(reinterpret_cast<uint8_t *>(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); } |
150 changes: 150 additions & 0 deletions
150
tasks/omp/kulagin_a_gauss_filter_vert/include/common.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
// Copyright 2024 Kulagin Aleksandr | ||
#pragma once | ||
|
||
#include <algorithm> | ||
#include <cmath> | ||
#include <cstddef> | ||
#include <cstdint> | ||
#include <filesystem> | ||
#include <fstream> | ||
#include <limits> | ||
#include <string> | ||
#include <vector> | ||
|
||
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 <bool is_channel_clean = true> | ||
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 <bool check_border = true> | ||
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<float>(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<uint32_t>(res_pxl[i], 0, 255), i); | ||
} | ||
} | ||
|
||
template <bool check_border = true> | ||
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<true>(w, h, img, kernel, res_img, x, 0); | ||
for (y = 1; y < h - 1; y++) { | ||
apply_filter_pixel<check_border>(w, h, img, kernel, res_img, x, y); | ||
} | ||
if (h != 1) { | ||
apply_filter_pixel<true>(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<true>(w, h, img, kernel, img_res, 0); | ||
for (size_t x = 1; x < w - 1; x++) { | ||
kulagin_a_gauss::apply_filter_line<false>(w, h, img, kernel, img_res, x); | ||
} | ||
if (w != 1) { | ||
kulagin_a_gauss::apply_filter_line<true>(w, h, img, kernel, img_res, w - 1); | ||
} | ||
} | ||
|
||
inline std::vector<uint32_t> generator1(const size_t& w, const size_t& h) { | ||
std::vector<uint32_t> 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<float> generate_kernel() { return std::vector<float>(3 * 3, 1.0f / 9.0f); } | ||
|
||
inline std::vector<float> generate_kernel(const float& sigma) { | ||
if (std::abs(sigma) <= std::numeric_limits<float>::epsilon() || std::isinf(sigma)) { | ||
// if sigma is 0 | ||
return generate_kernel(); | ||
} | ||
std::vector<float> 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<float>(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<uint32_t>& 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<char*>(&_w), sizeof(uint32_t)); | ||
f.read(reinterpret_cast<char*>(&_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<char*>(&img[i]), sizeof(uint32_t)); | ||
} | ||
f.close(); | ||
return f.good(); | ||
} | ||
} // namespace kulagin_a_gauss |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<ppc::core::TaskData> 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<uint32_t[]> img_res; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright 2024 Kulagin Aleksandr | ||
#include <gtest/gtest.h> | ||
|
||
#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<uint32_t> img = kulagin_a_gauss::generator1(w, h); | ||
std::vector<float> kernel = kulagin_a_gauss::generate_kernel(sigma); | ||
std::vector<uint32_t> out(w * h); | ||
std::vector<uint32_t> res(w * h); | ||
kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); | ||
|
||
// Create TaskData | ||
std::shared_ptr<ppc::core::TaskData> taskDataSeq = std::make_shared<ppc::core::TaskData>(); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(img.data())); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(kernel.data())); | ||
taskDataSeq->inputs_count.emplace_back(w); | ||
taskDataSeq->inputs_count.emplace_back(h); | ||
taskDataSeq->outputs.emplace_back(reinterpret_cast<uint8_t *>(out.data())); | ||
taskDataSeq->outputs_count.emplace_back(w); | ||
taskDataSeq->outputs_count.emplace_back(h); | ||
|
||
// Create Task | ||
auto myTask = std::make_shared<FilterGaussVerticalTaskOMPKulagin>(taskDataSeq); | ||
|
||
// Create Perf attributes | ||
auto perfAttr = std::make_shared<ppc::core::PerfAttr>(); | ||
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<std::chrono::nanoseconds>(current_time_point - t0).count(); | ||
return static_cast<double>(duration) * 1e-9; | ||
}; | ||
|
||
// Create and init perf results | ||
auto perfResults = std::make_shared<ppc::core::PerfResults>(); | ||
|
||
// Create Perf analyzer | ||
auto perfAnalyzer = std::make_shared<ppc::core::Perf>(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<uint32_t> img = kulagin_a_gauss::generator1(w, h); | ||
std::vector<float> kernel = kulagin_a_gauss::generate_kernel(sigma); | ||
std::vector<uint32_t> out(w * h); | ||
std::vector<uint32_t> res(w * h); | ||
kulagin_a_gauss::apply_filter(w, h, img.data(), kernel.data(), res.data()); | ||
|
||
// Create TaskData | ||
std::shared_ptr<ppc::core::TaskData> taskDataSeq = std::make_shared<ppc::core::TaskData>(); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(img.data())); | ||
taskDataSeq->inputs.emplace_back(reinterpret_cast<uint8_t *>(kernel.data())); | ||
taskDataSeq->inputs_count.emplace_back(w); | ||
taskDataSeq->inputs_count.emplace_back(h); | ||
taskDataSeq->outputs.emplace_back(reinterpret_cast<uint8_t *>(out.data())); | ||
taskDataSeq->outputs_count.emplace_back(w); | ||
taskDataSeq->outputs_count.emplace_back(h); | ||
|
||
// Create Task | ||
auto myTask = std::make_shared<FilterGaussVerticalTaskOMPKulagin>(taskDataSeq); | ||
|
||
// Create Perf attributes | ||
auto perfAttr = std::make_shared<ppc::core::PerfAttr>(); | ||
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<std::chrono::nanoseconds>(current_time_point - t0).count(); | ||
return static_cast<double>(duration) * 1e-9; | ||
}; | ||
|
||
// Create and init perf results | ||
auto perfResults = std::make_shared<ppc::core::PerfResults>(); | ||
|
||
// Create Perf analyzer | ||
auto perfAnalyzer = std::make_shared<ppc::core::Perf>(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]); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
// Copyright 2024 Kulagin Aleksandr | ||
#include "omp/kulagin_a_gauss_filter_vert/include/ops_omp.hpp" | ||
|
||
#include <cstring> | ||
#include <exception> | ||
#include <omp.h> | ||
|
||
bool FilterGaussVerticalTaskOMPKulagin::pre_processing() { | ||
internal_order_test(); | ||
try { | ||
// Init value for input and output | ||
img = reinterpret_cast<uint32_t*>(taskData->inputs[0]); | ||
kernel = reinterpret_cast<float*>(taskData->inputs[1]); | ||
w = taskData->inputs_count[0]; | ||
h = taskData->inputs_count[1]; | ||
img_res = std::make_unique<uint32_t[]>(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<true>(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<uint32_t*>(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; | ||
} |