diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93393f7..1e2fb5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,4 +46,4 @@ jobs: path: | opencv build_opencv - key: ubuntu-x86-cross-build-opencv + key: ubuntu-x86-cross-build-opencv \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3fe1814..9141eee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,21 +2,46 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.25.0) project(computer_vision) -include_directories(./headers) - -set(HDR1 headers/Function.hpp) -set(SRC1 sources/Function.cpp) -add_library(func_algo1 ${HDR1} ${SRC1}) - -find_package(OpenMP) -if(OpenMP_FOUND) - message(STATUS "Link OpenMP") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") +include_directories(./generating_data/headers) +include_directories(./algorithms/headers) +include_directories(./tests/headers) +include_directories(./benchmarks/headers) + +set(ALGORITHMS_HDR algorithms/headers/function.hpp) +set(ALGORITHMS_SRC algorithms/sources/function.cpp) +add_library(func_algo ${ALGORITHMS_HDR} ${ALGORITHMS_SRC}) + +set(TIMER_HDR benchmarks/headers/timer.hpp) +set(TIMER_SRC benchmarks/sources/timer.cpp) +add_library(func_timer ${TIMER_HDR} ${TIMER_SRC}) + +add_executable(computer_vision main.cpp) + +#Link OpenMP +option(WITH_OPENCV2 "Build with OpenCV" OFF) +if(WITH_OPENCV2) + find_package(OpenCV REQUIRED) + if(OpenCV_FOUND) + message(STATUS "Link OpenCV") + include_directories( ${OpenCV_INCLUDE_DIRS} ) + add_compile_definitions(WITH_OPENCV2) + target_link_libraries(computer_vision ${OpenCV_LIBS}) + else() + message(STATUS "Not link OpenCV") + endif() +endif() + +# Link OpenCV +find_package(OpenCV REQUIRED) +if(OpenCV_FOUND) + message(STATUS "Link OpenCV") + include_directories( ${OpenCV_INCLUDE_DIRS} ) + add_compile_definitions(WITH_OPENCV2) + target_link_libraries(computer_vision ${OpenCV_LIBS}) else() - message(STATUS "Not link OpenMP") + message(STATUS "Not link OpenCV") endif() -add_executable(computer_vision main.cpp ./headers/Structer.hpp ./headers/data_render.hpp ./headers/Color.hpp ./headers/BMP.hpp) -target_link_libraries(computer_vision func_algo1) \ No newline at end of file +target_link_libraries(computer_vision func_algo) +target_link_libraries(computer_vision func_timer) + diff --git a/README.md b/README.md index f060ecb..85c4152 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ - Working with Raspberry PI devices in practice. ## Software modules: -### Data rendering +### Data generating A module for generating data/reading images and storing images. The Image class is independent when reading format images `.bmp` (`OpenCV` library is used for other formats). ### Algorithms The algorithms module. At the moment, it contains the following matrix multiplication algorithms: classical multiplication,multiplication using OpenMP and Neon_intrinsics diff --git a/algorithms/headers/filter.hpp b/algorithms/headers/filter.hpp new file mode 100644 index 0000000..afdc8c8 --- /dev/null +++ b/algorithms/headers/filter.hpp @@ -0,0 +1,136 @@ +#include +#include + +#include "structer.hpp" + +int interval(int val, int mmin, int mmax) { + int ans; + if (val < mmin) + ans = mmin; + else if (val > mmax) + ans = mmax; + else + ans = val; + + return ans; +} + +template +Image Filter(const Image& sourceImage); + +template +Color SobelX(const Image& im, int x, int y); + +template +Color SobelY(const Image& im, int x, int y); + +template +Image Filter(const Image& sourceImage) { + Image resIm(sourceImage.height, sourceImage.width); + + Image resImX(sourceImage.height, sourceImage.width); + for (int i = 0; i < resImX.height; i++) { + for (int j = 0; j < resImX.width; j++) { + resImX(i, j) = SobelX(sourceImage, i, j); + } + } + + Image resImY(sourceImage.height, sourceImage.width); + for (int i = 0; i < resImY.height; i++) { + for (int j = 0; j < resImY.width; j++) { + resImY(i, j) = SobelY(sourceImage, i, j); + } + } + + for (int i = 0; i < resIm.height; i++) { + for (int j = 0; j < resIm.width; j++) { + float resultR = sqrt(resImX(i, j).R() * resImX(i, j).R() + + resImY(i, j).R() * resImY(i, j).R()); + float resultG = sqrt(resImX(i, j).G() * resImX(i, j).G() + + resImY(i, j).G() * resImY(i, j).G()); + float resultB = sqrt(resImX(i, j).B() * resImX(i, j).B() + + resImY(i, j).B() * resImY(i, j).B()); + resIm(i, j).R() = interval((unsigned char)resultR, 0, 255); + resIm(i, j).G() = interval((unsigned char)resultG, 0, 255); + resIm(i, j).B() = interval((unsigned char)resultB, 0, 255); + } + } + + return resIm; +} + +template +Color SobelX(const Image& im, int x, int y) { + int radiusX = 1; + int radiusY = 1; + + Mtrx kernelX(3, 3); + kernelX(0, 0) = -1; + kernelX(0, 1) = 0; + kernelX(0, 2) = 1; + + kernelX(1, 0) = -2; + kernelX(1, 1) = 0; + kernelX(1, 2) = 2; + + kernelX(2, 0) = -1; + kernelX(2, 1) = 0; + kernelX(2, 2) = 1; + + float resultR = 0; + float resultG = 0; + float resultB = 0; + + for (int l = -radiusY; l <= radiusY; l++) { + for (int k = -radiusX; k <= radiusX; k++) { + int idX = interval(x + l, 0, im.height - 1); + int idY = interval(y + k, 0, im.width - 1); + Color neighborColor = im(idX, idY); + resultR += neighborColor.R() * kernelX(l + radiusX, k + radiusY); + resultG += neighborColor.G() * kernelX(l + radiusX, k + radiusY); + resultB += neighborColor.B() * kernelX(l + radiusX, k + radiusY); + } + } + + return Color(interval((unsigned char)resultR, 0, 255), + interval((unsigned char)resultG, 0, 255), + interval((unsigned char)resultB, 0, 255)); +} + +template +Color SobelY(const Image& im, int x, int y) { + int radiusX = 1; + int radiusY = 1; + + Mtrx kernelY(3, 3); + kernelY(0, 0) = -1; + kernelY(0, 1) = -2; + kernelY(0, 2) = -1; + + kernelY(1, 0) = 0; + kernelY(1, 1) = 0; + kernelY(1, 2) = 0; + + kernelY(2, 0) = 1; + kernelY(2, 1) = 2; + kernelY(2, 2) = 1; + + float resultR = 0; + float resultG = 0; + float resultB = 0; + + for (int l = -radiusY; l <= radiusY; l++) { + for (int k = -radiusX; k <= radiusX; k++) { + int idX = interval(x + l, 0, im.height - 1); + int idY = interval(y + k, 0, im.width - 1); + Color neighborColor = im(idX, idY); + resultR += neighborColor.R() * kernelY(l + radiusX, k + radiusY); + resultG += neighborColor.G() * kernelY(l + radiusX, k + radiusY); + resultB += neighborColor.B() * kernelY(l + radiusX, k + radiusY); + } + } + + return Color(interval((unsigned char)resultR, 0, 255), + interval((unsigned char)resultG, 0, 255), + interval((unsigned char)resultB, 0, 255)); +} diff --git a/headers/Function.hpp b/algorithms/headers/function.hpp similarity index 93% rename from headers/Function.hpp rename to algorithms/headers/function.hpp index 98d721c..a69ddcd 100644 --- a/headers/Function.hpp +++ b/algorithms/headers/function.hpp @@ -4,8 +4,8 @@ #include #include -#define cllps 3 -#define thr 4 +#define cllps 1 +#define thr 1 template T multiplication_omp(const T& data1, const T& data2, int N, int M, int L) { @@ -28,4 +28,4 @@ void matrix_multiply_4x4_neon_float(float32_t* A, float32_t* B, float32_t* C, void matrix_multiply_2x2_neon_float(float32_t* A, float32_t* B, float32_t* C, int N, int M, int L); -#endif \ No newline at end of file +#endif // _FUNCTION_ diff --git a/sources/Function.cpp b/algorithms/sources/function.cpp similarity index 99% rename from sources/Function.cpp rename to algorithms/sources/function.cpp index 8d18958..284ed99 100644 --- a/sources/Function.cpp +++ b/algorithms/sources/function.cpp @@ -1,4 +1,4 @@ -#include "Function.hpp" +#include "function.hpp" void matrix_multiply_4x4_neon_float(float32_t* A, float32_t* B, float32_t* C, int N, int M, int L) { @@ -59,12 +59,10 @@ void matrix_multiply_4x4_neon_float(float32_t* A, float32_t* B, float32_t* C, C1 = vfmaq_laneq_f32(C1, B1, A1, 1); C1 = vfmaq_laneq_f32(C1, B2, A1, 2); C1 = vfmaq_laneq_f32(C1, B3, A1, 3); - C2 = vfmaq_laneq_f32(C2, B0, A2, 0); C2 = vfmaq_laneq_f32(C2, B1, A2, 1); C2 = vfmaq_laneq_f32(C2, B2, A2, 2); C2 = vfmaq_laneq_f32(C2, B3, A2, 3); - C3 = vfmaq_laneq_f32(C3, B0, A3, 0); C3 = vfmaq_laneq_f32(C3, B1, A3, 1); C3 = vfmaq_laneq_f32(C3, B2, A3, 2); diff --git a/benchmarks/headers/benchmark.hpp b/benchmarks/headers/benchmark.hpp new file mode 100644 index 0000000..8c2ea6f --- /dev/null +++ b/benchmarks/headers/benchmark.hpp @@ -0,0 +1,71 @@ +#ifndef _BENCHMARK_ +#define _BENCHMARK_ + +#include + +#include +#include +#include +#include + +#include "timer.hpp" + +template +class Benchmark { + private: + int m_count; // number of iterations of the measurement + std::string m_name; // function benchmark name + std::function m_func; // pointer to the function to be measured + double exec_time; // arithmetic mean of function execution time (seconds) + public: + Benchmark(Func func, Arg... args); + + void setIterations(int count); // sets the number of benchmark iterations + void setName(const std::string& name); // sets the name of the benchmark + void run(); // launch benchmark + void info(); // output of benchmark information +}; + +template +Benchmark::Benchmark(Func func, Arg... args) + : m_count(1), m_name("Some function"), exec_time(-1) { + m_func = std::bind(func, args...); +} + +template +void Benchmark::setIterations(int count) { + m_count = count; +} + +template +void Benchmark::setName(const std::string& name) { + m_name = name; +} + +template +void Benchmark::run() { + try { + if (m_name == "Some function") throw(-1); + } catch (int except) { + std::cout << "Set the name of the function under test:" + ".setName(\"Function name\")" + << std::endl; + } + double total_time = 0; + for (int i = 0; i < m_count; i++) { + Timer t; + m_func(); + t.stop(); + total_time += t.duration_s(); + } + exec_time = total_time / m_count; +} + +template +void Benchmark::info() { + std::cout << " " << m_name << std::endl; + std::cout << "===========================" << std::endl; + std::cout << "Timer: " << exec_time << " seconds" << std::endl; +} + +#endif // _BENCHMARK_ diff --git a/benchmarks/headers/timer.hpp b/benchmarks/headers/timer.hpp new file mode 100644 index 0000000..11e205f --- /dev/null +++ b/benchmarks/headers/timer.hpp @@ -0,0 +1,26 @@ +#ifndef _TIMER_ +#define _TIMER_ + +#include + +#include +#include + +class Timer { + private: + bool strt; // timer start flag + bool stp; // timeк stop flag + std::chrono::time_point m_StartPoint{}; + std::chrono::duration duration; + + public: + Timer(); // сonstructor: initializes the timer duration to zero microseconds + ~Timer(); // returns a time measurement resource + void start(); // starts the timer + void stop(); // stops time + + double duration_s(); // Timer measurement duration in seconds + double duration_ms(); // Timer measurement duration in milliseconds +}; + +#endif // _TIMER_ diff --git a/benchmarks/sources/timer.cpp b/benchmarks/sources/timer.cpp new file mode 100644 index 0000000..53837c3 --- /dev/null +++ b/benchmarks/sources/timer.cpp @@ -0,0 +1,33 @@ +#include "timer.hpp" + +Timer::Timer() + : strt(true), stp(false), duration(std::chrono::microseconds{0}) { + start(); +} + +Timer::~Timer() { + if (strt && !stp) stop(); +} + +void Timer::start() { + strt = true; + stp = false; + m_StartPoint = std::chrono::high_resolution_clock::now(); +} /*-------------------------------------------------------------------------*/ + +void Timer::stop() { + stp = true; + std::chrono::time_point m_EndPoint = + std::chrono::high_resolution_clock::now(); + duration = m_EndPoint - m_StartPoint; +} /*-------------------------------------------------------------------------*/ + +double Timer::duration_s() { + if (!stp) std::cout << "Warning!!! The timer did not stop" << std::endl; + return duration.count(); +} /*-------------------------------------------------------------------------*/ + +double Timer::duration_ms() { + if (!stp) std::cout << "Warning!!! The timer did not stop" << std::endl; + return duration.count() * 1000.0; +} /*-------------------------------------------------------------------------*/ diff --git a/headers/BMP.hpp b/generating_data/headers/bmp.hpp similarity index 98% rename from headers/BMP.hpp rename to generating_data/headers/bmp.hpp index e63fa30..679636b 100644 --- a/headers/BMP.hpp +++ b/generating_data/headers/bmp.hpp @@ -59,4 +59,4 @@ void check_color_header(BMPColorHeader& bmp_color_header) { } } #pragma pack(pop) -#endif \ No newline at end of file +#endif // __BMP__ diff --git a/headers/Color.hpp b/generating_data/headers/color.hpp similarity index 99% rename from headers/Color.hpp rename to generating_data/headers/color.hpp index 38e75ee..f0aaa9a 100644 --- a/headers/Color.hpp +++ b/generating_data/headers/color.hpp @@ -168,4 +168,4 @@ std::ostream& operator<<(std::ostream& out, const Color& color_m) { out << (int)color_m.B() << ',' << (int)color_m.G() << ',' << (int)color_m.R(); return out; } -#endif \ No newline at end of file +#endif // _COLOR_ diff --git a/headers/data_render.hpp b/generating_data/headers/generating_data.hpp similarity index 92% rename from headers/data_render.hpp rename to generating_data/headers/generating_data.hpp index 5480ef0..e591ce6 100644 --- a/headers/data_render.hpp +++ b/generating_data/headers/generating_data.hpp @@ -13,4 +13,4 @@ void get_random(T& source, int size, int mmax = 256) { } } // namespace data_render -#endif \ No newline at end of file +#endif // _DATA_RENDER_ \ No newline at end of file diff --git a/headers/Structer.hpp b/generating_data/headers/structer.hpp similarity index 89% rename from headers/Structer.hpp rename to generating_data/headers/structer.hpp index 2712144..975cef4 100644 --- a/headers/Structer.hpp +++ b/generating_data/headers/structer.hpp @@ -1,8 +1,12 @@ #ifndef _MTRX_ #define _MTRX_ -#include "BMP.hpp" -#include "Color.hpp" +#include "bmp.hpp" +#include "color.hpp" + +#ifdef WITH_OPENCV2 +#include +#endif // WITH_OPENCV2 template class Mtrx { @@ -59,6 +63,11 @@ class Image : public Mtrx> { unsigned char* splitB(); // split component B unsigned char* getMemory(int a, int b); void readBMP(const char* fname); + +#ifdef WITH_OPENCV2 + void readPicture(const char* fname); // reading a three-channel bgr image, if + // Opencv is linked +#endif // WITH_OPENCV2 }; /// @@ -126,7 +135,6 @@ Mtrx& Mtrx::operator=(Mtrx&& mtrx_m) { template const ValType& Mtrx::operator[](int position_m) const { return data[position_m]; - } /*-------------------------------------------------------------------------*/ template @@ -148,7 +156,9 @@ template Mtrx Mtrx::operator*(const ValType& val) { Mtrx res(*this); for (int i = 0; i < height; i++) - for (int j = 0; j < width; j++) res[i * width + j] *= val; + for (int j = 0; j < width; j++) { + res[i * width + j] *= val; + } return res; } /*-------------------------------------------------------------------------*/ @@ -319,4 +329,29 @@ void Image::readBMP(const char* fname) { throw std::runtime_error("Unable to open the input image file"); } } -#endif \ No newline at end of file + +#ifdef WITH_OPENCV2 +template <> +void Image::readPicture(const char* fname) { + cv::Mat a = cv::imread(fname); + if (a.channels() == 3) { + unsigned char* mat_data = a.data; + delete[] data; + height = a.rows; + width = a.cols; + data = new Color[width * height]; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + data[i * width + j].B() = mat_data[(i * width + j) * 3]; + data[i * width + j].G() = mat_data[(i * width + j) * 3 + 1]; + data[i * width + j].R() = mat_data[(i * width + j) * 3 + 2]; + } + } + } else { + std::cout << "Warning!!! Picture can`t be read as it is not three-channel" + << std::endl; + } +} + +#endif // WITH_OPENCV2 +#endif // _MTRX_ diff --git a/main.cpp b/main.cpp index eb74910..18dbd46 100644 --- a/main.cpp +++ b/main.cpp @@ -1,14 +1,21 @@ +#include + #include -#include "BMP.hpp" -#include "Color.hpp" -#include "Function.hpp" -#include "Structer.hpp" -#include "data_render.hpp" +#include "benchmark.hpp" +#include "filter.hpp" +#include "function.hpp" +#include "structer.hpp" -int main() { - Image a(10, 10, Color(123, 124, 5)); - std::cout << a(9, 4); +using namespace cv; +int main() { + Image a(1, 1); + a.readPicture("clock.jpg"); + Image b = Filter(a); + b.writePicture("clockXY.jpg"); + Mat m = imread("clock.jpg", m1); + Sobel(m, m1, CV_32F, 1, 1); + imwrite("clockCV11.jpg", m1); return 0; -} \ No newline at end of file +}