Skip to content
Open
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
23 changes: 23 additions & 0 deletions .clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
BasedOnStyle: LLVM
Language: Cpp

IndentWidth: 2
ContinuationIndentWidth: 2
TabWidth: 2
UseTab: Never

ColumnLimit: 80
IndentPPDirectives: None
AlignEscapedNewlines: Left
AlignConsecutiveMacros: None

BinPackArguments: false
BinPackParameters: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false

BreakBeforeBraces: Attach
PointerAlignment: Right
SortIncludes: Never
ReflowComments: false
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/.cache
/build
__pycache__/
*.pyc
/benchmarks/processed_outputs
/benchmarks/pipeline_outputs
/benchmarks/results
131 changes: 131 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
cmake_minimum_required(VERSION 3.20)

project(ImageConversation LANGUAGES C)

set(CMAKE_C_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

include(CTest)
include(FetchContent)

option(IMAGE_CONVERSATION_ENABLE_OPENCV
"Fetch the legacy OpenCV C API target"
OFF)

file(GLOB_RECURSE FILTER_SOURCES CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/filters/*.c"
)

file(GLOB_RECURSE CONVOLUTION_RUNTIME_SOURCES CONFIGURE_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/src/sequentially_convolution/*.c"
"${CMAKE_CURRENT_SOURCE_DIR}/src/image_helpers/*.c"
)

set(BUILD_TESTS OFF CACHE BOOL "")
set(BUILD_PERF_TESTS OFF CACHE BOOL "")
set(BUILD_EXAMPLES OFF CACHE BOOL "")
set(BUILD_DOCS OFF CACHE BOOL "")
set(BUILD_opencv_apps OFF CACHE BOOL "")

set(BUILD_opencv_core ON CACHE BOOL "")
set(BUILD_opencv_imgproc ON CACHE BOOL "")
set(BUILD_opencv_highgui ON CACHE BOOL "")

set(BUILD_opencv_ts OFF CACHE BOOL "")
set(BUILD_opencv_calib3d OFF CACHE BOOL "")
set(BUILD_opencv_contrib OFF CACHE BOOL "")
set(BUILD_opencv_features2d OFF CACHE BOOL "")
set(BUILD_opencv_flann OFF CACHE BOOL "")
set(BUILD_opencv_gpu OFF CACHE BOOL "")
set(BUILD_opencv_legacy OFF CACHE BOOL "")
set(BUILD_opencv_ml OFF CACHE BOOL "")
set(BUILD_opencv_nonfree OFF CACHE BOOL "")
set(BUILD_opencv_objdetect OFF CACHE BOOL "")
set(BUILD_opencv_ocl OFF CACHE BOOL "")
set(BUILD_opencv_photo OFF CACHE BOOL "")
set(BUILD_opencv_stitching OFF CACHE BOOL "")
set(BUILD_opencv_superres OFF CACHE BOOL "")
set(BUILD_opencv_video OFF CACHE BOOL "")
set(BUILD_opencv_videostab OFF CACHE BOOL "")
set(BUILD_opencv_world OFF CACHE BOOL "")

set(WITH_QT OFF CACHE BOOL "")
set(WITH_GTK OFF CACHE BOOL "")
set(WITH_OPENGL OFF CACHE BOOL "")
set(WITH_FFMPEG OFF CACHE BOOL "")
set(WITH_GSTREAMER OFF CACHE BOOL "")
set(WITH_V4L OFF CACHE BOOL "")
set(WITH_1394 OFF CACHE BOOL "")
set(WITH_CUDA OFF CACHE BOOL "")
set(WITH_OPENCL OFF CACHE BOOL "")
set(WITH_TBB OFF CACHE BOOL "")
set(WITH_IPP OFF CACHE BOOL "")
set(WITH_TIFF OFF CACHE BOOL "")
set(WITH_JASPER OFF CACHE BOOL "")
set(WITH_OPENEXR OFF CACHE BOOL "")

FetchContent_Declare(
opencv
GIT_REPOSITORY https://github.com/opencv/opencv
GIT_TAG 2.4.13.6
GIT_SHALLOW TRUE
)

FetchContent_MakeAvailable(opencv)

add_library(opencv_legacy_c_api INTERFACE)
target_include_directories(opencv_legacy_c_api
INTERFACE
${opencv_SOURCE_DIR}/modules/core/include
${opencv_SOURCE_DIR}/modules/imgproc/include
${opencv_SOURCE_DIR}/modules/highgui/include
)
target_link_libraries(opencv_legacy_c_api
INTERFACE
opencv_core
opencv_imgproc
opencv_highgui
)

add_library(filters_core STATIC
${FILTER_SOURCES}
)
add_library(filters ALIAS filters_core)

target_include_directories(filters_core
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
)

add_library(convolution_runtime STATIC
${CONVOLUTION_RUNTIME_SOURCES}
)
target_include_directories(convolution_runtime
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/src
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/internal
)
target_link_libraries(convolution_runtime
PUBLIC
filters_core
opencv_legacy_c_api
)

add_executable(app
src/app.c
src/cli_args.c
)
target_link_libraries(app
PRIVATE
convolution_runtime
)

if(BUILD_TESTING)
find_package(PkgConfig REQUIRED)
pkg_check_modules(CMOCKA REQUIRED IMPORTED_TARGET cmocka)

add_subdirectory(test/sequential_tests)
endif()
35 changes: 29 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,32 @@
# Image Conversation
# Image Conversation — Sequential Convolution

Основная ветка намеренно оставлена минимальной.
Ветка содержит первую задачу: последовательную свёртку одного изображения.

Реализации задач разнесены по отдельным веткам:
В составе ветки оставлены фильтры, загрузка/сохранение изображения, CLI, тестовые изображения и тесты последовательной реализации. Параллельная свёртка, thread pool и pipeline здесь намеренно отсутствуют, чтобы diff относительно `main` показывал только первую задачу.

- `feat/sequential_convolution` — последовательная свёртка.
- `feat/parallel_convolution` — параллельная свёртка поверх последовательной реализации.
- `feat/image_pipepline` — pipeline обработки изображений поверх параллельной реализации.
## Сборка

```bash
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF
cmake --build build --target app -j
```

## Пример запуска

```bash
./build/app -i input/sea.png -o output/sea.png -f gauss -h 5 -w 5 -s
```

## Посмотреть параметры запуска

```bash
./build/app --help
```

## Тесты

```bash
cmake -S . -B build-tests -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON
cmake --build build-tests -j
ctest --test-dir build-tests --output-on-failure
```
29 changes: 29 additions & 0 deletions docs/task1/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Бенчмарк времени обработки изображений

![График зависимости времени обработки от размера изображения](benchmark.png)

На графике показана зависимость времени обработки от размера изображения для композиции фильтров `motion_blur 9x9 + gauss 5x5`. Использовались четыре изображения: `satoru`, `stariy_bog`, `musashi`, `sea`.

По горизонтали отложен размер изображения, по вертикали - среднее время обработки в `мс`. Чем выше точка на графике, тем дольше обрабатывалось изображение. Оранжевые интервалы показывают погрешность среднего значения времени.

В каждом измерении учитывался полный цикл обработки одного изображения:

1. чтение изображения;
2. применение `motion_blur 9x9`;
3. применение `gauss 5x5`;
4. запись результата.

Для каждого изображения выполнено по 10 измерений. В таблице указано среднее время полного цикла обработки и погрешность.

Погрешность на графике и в таблице - половина 95% доверительного интервала среднего значения времени:

`error = t_0.975,9 * s / sqrt(n)`, где `n = 10`, `t_0.975,9 = 2.262`, `s` - выборочное стандартное отклонение 10 значений времени.

| Изображение | Размер | Время, мс |
|---|---:|---:|
| `satoru` | `225x225` | `44.66 +- 0.58` |
| `stariy_bog` | `914x480` | `383.81 +- 0.40` |
| `musashi` | `2560x1440` | `3188.37 +- 9.60` |
| `sea` | `3840x2160` | `7246.61 +- 10.61` |

Время обработки растёт вместе с размером изображения: минимальное среднее время получилось на `satoru` размером `225x225`, максимальное - на `sea` размером `3840x2160`.
Binary file added docs/task1/benchmark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added input/musashi.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added input/satoru.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added input/sea.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added input/stariy_bog.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added input/sunshine.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions internal/sequentially_convolution/helper_functions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include <stddef.h>

#include <filters/filter.h>

static inline unsigned char clamp_to_u8(double value) {
if (value < 0.0) {
return 0U;
}
if (value > 255.0) {
return 255U;
}
return (unsigned char)(value + 0.5);
}

static inline size_t wrap_index(long value, size_t limit) {
long wrapped = value % (long)limit;
if (wrapped < 0) {
wrapped += (long)limit;
}
return (size_t)wrapped;
}

static inline size_t clamp_index(long value, size_t limit) {
if (value < 0) {
return 0U;
}
if ((size_t)value >= limit) {
return limit - 1U;
}
return (size_t)value;
}

static inline size_t reflect_index(long value, size_t limit) {
if (limit <= 1U) {
return 0U;
}

const long period = 2L * ((long)limit - 1L);
long reflected = value % period;

if (reflected < 0) {
reflected += period;
}
if (reflected >= (long)limit) {
reflected = period - reflected;
}

return (size_t)reflected;
}

static inline size_t
resolve_index(long value, size_t limit, filter_border_mode_t border_mode) {
switch (border_mode) {
case FILTER_BORDER_CLAMP:
return clamp_index(value, limit);
case FILTER_BORDER_REFLECT:
return reflect_index(value, limit);
case FILTER_BORDER_WRAP:
default:
return wrap_index(value, limit);
}
}
Empty file added output/.gitkeep
Empty file.
81 changes: 81 additions & 0 deletions src/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#include "cli_args.h"

#include <filters/filter.h>
#include <sequentially_convolution/sequentially_convolution.h>

#include <stdio.h>

#include <opencv2/highgui/highgui_c.h>
#include <opencv2/core/core_c.h>

#define MAX_ERROR_MESSAGE_LENGTH 32

static int apply_filters(const cli_request_t *request,
image_view_t *image_view) {
for (size_t i = 0; i < request->filter_count; ++i) {
filter_t filter;
filter_request_t filter_request = {
.kind = request->filters[i].kind,
.width = request->filters[i].width,
.height = request->filters[i].height,
.direction = request->filters[i].direction,
.border_mode = request->filters[i].border_mode,
};

if (filter_init_builtin(&filter, &filter_request) != FILTER_STATUS_OK) {
return -1;
}

if (!filter_is_convolution(&filter) ||
sequential_convolution(&filter, image_view) != 0) {
return -1;
}
}

return 0;
}

int main(int argc, char **argv) {
cli_request_t request;
char error_message[MAX_ERROR_MESSAGE_LENGTH];
cli_parse_status_t parse_status =
cli_parse_args(argc, argv, &request, error_message, sizeof(error_message));

if (parse_status == CLI_PARSE_HELP) {
return 0;
}

if (parse_status != CLI_PARSE_OK) {
puts(error_message);
return -1;
}

IplImage *image = cvLoadImage(request.input_path, CV_LOAD_IMAGE_UNCHANGED);
if (image == NULL) {
fprintf(stderr, "failed to load image: %s\n", request.input_path);
return -1;
}

image_view_t image_view = {
.data = (unsigned char *)image->imageData,
.height = (size_t)image->height,
.width = (size_t)image->width,
.stride = (size_t)image->widthStep,
.channels = (size_t)image->nChannels,
};

if (apply_filters(&request, &image_view) != 0) {
cvReleaseImage(&image);
fputs("failed to apply filters\n", stderr);
return -1;
}

if (!cvSaveImage(request.output_path, image, NULL)) {
cvReleaseImage(&image);
fprintf(stderr, "failed to save image: %s\n", request.output_path);
return -1;
}

cvReleaseImage(&image);
return 0;
}
Loading
Loading