Skip to content

Commit 6596297

Browse files
authored
Киселев Игорь. Технология SEQ-MPI. Повышение контраста полутонового изображения посредством линейной растяжки гистограммы. Вариант 30 (#284)
## Описание <!-- Пожалуйста, предоставьте подробное описание вашей реализации, включая: - основные детали решения (описание выбранного алгоритма) - применение технологии параллелизма (если применимо) --> - **Задача**: Повышение контраста полутонового изображения посредством линейной растяжки гистограммы - **Вариант**: 30 - **Технология**: SEQ, MPI - **Описание** **SEQ-реализация** Последовательная версия реализует алгоритм линейной растяжки гистограммы для полутонового изображения. На первом этапе производится поиск минимального и максимального значений яркости среди всех пикселей изображения. Далее для каждого пикселя выполняется линейное преобразование яркости по формуле растяжки в диапазон \[0; 255\]. В случае, если минимальное и максимальное значения совпадают, изображение копируется без изменений. Алгоритм выполняется полностью последовательно и корректно обрабатывает все допустимые входные данные. **MPI-реализация** Параллельная версия использует технологию MPI и реализует линейную растяжку гистограммы с распределением пикселей изображения между процессами. Общее количество пикселей делится между процессами равномерно. Каждый процесс получает свою часть данных и локально вычисляет минимальное и максимальное значения яркости. Глобальные минимальное и максимальное значения определяются с помощью коллективных операций `MPI_Allreduce`. После этого каждый процесс выполняет линейную растяжку яркости для своей части изображения. На завершающем этапе обработанные данные собираются на корневом процессе с помощью `MPI_Gatherv`. MPI-реализация обеспечивает корректность вычислений и позволяет ускорить обработку изображений большого размера. Отчёт оформлен в соответствии с примером из учебных материалов и содержит описание последовательной и параллельной реализаций алгоритма, а также сравнение SEQ и MPI решений на больших входных данных. --- ## Чек-лист <!-- Пожалуйста, убедитесь, что следующие пункты выполнены **до** отправки pull request'а и запроса его ревью: --> - [x] **Статус CI**: Все CI-задачи (сборка, тесты, генерация отчёта) успешно проходят на моей ветке в моем форке - [x] **Директория и именование задачи**: Я создал директорию с именем `<фамилия>_<первая_буква_имени>_<короткое_название_задачи>` - [x] **Полное описание задачи**: Я предоставил полное описание задачи в теле pull request - [x] **clang-format**: Мои изменения успешно проходят `clang-format` локально в моем форке (нет ошибок форматирования) - [x] **clang-tidy**: Мои изменения успешно проходят `clang-tidy` локально в моем форке (нет предупреждений/ошибок) - [x] **Функциональные тесты**: Все функциональные тесты успешно проходят локально на моей машине - [x] **Тесты производительности**: Все тесты производительности успешно проходят локально на моей машине - [x] **Ветка**: Я работаю в ветке, названной точно так же, как директория моей задачи (например, `nesterov_a_vector_sum`), а не в `master` - [x] **Правдивое содержание**: Я подтверждаю, что все сведения, указанные в этом pull request, являются точными и достоверными <!-- ПРИМЕЧАНИЕ: Ложные сведения в этом чек-листе могут привести к отклонению PR и получению нулевого балла за соответствующую задачу. -->
1 parent 23cfe73 commit 6596297

11 files changed

Lines changed: 672 additions & 0 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
#include <cstdint>
5+
#include <string>
6+
#include <tuple>
7+
#include <vector>
8+
9+
#include "task/include/task.hpp"
10+
11+
namespace kiselev_i_linear_histogram_stretch {
12+
13+
struct GrayImage {
14+
std::vector<uint8_t> pixels;
15+
std::size_t width = 0;
16+
std::size_t height = 0;
17+
};
18+
19+
using InType = GrayImage;
20+
using OutType = std::vector<uint8_t>;
21+
using TestType = std::tuple<InType, OutType, std::string>;
22+
using BaseTask = ppc::task::Task<InType, OutType>;
23+
24+
} // namespace kiselev_i_linear_histogram_stretch
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"student": {
3+
"first_name": "Игорь",
4+
"last_name": "Киселев",
5+
"middle_name": "Вячеславович",
6+
"group_number": "3823Б1ФИ1",
7+
"task_number": "3"
8+
}
9+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#pragma once
2+
3+
#include "kiselev_i_linear_histogram_stretch/common/include/common.hpp"
4+
#include "task/include/task.hpp"
5+
6+
namespace kiselev_i_linear_histogram_stretch {
7+
8+
class KiselevITestTaskMPI : public BaseTask {
9+
public:
10+
static constexpr ppc::task::TypeOfTask GetStaticTypeOfTask() {
11+
return ppc::task::TypeOfTask::kMPI;
12+
}
13+
explicit KiselevITestTaskMPI(const InType &in);
14+
15+
private:
16+
bool ValidationImpl() override;
17+
bool PreProcessingImpl() override;
18+
bool RunImpl() override;
19+
bool PostProcessingImpl() override;
20+
};
21+
22+
} // namespace kiselev_i_linear_histogram_stretch
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#include "kiselev_i_linear_histogram_stretch/mpi/include/ops_mpi.hpp"
2+
3+
#include <mpi.h>
4+
5+
#include <algorithm>
6+
#include <cmath>
7+
#include <cstddef>
8+
#include <limits>
9+
#include <vector>
10+
11+
#include "kiselev_i_linear_histogram_stretch/common/include/common.hpp"
12+
13+
namespace kiselev_i_linear_histogram_stretch {
14+
15+
KiselevITestTaskMPI::KiselevITestTaskMPI(const InType &in) {
16+
SetTypeOfTask(GetStaticTypeOfTask());
17+
GetInput() = in;
18+
19+
if (!in.pixels.empty()) {
20+
GetOutput().resize(in.pixels.size());
21+
}
22+
}
23+
24+
bool KiselevITestTaskMPI::ValidationImpl() {
25+
const auto &img = GetInput();
26+
return img.width > 0 && img.height > 0 && img.pixels.size() == img.width * img.height;
27+
}
28+
29+
bool KiselevITestTaskMPI::PreProcessingImpl() {
30+
return true;
31+
}
32+
33+
bool KiselevITestTaskMPI::RunImpl() {
34+
int rank = 0;
35+
int size = 1;
36+
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
37+
MPI_Comm_size(MPI_COMM_WORLD, &size);
38+
39+
constexpr int kRoot = 0;
40+
41+
std::size_t total_size = 0;
42+
if (rank == kRoot) {
43+
total_size = GetInput().pixels.size();
44+
}
45+
46+
MPI_Bcast(&total_size, 1, MPI_UNSIGNED_LONG_LONG, kRoot, MPI_COMM_WORLD);
47+
48+
const std::size_t base = total_size / static_cast<std::size_t>(size);
49+
const std::size_t extra = total_size % static_cast<std::size_t>(size);
50+
51+
std::vector<int> counts(size, 0);
52+
std::vector<int> offsets(size, 0);
53+
54+
if (rank == kRoot) {
55+
std::size_t shift = 0;
56+
const auto size_sz = static_cast<std::size_t>(size);
57+
58+
for (std::size_t index = 0; index < size_sz; ++index) {
59+
const std::size_t add = (index < extra) ? 1 : 0;
60+
counts[static_cast<int>(index)] = static_cast<int>(base + add);
61+
offsets[static_cast<int>(index)] = static_cast<int>(shift);
62+
shift += static_cast<std::size_t>(counts[static_cast<int>(index)]);
63+
}
64+
}
65+
66+
int local_count = 0;
67+
MPI_Scatter(counts.data(), 1, MPI_INT, &local_count, 1, MPI_INT, kRoot, MPI_COMM_WORLD);
68+
69+
std::vector<unsigned char> local_pixels(static_cast<std::size_t>(local_count));
70+
71+
MPI_Scatterv(GetInput().pixels.data(), counts.data(), offsets.data(), MPI_UNSIGNED_CHAR, local_pixels.data(),
72+
local_count, MPI_UNSIGNED_CHAR, kRoot, MPI_COMM_WORLD);
73+
74+
unsigned char local_min = std::numeric_limits<unsigned char>::max();
75+
unsigned char local_max = std::numeric_limits<unsigned char>::min();
76+
77+
for (unsigned char px : local_pixels) {
78+
local_min = std::min(local_min, px);
79+
local_max = std::max(local_max, px);
80+
}
81+
82+
unsigned char global_min = 0;
83+
unsigned char global_max = 0;
84+
85+
MPI_Allreduce(&local_min, &global_min, 1, MPI_UNSIGNED_CHAR, MPI_MIN, MPI_COMM_WORLD);
86+
MPI_Allreduce(&local_max, &global_max, 1, MPI_UNSIGNED_CHAR, MPI_MAX, MPI_COMM_WORLD);
87+
88+
if (global_min != global_max) {
89+
const double scale = 255.0 / static_cast<double>(global_max - global_min);
90+
91+
for (unsigned char &px : local_pixels) {
92+
const double value = static_cast<double>(px - global_min) * scale;
93+
px = static_cast<unsigned char>(std::lround(value));
94+
}
95+
}
96+
97+
MPI_Gatherv(local_pixels.data(), local_count, MPI_UNSIGNED_CHAR, GetOutput().data(), counts.data(), offsets.data(),
98+
MPI_UNSIGNED_CHAR, kRoot, MPI_COMM_WORLD);
99+
100+
return true;
101+
}
102+
103+
bool KiselevITestTaskMPI::PostProcessingImpl() {
104+
return true;
105+
}
106+
107+
} // namespace kiselev_i_linear_histogram_stretch

0 commit comments

Comments
 (0)