GCC Code Coverage Report


Directory: ./
File: tasks/korolev_k_sobel_oprator/mpi/src/ops_mpi.cpp
Date: 2026-01-27 01:59:34
Exec Total Coverage
Lines: 150 155 96.8%
Functions: 12 12 100.0%
Branches: 88 126 69.8%

Line Branch Exec Source
1 #include "korolev_k_sobel_oprator/mpi/include/ops_mpi.hpp"
2
3 #include <mpi.h>
4
5 #include <algorithm>
6 #include <array>
7 #include <cmath>
8 #include <cstddef>
9 #include <cstdint>
10 #include <vector>
11
12 #include "korolev_k_sobel_oprator/common/include/common.hpp"
13
14 namespace korolev_k_sobel_oprator {
15
16 namespace {
17
18 // Матрицы Собеля для свертки
19 constexpr std::array<std::array<int, 3>, 3> kSobelX = {{{{-1, 0, 1}}, {{-2, 0, 2}}, {{-1, 0, 1}}}};
20 constexpr std::array<std::array<int, 3>, 3> kSobelY = {{{{-1, -2, -1}}, {{0, 0, 0}}, {{1, 2, 1}}}};
21
22 // Конвертация цветного изображения в grayscale
23 12 std::vector<uint8_t> ConvertToGrayscale(const std::vector<uint8_t> &pixels, int width, int channels, int start_row,
24 int num_rows) {
25 12 const auto size = static_cast<std::size_t>(width) * static_cast<std::size_t>(num_rows);
26 12 std::vector<uint8_t> grayscale(size);
27
28
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (channels == 1) {
29
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 6 times.
47 for (int row_idx = 0; row_idx < num_rows; ++row_idx) {
30 41 const int src_y = start_row + row_idx;
31
2/2
✓ Branch 0 taken 595 times.
✓ Branch 1 taken 41 times.
636 for (int col_idx = 0; col_idx < width; ++col_idx) {
32 595 const int src_idx = ((src_y * width) + col_idx) * channels;
33 595 const int gray_idx = (row_idx * width) + col_idx;
34 595 grayscale[gray_idx] = pixels[src_idx];
35 }
36 }
37 return grayscale;
38 }
39
40
2/2
✓ Branch 0 taken 41 times.
✓ Branch 1 taken 6 times.
47 for (int row_idx = 0; row_idx < num_rows; ++row_idx) {
41 41 const int src_y = start_row + row_idx;
42
2/2
✓ Branch 0 taken 595 times.
✓ Branch 1 taken 41 times.
636 for (int col_idx = 0; col_idx < width; ++col_idx) {
43 595 const int src_idx = ((src_y * width) + col_idx) * channels;
44
1/2
✓ Branch 0 taken 595 times.
✗ Branch 1 not taken.
595 const uint8_t r = pixels[src_idx];
45
2/4
✓ Branch 0 taken 595 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 595 times.
✗ Branch 3 not taken.
595 const uint8_t g = (channels > 1) ? pixels[src_idx + 1] : 0;
46
1/2
✓ Branch 0 taken 595 times.
✗ Branch 1 not taken.
595 const uint8_t b = (channels > 2) ? pixels[src_idx + 2] : 0;
47 595 const int gray_idx = (row_idx * width) + col_idx;
48 595 grayscale[gray_idx] = static_cast<uint8_t>((0.299 * r) + (0.587 * g) + (0.114 * b));
49 }
50 }
51 return grayscale;
52 }
53
54 // Применение оператора Собеля к локальному блоку grayscale изображения
55 // local_grayscale содержит локальные строки плюс верхнюю и нижнюю граничные строки
56 12 std::vector<uint8_t> ApplySobelOperatorLocal(const std::vector<uint8_t> &local_grayscale, int width, int local_height) {
57 12 const auto size = static_cast<std::size_t>(width) * static_cast<std::size_t>(local_height);
58 12 std::vector<uint8_t> result(size, 0);
59
60 // Обрабатываем только внутренние пиксели
61 // Для оператора Собеля нужны соседние строки, поэтому обрабатываем начиная с y=1 и заканчивая y=local_height-2
62 const int start_y = 1;
63 12 const int end_y = local_height - 1;
64
65
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 12 times.
70 for (int row_idx = start_y; row_idx < end_y; ++row_idx) {
66
2/2
✓ Branch 0 taken 794 times.
✓ Branch 1 taken 58 times.
852 for (int col_idx = 1; col_idx < width - 1; ++col_idx) {
67 int gx = 0;
68 int gy = 0;
69
70 // Применяем матрицы свертки
71
2/2
✓ Branch 0 taken 2382 times.
✓ Branch 1 taken 794 times.
3176 for (int ky = -1; ky <= 1; ++ky) {
72
2/2
✓ Branch 0 taken 7146 times.
✓ Branch 1 taken 2382 times.
9528 for (int kx = -1; kx <= 1; ++kx) {
73 7146 const int pixel_idx = ((row_idx + ky) * width) + (col_idx + kx);
74 7146 const int pixel_value = static_cast<int>(local_grayscale[pixel_idx]);
75 7146 const int kernel_y = ky + 1;
76 7146 const int kernel_x = kx + 1;
77 7146 gx += pixel_value * kSobelX.at(static_cast<std::size_t>(kernel_y)).at(static_cast<std::size_t>(kernel_x));
78 7146 gy += pixel_value * kSobelY.at(static_cast<std::size_t>(kernel_y)).at(static_cast<std::size_t>(kernel_x));
79 }
80 }
81
82 // Вычисляем величину градиента: |Gx| + |Gy|
83 794 int magnitude = std::abs(gx) + std::abs(gy);
84
85 // Нормализуем в диапазон [0, 255]
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 794 times.
794 magnitude = std::min(255, magnitude / 4);
87
88 794 const int result_idx = (row_idx * width) + col_idx;
89 794 result[result_idx] = static_cast<uint8_t>(magnitude);
90 }
91 }
92
93 12 return result;
94 }
95
96 // Копирование локальных данных процессом 0
97 6 void CopyLocalData(int width, int channels, const std::vector<uint8_t> &all_pixels, std::vector<uint8_t> &local_pixels,
98 int local_start_row_with_border, int local_rows_with_borders) {
99
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 6 times.
48 for (int row_idx = 0; row_idx < local_rows_with_borders; ++row_idx) {
100 42 const int src_y = local_start_row_with_border + row_idx;
101
2/2
✓ Branch 0 taken 600 times.
✓ Branch 1 taken 42 times.
642 for (int col_idx = 0; col_idx < width; ++col_idx) {
102
2/2
✓ Branch 0 taken 1200 times.
✓ Branch 1 taken 600 times.
1800 for (int ch_idx = 0; ch_idx < channels; ++ch_idx) {
103 1200 const int src_idx = (((src_y * width) + col_idx) * channels) + ch_idx;
104 1200 const int dst_idx = (((row_idx * width) + col_idx) * channels) + ch_idx;
105 1200 local_pixels[dst_idx] = all_pixels[src_idx];
106 }
107 }
108 }
109 6 }
110
111 // Отправка данных процессу-получателю
112 6 void SendDataToProcess(int dest, int size_z, int width, int channels, int base_rows, int rem_rows,
113 const std::vector<uint8_t> &all_pixels) {
114
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int dest_start_row = (dest * base_rows) + std::min(dest, rem_rows);
115
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int dest_num_rows = base_rows + (dest < rem_rows ? 1 : 0);
116 int dest_rows_with_borders = dest_num_rows;
117
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if (dest > 0) {
118 6 dest_rows_with_borders++;
119 }
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (dest < size_z - 1) {
121 dest_rows_with_borders++;
122 }
123
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int dest_start_row_with_border = (dest > 0) ? dest_start_row - 1 : dest_start_row;
124
125 6 const int send_count = dest_rows_with_borders * width * channels;
126 6 const auto offset = static_cast<ptrdiff_t>(dest_start_row_with_border) * static_cast<ptrdiff_t>(width) *
127 6 static_cast<ptrdiff_t>(channels);
128 6 MPI_Send(all_pixels.data() + offset, send_count, MPI_UNSIGNED_CHAR, dest, 0, MPI_COMM_WORLD);
129 6 }
130
131 // Распределение данных по процессам
132 12 void DistributeData(int rank, int size_z, int width, int channels, const std::vector<uint8_t> &all_pixels,
133 std::vector<uint8_t> &local_pixels, int local_start_row_with_border, int local_rows_with_borders,
134 int base_rows, int rem_rows) {
135
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
136 // Процесс 0 копирует свои данные
137 6 CopyLocalData(width, channels, all_pixels, local_pixels, local_start_row_with_border, local_rows_with_borders);
138
139 // Отправляем данные остальным процессам
140
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (int dest = 1; dest < size_z; ++dest) {
141 6 SendDataToProcess(dest, size_z, width, channels, base_rows, rem_rows, all_pixels);
142 }
143 } else {
144 // Принимаем данные от процесса 0
145 6 const int recv_count = local_rows_with_borders * width * channels;
146 6 MPI_Recv(local_pixels.data(), recv_count, MPI_UNSIGNED_CHAR, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
147 }
148 12 }
149
150 // Сбор результатов от всех процессов
151 12 void GatherResults(int rank, int size_z, int width, int height, int local_num_rows, int base_rows, int rem_rows,
152 const std::vector<uint8_t> &local_result_clean, std::vector<uint8_t> &output) {
153
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
154 6 const auto output_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(height);
155 6 output.resize(output_size);
156 // Копируем результат процесса 0
157
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 6 times.
42 for (int row_idx = 0; row_idx < local_num_rows; ++row_idx) {
158
2/2
✓ Branch 0 taken 530 times.
✓ Branch 1 taken 36 times.
566 for (int col_idx = 0; col_idx < width; ++col_idx) {
159 530 const int output_idx = (row_idx * width) + col_idx;
160 const int clean_idx = (row_idx * width) + col_idx;
161 530 output[output_idx] = local_result_clean[clean_idx];
162 }
163 }
164
165 // Принимаем результаты от остальных процессов
166 int current_row = local_num_rows;
167
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (int src = 1; src < size_z; ++src) {
168
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const int src_num_rows = base_rows + (src < rem_rows ? 1 : 0);
169 6 const int recv_count = src_num_rows * width;
170 6 const auto offset = static_cast<ptrdiff_t>(current_row) * static_cast<ptrdiff_t>(width);
171 6 MPI_Recv(output.data() + offset, recv_count, MPI_UNSIGNED_CHAR, src, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
172 6 current_row += src_num_rows;
173 }
174 } else {
175 // Отправляем результат процессу 0
176 6 const int send_count = local_num_rows * width;
177 6 MPI_Send(local_result_clean.data(), send_count, MPI_UNSIGNED_CHAR, 0, 0, MPI_COMM_WORLD);
178 }
179 12 }
180
181 // Рассылка результата всем процессам
182 12 void BroadcastResult(int rank, int size_z, int width, int height, std::vector<uint8_t> &output) {
183
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
184 6 const int total_size = width * height;
185
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 for (int dest = 1; dest < size_z; ++dest) {
186 6 MPI_Send(output.data(), total_size, MPI_UNSIGNED_CHAR, dest, 1, MPI_COMM_WORLD);
187 }
188 } else {
189 6 const auto output_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(height);
190 6 output.resize(output_size);
191 6 const int total_size = width * height;
192 6 MPI_Recv(output.data(), total_size, MPI_UNSIGNED_CHAR, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
193 }
194 12 }
195
196 } // namespace
197
198
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 KorolevKSobelOpratorMPI::KorolevKSobelOpratorMPI(const InType &in) {
199 SetTypeOfTask(GetStaticTypeOfTask());
200 GetInput() = in;
201 GetOutput() = {};
202 12 }
203
204 12 bool KorolevKSobelOpratorMPI::ValidationImpl() {
205 const auto &input = GetInput();
206
3/6
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
12 if (input.width <= 0 || input.height <= 0 || input.channels <= 0) {
207 return false;
208 }
209 12 const auto expected_size = static_cast<std::size_t>(input.width) * static_cast<std::size_t>(input.height) *
210
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 static_cast<std::size_t>(input.channels);
211
1/2
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
12 if (input.pixels.size() != expected_size) {
212 return false;
213 }
214 12 return GetOutput().empty();
215 }
216
217 12 bool KorolevKSobelOpratorMPI::PreProcessingImpl() {
218 GetOutput() = {};
219 12 return true;
220 }
221
222 12 bool KorolevKSobelOpratorMPI::RunImpl() {
223 12 int rank = 0;
224 12 int size = 1;
225 12 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
226 12 MPI_Comm_size(MPI_COMM_WORLD, &size);
227
228 12 int width = 0;
229 12 int height = 0;
230 12 int channels = 0;
231 12 std::vector<uint8_t> all_pixels;
232
233 // Процесс 0 рассылает размеры изображения
234
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank == 0) {
235 const auto &input = GetInput();
236 6 width = input.width;
237 6 height = input.height;
238 6 channels = input.channels;
239
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 all_pixels = input.pixels;
240 }
241
242
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Bcast(&width, 1, MPI_INT, 0, MPI_COMM_WORLD);
243
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Bcast(&height, 1, MPI_INT, 0, MPI_COMM_WORLD);
244
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 MPI_Bcast(&channels, 1, MPI_INT, 0, MPI_COMM_WORLD);
245
246 // Если изображение слишком маленькое
247
2/4
✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 12 times.
12 if (width < 3 || height < 3) {
248 if (rank == 0) {
249 const auto output_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(height);
250 GetOutput() = std::vector<uint8_t>(output_size, 0);
251 } else {
252 GetOutput() = {};
253 }
254 return true;
255 }
256
257 // Распределяем строки между процессами
258 12 const int size_z = size;
259 12 const int base_rows = height / size_z;
260 12 const int rem_rows = height % size_z;
261
262
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 int local_start_row = (rank * base_rows) + std::min(rank, rem_rows);
263
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 int local_num_rows = base_rows + (rank < rem_rows ? 1 : 0);
264
265 // Каждому процессу нужна дополнительная строка сверху и снизу для свертки
266 int local_rows_with_borders = local_num_rows;
267
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank > 0) {
268 6 local_rows_with_borders++; // Верхняя граница
269 }
270
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if (rank < size_z - 1) {
271 6 local_rows_with_borders++; // Нижняя граница
272 }
273
274
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 int local_start_row_with_border = (rank > 0) ? local_start_row - 1 : local_start_row;
275
276 // Распределяем данные по процессам
277 12 const auto local_pixels_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(local_rows_with_borders) *
278 12 static_cast<std::size_t>(channels);
279
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 std::vector<uint8_t> local_pixels(local_pixels_size);
280
281
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 DistributeData(rank, size_z, width, channels, all_pixels, local_pixels, local_start_row_with_border,
282 local_rows_with_borders, base_rows, rem_rows);
283
284 // Конвертируем локальный блок в grayscale
285
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 std::vector<uint8_t> local_grayscale = ConvertToGrayscale(local_pixels, width, channels, 0, local_rows_with_borders);
286
287 // Применяем оператор Собеля к локальному блоку
288
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 std::vector<uint8_t> local_result = ApplySobelOperatorLocal(local_grayscale, width, local_rows_with_borders);
289
290 // Извлекаем только нужные строки (без граничных)
291 12 const auto clean_size = static_cast<std::size_t>(width) * static_cast<std::size_t>(local_num_rows);
292
1/4
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
12 std::vector<uint8_t> local_result_clean(clean_size);
293
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 const int offset = (rank > 0) ? 1 : 0;
294
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 12 times.
82 for (int row_idx = 0; row_idx < local_num_rows; ++row_idx) {
295
2/2
✓ Branch 0 taken 1050 times.
✓ Branch 1 taken 70 times.
1120 for (int col_idx = 0; col_idx < width; ++col_idx) {
296 1050 const int clean_idx = (row_idx * width) + col_idx;
297 1050 const int result_idx = ((row_idx + offset) * width) + col_idx;
298 1050 local_result_clean[clean_idx] = local_result[result_idx];
299 }
300 }
301
302 // Собираем результаты в процесс 0
303
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 GatherResults(rank, size_z, width, height, local_num_rows, base_rows, rem_rows, local_result_clean, GetOutput());
304
305 // Рассылаем результат всем процессам
306
1/2
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
12 BroadcastResult(rank, size_z, width, height, GetOutput());
307
308 return true;
309 }
310
311 12 bool KorolevKSobelOpratorMPI::PostProcessingImpl() {
312 12 return true;
313 }
314
315 } // namespace korolev_k_sobel_oprator
316