diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9c36702..969ece8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -8,4 +8,4 @@ RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone RUN apt-get -y update && apt-get install -y # Install the Clang compiler -RUN apt-get -y install git g++ cmake python3 valgrind gcovr libsqlite3-dev +RUN apt-get -y install git g++ cmake python3 valgrind gcovr libsqlite3-dev libjpeg-dev diff --git a/.github/workflows/build_minimal_linux.yml b/.github/workflows/build_linux.yml similarity index 78% rename from .github/workflows/build_minimal_linux.yml rename to .github/workflows/build_linux.yml index 6f994cf..c58dc80 100644 --- a/.github/workflows/build_minimal_linux.yml +++ b/.github/workflows/build_linux.yml @@ -19,18 +19,18 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install packages - run: sudo apt -y install git g++ cmake libsqlite3-dev gcovr + run: sudo apt -y install git g++ cmake libsqlite3-dev gcovr libjpeg-dev - name: Clone aquamarine repo - run: git clone https://github.com/MaksymT17/aquamarine.git + run: git clone --branch ${{ github.head_ref }} https://github.com/MaksymT17/aquamarine.git - name: Build artifacts with Cmake run: | - cd aquamarine + cd aquamarine && ./build.sh - name: Run unit test run: | - cd aquamarine/unit_tests - ./prepare_build.sh + cd aquamarine/unit_tests && + ./prepare_build.sh && cd build && make -j8 && ./aquamarine_ut --gtest_output="xml" diff --git a/.github/workflows/build_minimal_windows.yml b/.github/workflows/build_windows.yml similarity index 55% rename from .github/workflows/build_minimal_windows.yml rename to .github/workflows/build_windows.yml index 30b7065..5cf0d6b 100644 --- a/.github/workflows/build_minimal_windows.yml +++ b/.github/workflows/build_windows.yml @@ -3,8 +3,6 @@ name: Windows build check on: push: branches: [ "master" ] - pull_request: - branches: [ "master" ] jobs: build-windows: @@ -13,14 +11,23 @@ jobs: steps: - uses: actions/checkout@v3 - name: Clone aquamarine repo - run: git clone https://github.com/MaksymT17/aquamarine.git + run: git clone --branch ${{ github.head_ref }} https://github.com/MaksymT17/aquamarine.git + + - name: Install packages + run: | + choco install sqlite --version=3.8.1 + choco install libjpeg-turbo + shell: bash + - name: Build artifacts with Cmake run: | cd aquamarine ./build.sh + shell: bash - name: Run unit tests run: | cd aquamarine/unit_tests ./prepare_build.sh - ./make_and_run_tests.sh \ No newline at end of file + ./make_and_run_tests.sh + shell: bash \ No newline at end of file diff --git a/AmApi.cpp b/AmApi.cpp index dc7b78a..c569d7c 100644 --- a/AmApi.cpp +++ b/AmApi.cpp @@ -4,6 +4,7 @@ #include "analyze/algorithm/ImagePair.h" #include +#include namespace am { using namespace common::types; diff --git a/AmApi.h b/AmApi.h index 7ec4fb7..21961bf 100644 --- a/AmApi.h +++ b/AmApi.h @@ -4,7 +4,7 @@ #include "extraction/BmpExtractor.h" #include "configuration/ConfigurationReader.hpp" #include "common/Logger.hpp" -#include "extraction/MultipleBmpExtractor.h" +#include "extraction/MultipleExtractor.h" #include "database/DataBaseCommunicator.h" namespace am @@ -21,7 +21,7 @@ namespace am private: std::shared_ptr loggerPtr; - extraction::MultipleBmpExtractor extractor; + extraction::MultipleExtractor extractor; std::unique_ptr detector; std::string base_img_path; diff --git a/CMakeLists.txt b/CMakeLists.txt index 22a31a2..bf0cf2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.6) +cmake_minimum_required (VERSION 3.0) project (aquamarine) @@ -11,8 +11,9 @@ add_compile_options(-fno-rtti) find_package (Threads) find_package (SQLite3) +find_package (JPEG REQUIRED) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${SQLite3_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${SQLite3_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR}) set(AM_SOURCE_FILES analyze/AffinityComparer.cpp @@ -26,15 +27,16 @@ set(AM_SOURCE_FILES analyze/algorithm/ImagePair.cpp analyze/algorithm/movement/MovementDetector.cpp extraction/BmpExtractor.cpp - extraction/MultipleBmpExtractor.cpp + extraction/JpgExtractor.cpp + extraction/MultipleExtractor.cpp common/Timers.hpp database/DataBaseCommunicator.cpp AmApi.cpp ) -add_library(aquamarine_lib STATIC ${AM_SOURCE_FILES} ) +add_library(aquamarine_lib STATIC ${AM_SOURCE_FILES} ${JPEG_LIBRARIES}) add_executable(aquamarine main.cpp) -target_link_libraries (aquamarine aquamarine_lib ${CMAKE_THREAD_LIBS_INIT} ${SQLite3_LIBRARIES}) +target_link_libraries (aquamarine aquamarine_lib ${CMAKE_THREAD_LIBS_INIT} ${SQLite3_LIBRARIES} ${JPEG_LIBRARIES} ) diff --git a/analyze/algorithm/ObjectBase.h b/analyze/algorithm/ObjectBase.h index d6f362d..bac36b7 100644 --- a/analyze/algorithm/ObjectBase.h +++ b/analyze/algorithm/ObjectBase.h @@ -7,7 +7,7 @@ namespace am::analyze::algorithm class ObjectBase { public: - explicit ObjectBase(const size_t row, const size_t col) : mPixelsCount(1), + explicit ObjectBase(const size_t row, const size_t col) : mPixelsCount(0), mLeft(col), mMin_height(row), mRight(col), diff --git a/analyze/algorithm/ObjectRectangle.cpp b/analyze/algorithm/ObjectRectangle.cpp index 9927500..d3f33ea 100644 --- a/analyze/algorithm/ObjectRectangle.cpp +++ b/analyze/algorithm/ObjectRectangle.cpp @@ -5,6 +5,7 @@ namespace am::analyze::algorithm ObjectRectangle::ObjectRectangle(const size_t row, const size_t col) : ObjectBase(row, col) { + addPixel(row,col); } void ObjectRectangle::addPixel(const size_t row, const size_t col) diff --git a/common/exceptions/WrongFormatException.hpp b/common/exceptions/WrongFormatException.hpp new file mode 100644 index 0000000..25b1937 --- /dev/null +++ b/common/exceptions/WrongFormatException.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "common/exceptions/AmException.hpp" + +namespace am::common::exceptions +{ + class WrongFormatException : public AmException + { + public: + WrongFormatException(std::string &msg) : AmException(msg) {} + }; +} // namespace am::common::exceptions diff --git a/extraction/IMultipleBmpExtractor.h b/extraction/IMultipleExtractor.h similarity index 82% rename from extraction/IMultipleBmpExtractor.h rename to extraction/IMultipleExtractor.h index 66f25c3..15dd1d1 100644 --- a/extraction/IMultipleBmpExtractor.h +++ b/extraction/IMultipleExtractor.h @@ -8,10 +8,10 @@ namespace am::extraction { - class IMultipleBmpExtractor + class IMultipleExtractor { public: - ~IMultipleBmpExtractor() = default; + ~IMultipleExtractor() = default; virtual std::vector> readFiles(std::vector &&fileNames) = 0; }; diff --git a/extraction/JpgExtractor.cpp b/extraction/JpgExtractor.cpp new file mode 100644 index 0000000..ab760e7 --- /dev/null +++ b/extraction/JpgExtractor.cpp @@ -0,0 +1,51 @@ + +#include "JpgExtractor.h" +#include "common/exceptions/FileAccessException.hpp" +#include "common/exceptions/AllocationException.hpp" +#include +#include +#include + +namespace am::extraction +{ + using namespace common::types; + + Matrix JpgExtractor::readFile(const std::string &fileName) + { + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + + FILE *infile = fopen(fileName.c_str(), "rb"); + if (!infile) + { + std::string errMsg = "File '" + fileName + "' could not be found!"; + throw common::exceptions::FileAccessException(errMsg); + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + JSAMPLE* tempdata = new JSAMPLE[cinfo.output_width * cinfo.output_components]; + Matrix data(cinfo.output_width, cinfo.output_height); + size_t rowid =0; + while (cinfo.output_scanline < cinfo.output_height) + { + jpeg_read_scanlines(&cinfo, &tempdata, 1); + for (int j = 0; j < cinfo.output_width; j++) + { + //printf("[%d]c:%d_%d_%d ", j, tempdata[j*3],tempdata[j*3+1], tempdata[j*3+2] ); + data(rowid, j) = {tempdata[j*cinfo.output_components],tempdata[j*cinfo.output_components+1], tempdata[j*cinfo.output_components+2]}; + } + //printf("\n"); + rowid++; + } + delete tempdata; + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(infile); + return data; + } +} // namespace am::extraction diff --git a/extraction/JpgExtractor.h b/extraction/JpgExtractor.h new file mode 100644 index 0000000..d4c61b4 --- /dev/null +++ b/extraction/JpgExtractor.h @@ -0,0 +1,19 @@ +#pragma once + +#include "common/types/Matrix.hpp" +#include "common/types/Color24b.hpp" +#include + +namespace am::extraction +{ + /// JpgExtractor class for RGB data extraction from JPG/JPEG file + class JpgExtractor + { + public: + JpgExtractor() = default; + ~JpgExtractor() = default; + + static common::types::Matrix readFile(const std::string &filePath); + }; + +} \ No newline at end of file diff --git a/extraction/MultipleBmpExtractor.cpp b/extraction/MultipleBmpExtractor.cpp deleted file mode 100644 index bf5b1c2..0000000 --- a/extraction/MultipleBmpExtractor.cpp +++ /dev/null @@ -1,34 +0,0 @@ - -#include "MultipleBmpExtractor.h" -#include -#include "BmpExtractor.h" -#include "common/types/Color24b.hpp" - -namespace am::extraction -{ - using namespace common::types; - - MultipleBmpExtractor::MultipleBmpExtractor(std::shared_ptr &logger) - : mLogger(logger) - { - } - - std::vector> MultipleBmpExtractor::readFiles(std::vector &&fileNames) - { - std::vector> result; - result.reserve(fileNames.size()); - std::vector>> futures; - - for (size_t i = 0; i < fileNames.size(); ++i) - { - futures.emplace_back(std::async(std::launch::async, BmpExtractor::readFile, fileNames[i])); - mLogger->info("Reading of file:%s has been added in extraction queue.", fileNames[i].c_str()); - } - - for (auto &e : futures) - { - result.emplace_back(e.get()); - } - return result; - } -} // namespace am::extraction diff --git a/extraction/MultipleExtractor.cpp b/extraction/MultipleExtractor.cpp new file mode 100644 index 0000000..6095b6f --- /dev/null +++ b/extraction/MultipleExtractor.cpp @@ -0,0 +1,55 @@ + +#include "MultipleExtractor.h" +#include +#include "BmpExtractor.h" +#include "JpgExtractor.h" +#include "common/types/Color24b.hpp" + +#include +#include "common/exceptions/WrongFormatException.hpp" + +namespace am +{ + namespace extraction + { + using namespace common::types; + + MultipleExtractor::MultipleExtractor(std::shared_ptr &logger) + : mLogger(logger) + { + } + + std::vector> MultipleExtractor::readFiles(std::vector &&fileNames) + { + std::vector> result; + result.reserve(fileNames.size()); + std::vector>> futures; + + for (size_t i = 0; i < fileNames.size(); ++i) + { + std::string file_ext = fileNames[i].substr(fileNames[i].find_last_of(".") + 1); + std::transform(file_ext.begin(), file_ext.end(), file_ext.begin(), + [](unsigned char c) + { return std::tolower(c); }); + if(file_ext =="jpg" || file_ext =="jpeg" || file_ext =="jpe") + futures.emplace_back(std::async(std::launch::deferred, JpgExtractor::readFile, fileNames[i])); + else if(file_ext =="bmp") + futures.emplace_back(std::async(std::launch::deferred, BmpExtractor::readFile, fileNames[i])); + else{ + std::string errorMsg("WrongFormatException on data extraction from file(allowed jpeg/bmp)! File: "); + errorMsg.append(fileNames[i]); + throw am::common::exceptions::WrongFormatException(errorMsg); + } + + + mLogger->info("Reading of file:%s has been added in extraction queue.", fileNames[i].c_str()); + } + + for (auto &e : futures) + { + result.emplace_back(e.get()); + } + return result; + } + } +} \ No newline at end of file diff --git a/extraction/MultipleBmpExtractor.h b/extraction/MultipleExtractor.h similarity index 72% rename from extraction/MultipleBmpExtractor.h rename to extraction/MultipleExtractor.h index a2aeb31..b0ed8e7 100644 --- a/extraction/MultipleBmpExtractor.h +++ b/extraction/MultipleExtractor.h @@ -4,18 +4,19 @@ #include "common/types/Matrix.hpp" #include "common/types/Color24b.hpp" #include "common/Logger.hpp" -#include "IMultipleBmpExtractor.h" +#include "IMultipleExtractor.h" namespace am::extraction { // class for multiple reading files, given vector of fieNames will be // fullfilled as return value with relative data from files // Usage of async calls can reduce extraction time. - class MultipleBmpExtractor : public IMultipleBmpExtractor + class MultipleExtractor : public IMultipleExtractor { + public: - explicit MultipleBmpExtractor(std::shared_ptr &logger); - ~MultipleBmpExtractor() = default; + MultipleExtractor(std::shared_ptr &logger); + ~MultipleExtractor() = default; // fill up the Matrices for each file provided in the input parameter virtual std::vector> readFiles(std::vector &&fileNames) override; @@ -23,4 +24,4 @@ namespace am::extraction private: std::shared_ptr mLogger; }; -} // namespace am::extraction +} diff --git a/inputs/10x10_2obj.jpg b/inputs/10x10_2obj.jpg new file mode 100644 index 0000000..950ca5d Binary files /dev/null and b/inputs/10x10_2obj.jpg differ diff --git a/inputs/10x10_clean.jpg b/inputs/10x10_clean.jpg new file mode 100644 index 0000000..66c1de5 Binary files /dev/null and b/inputs/10x10_clean.jpg differ diff --git a/inputs/_DSC4097.JPG b/inputs/_DSC4097.JPG new file mode 100755 index 0000000..6fbe07a Binary files /dev/null and b/inputs/_DSC4097.JPG differ diff --git a/inputs/_DSC4098.JPG b/inputs/_DSC4098.JPG new file mode 100755 index 0000000..0415d43 Binary files /dev/null and b/inputs/_DSC4098.JPG differ diff --git a/inputs/configuration.csv b/inputs/configuration.csv index b11b586..1d2984c 100644 --- a/inputs/configuration.csv +++ b/inputs/configuration.csv @@ -1,6 +1,6 @@ Affinity_Threshold: 75 -Minimum_Pixels_In_Object: 3 -Pixel_Step: 2 +Minimum_Pixels_In_Object: 1 +Pixel_Step: 1 Calculation_Time_Limit: 50 Idle_Timeout: 5 Threads_Multiplier: 10.0 diff --git a/main.cpp b/main.cpp index 817a541..2e324e8 100644 --- a/main.cpp +++ b/main.cpp @@ -22,8 +22,8 @@ int main(int argc, char *argv[]) else { std::cout << "Use default parameters" << std::endl; - base_img_path = "inputs/rs_1.bmp"; - cmp_img_path = "inputs/rs_2.bmp"; + base_img_path = "inputs/10x10_clean.jpg"; + cmp_img_path = "inputs/10x10_2obj.jpg"; conf_path = "inputs/configuration.csv"; out_img_path = "compare_result.bmp"; } @@ -39,7 +39,13 @@ int main(int argc, char *argv[]) rect.getRight(), rect.getPixelsCount()); }*/ amApi.enable_database_reports("results.db"); - amApi.compare_and_save_diff_img(base_img_path, cmp_img_path, out_img_path); - + const auto rects = amApi.compare(base_img_path, cmp_img_path); + for (auto &rect : rects) + { + printf("row:%zd col:%zd row:%zd col:%zd value:%zd\n", + rect.getMinHeight(), rect.getLeft(), rect.getMaxHeight(), + rect.getRight(), rect.getPixelsCount()); + } + printf("rects %zu\n", rects.size()); return 0; } \ No newline at end of file diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 9ea5766..cedf1c9 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -15,6 +15,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O1 -fprofile-arcs -ftest-cov find_package (Threads) find_package (SQLite3) +find_package (JPEG REQUIRED) # Set the CMAKE_CXX_STANDARD variable to 17 set(CMAKE_CXX_STANDARD 17) @@ -22,7 +23,7 @@ set(CMAKE_CXX_STANDARD 17) # Set the CMAKE_CXX_STANDARD_REQUIRED variable to ON set(CMAKE_CXX_STANDARD_REQUIRED ON) -include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${AM_INC} ${SQLite3_INCLUDE_DIRS}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${AM_INC} ${SQLite3_INCLUDE_DIRS} ${JPEG_INCLUDE_DIR}) #fetch gtest library include(FetchContent) @@ -38,6 +39,7 @@ file(GLOB UT_SOURCES "*.cpp") add_executable(aquamarine_ut ${AM_SRC}/extraction/BmpExtractor.cpp + ${AM_SRC}/extraction/JpgExtractor.cpp ${AM_SRC}/analyze/algorithm/movement/MovementDetector.cpp ${AM_SRC}/analyze/algorithm/ImagePair.cpp ${AM_SRC}/analyze/algorithm/BfsObjectDetector.cpp @@ -48,11 +50,11 @@ add_executable(aquamarine_ut ${AM_SRC}/analyze/algorithm/ObjectRectangle.cpp ${AM_SRC}/analyze/algorithm/ObjectDetector.cpp ${AM_SRC}/analyze/algorithm/DiffObjectDetector.cpp - ${AM_SRC}/extraction/MultipleBmpExtractor.cpp + ${AM_SRC}/extraction/MultipleExtractor.cpp ${AM_SRC}/database/DataBaseCommunicator.cpp ${UT_SOURCES} ) -target_link_libraries(aquamarine_ut gtest_main ${CMAKE_THREAD_LIBS_INIT} ${SQLite3_LIBRARIES}) +target_link_libraries(aquamarine_ut gtest_main ${CMAKE_THREAD_LIBS_INIT} ${SQLite3_LIBRARIES} ${JPEG_LIBRARIES}) # gcov - coverage set(CMAKE_COMPILER_IS_GNUCXX true) diff --git a/unit_tests/ObjectDetectorTest.cpp b/unit_tests/ObjectDetectorTest.cpp index 154373b..117fec6 100644 --- a/unit_tests/ObjectDetectorTest.cpp +++ b/unit_tests/ObjectDetectorTest.cpp @@ -2,7 +2,7 @@ #include "gtest/gtest.h" #include "analyze/AffinityComparer.h" #include -#include "extraction/MultipleBmpExtractor.h" +#include "extraction/MultipleExtractor.h" #include "extraction/BmpExtractor.h" #include "analyze/ThresholdDiffChecker.h" #include "analyze/algorithm/BfsObjectDetector.h" @@ -29,7 +29,7 @@ struct ObjectDetectorWrapper : public ::testing::Test void SetUp() override { loggerPtr = std::make_shared("od_dummy_log.log"); - extractor = std::make_unique(loggerPtr); + extractor = std::make_unique(loggerPtr); } void TearDown() override @@ -37,7 +37,7 @@ struct ObjectDetectorWrapper : public ::testing::Test } std::shared_ptr loggerPtr; - std::unique_ptr extractor; + std::unique_ptr extractor; const size_t opt_threads = am::common::getOptimalThreadsCount(); am::configuration::ConfigurationReader reader; std::shared_ptr conf = reader.getConfigurationFromFile("inputs/configuration.csv"); diff --git a/unit_tests/make_and_run_tests.sh b/unit_tests/make_and_run_tests.sh index a10c6b4..1018f18 100755 --- a/unit_tests/make_and_run_tests.sh +++ b/unit_tests/make_and_run_tests.sh @@ -4,7 +4,7 @@ cd build && make -j8 && echo "UNIT TESTS SCRIPT: Binaries ready, starting execution..." && -./aquamarine_ut > am_test_result.txt && +./aquamarine_ut && echo "UNIT TESTS SCRIPT: Execution of tests finished." && if [ $(dpkg-query -W -f='${Status}' valgrind 2>/dev/null | grep -c "ok installed") -eq 0 ]; then