From c34bdaf6ec73d80c09c40572cb608d4354e54fbe Mon Sep 17 00:00:00 2001 From: PaSawi Date: Thu, 3 Aug 2023 14:06:16 +0200 Subject: [PATCH] changed name of directory --- homework/grayscale-image/CMakeLists.txt | 27 ++++ homework/grayscale-image/README.md | 142 +++++++++++++++++ homework/grayscale-image/compression.cpp | 109 +++++++++++++ homework/grayscale-image/compression.hpp | 10 ++ homework/grayscale-image/main.cpp | 69 ++++++++ homework/grayscale-image/ninja.pgm | 36 +++++ homework/grayscale-image/ninja.png | Bin 0 -> 28514 bytes homework/grayscale-image/test.cpp | 191 +++++++++++++++++++++++ 8 files changed, 584 insertions(+) create mode 100644 homework/grayscale-image/CMakeLists.txt create mode 100644 homework/grayscale-image/README.md create mode 100644 homework/grayscale-image/compression.cpp create mode 100644 homework/grayscale-image/compression.hpp create mode 100644 homework/grayscale-image/main.cpp create mode 100644 homework/grayscale-image/ninja.pgm create mode 100644 homework/grayscale-image/ninja.png create mode 100644 homework/grayscale-image/test.cpp diff --git a/homework/grayscale-image/CMakeLists.txt b/homework/grayscale-image/CMakeLists.txt new file mode 100644 index 000000000..1018a5e7a --- /dev/null +++ b/homework/grayscale-image/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.14.0) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG main # release-1.10.0 +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +project(grayscaleImages) +enable_testing() + +add_executable(${PROJECT_NAME} main.cpp compression.cpp) +add_executable(${PROJECT_NAME}-ut test.cpp compression.cpp) + +add_compile_options(${PROJECT_NAME} -Wall -Wextra -Wconversion -pedantic -Werror) + +target_link_libraries(${PROJECT_NAME}-ut gtest_main) + +include(GoogleTest) +gtest_discover_tests(${PROJECT_NAME}-ut) diff --git a/homework/grayscale-image/README.md b/homework/grayscale-image/README.md new file mode 100644 index 000000000..a44bb795c --- /dev/null +++ b/homework/grayscale-image/README.md @@ -0,0 +1,142 @@ +# `compressGrayscale()` && `decompressGrayscale()` + +## `compressGrayscale()` + +Zadaniem będzie kompresja obrazka w odcieniach szarości o wymiarach 240x160 pikseli. Każdy piksel może mieć wartość od 0 (kolor czarny) do 255 (kolor biały). Im większa wartość tym jaśniejszy odcień piksel reprezentuje. Przykład małego obrazka o rozmiarach 6x4 piksele: + +Przykład pikseli + +```cpp +255 255 0 255 0 255 // 0xFF 0xFF 0x00 0xFF 0x00 0xFF +128 0 128 0 128 0 // 0x80 0x00 0x80 0x00 0x80 0x00 +64 64 64 64 64 64 // 0x40 0x40 0x40 0x40 0x40 0x40 +255 192 128 64 0 0 // 0xFF 0xB0 0x80 0x40 0x00 0x00 +``` + +Aby otrzymać z tego plik w formacie PGM wystarczy tylko dodać mu odpowiedni nagłówek. + +___ + + +## `compressGrayscale()` - opis + +Napisz funkcję `compressGrayscale()`. +Powinna ona przyjąć jeden argument typu `std::array, 160>` określający rozkład odcieni szarości na obrazku 2D (który w dalszej części nazywać będziemy bitmapą) i zwróci `std::vector>` zawierający skompresowaną bitmapę. + +Kompresja powinna przebiegać w następujący sposób: + +* Bitmapę rysujemy od górnego lewego rogu przechodząc w prawo, następnie poziom niżej. +* Jeżeli obok siebie występuje ten sam kolor więcej niż 1 raz, funkcja powinna wrzucić do `std::vector<>` wartość tego koloru (liczba z przedziału 0 – 255) jako pierwszy element pary oraz ilość jego powtórzeń jako drugi element pary. +* Jeżeli obok siebie występują różne odcienie to funkcja powinna wypełnić `std::vector<> ` wartością odcienia oraz liczbą wystąpień równą 1 (w tym przypadku pogarszamy optymalizację, gdyż przechowujemy 2x tyle danych, jednak najczęściej te same kolory są położone obok siebie). + +___ + + +## `compressGrayscale()` - przykład + +```cpp +input: {{0 0 0 1 1 2 3 0 0 0}, + {0 0 4 4 4 1 1 1 1 1}, + {2 2 2 2 2 1 2 2 2 2}} +output: {{0, 3}, {1, 2}, {2, 1}, {3, 1}, {0, 3}, {0, 2}, {4, 3}, {1, 5}, {2, 5}, {1, 1}, {2, 4}} +``` + +W przypadku powyższej konwersji zamiast 30 bajtów (wymiary 10x3) zużyjemy 22 (11x2). Więc skompresowaliśmy dane o 26,7%. + +Nie przejmujemy się na razie tym jak `uint_8` będzie zamieniany na kolor. Ważne w tym zadaniu jest, aby poćwiczyć korzystanie z kontenerów oraz wykonywania na nich różnych operacji. + +___ + +## `decompressGrayscale()` + +Napisz funkcję `decompressGrayscale()`, która zdekompresuje obrazek skompresowany w zadaniu 3 za pomocą funkcji `compressGrayscale()`. + +Jako argument funkcja `decompressGrayscale()` przyjmie `std::vector>` natomiast zwróci `std::array, 160>` i przeprowadzi operacje mające na celu rekonstrukcję pierwotnego formatu bitmapy. + +___ + +## Implementacja + +Stwórz odpowiedni plik nagłówkowy (hpp) oraz źródłowy (cpp). + +W pliku nagłówkowym zdefiniuj stałe dotyczące rozmiaru w taki sposób: + +```cpp +constexpr size_t width = 32; +constexpr size_t height = 32; +``` + +Dzięki temu będzie Ci łatwiej zmienić rozmiar obrazka w celach testowych, bo wystarczy to zrobić tylko w jednym miejscu. +W testach też są używane takie same nazwy stałych. + +___ + +## ASCII art + +Dla chętnych polecamy także napisać sobie funkcję `printMap()`, która wyświetli obrazek. +Domyślnie `std::cout` potraktuje `uint8_t` jako `unsigned char`, dlatego też możecie sobie wypisać mapę z kodów ASCII. + +ASCII art z kubkiem + +Jeśli chcesz zrobić to zadanie (nie ma za nie dodatkowych punktów) to zaimplementuj zakomentowaną w main.cpp funkcję `printMap()`. Jej implementację wrzuć do pliku z funkcjami `compressGrayscale()` i `decompressGrayscale()`. Jej prawidłowa implementacja i odpalenie funkcji `main()` poprzez wywołanie `./grayscale-image` powinna wyświetlić ninję na ekranie :) + +```text + $**********$ + 488888888883 + +@DDDDDDDDDD@/ + !]=.9W]]]]]]]]]]]]]L + 5GOQ������������W4 + !K[k�������������Y" + !]=Z����=������p]�d" + Wq /+e��������������d" vX + UpH' #\��������������Y" 9�k + )@�P #,333333333337;B1" a�Q6 + Nil #,333333333337;B1" yyk* + -]g**,333333333337;B1=Z�sB + ,>M6,333333333337;B1_�L+ + =,DHD + )>@2/////////369=C=) + 1::-**********3M?/ + *1333699>BBB>* + #/.033466:==869& + #,3,-3333369900B1" + #,3,-3333334900B1" + #,3,-3333334900B1" + #,3,-333333490/@0" + #,3,-333333490.9." + #+,* +#include +#include "compression.hpp" + +std::vector> compressGrayscale(const std::array, height>& arr_bitmap) +{ + std::vector> vec_c; + vec_c.reserve(width * height); + auto last_color = arr_bitmap[0][0]; + int counter = 0; + int temp = 0; + + std::ranges::for_each(arr_bitmap, + [&vec_c, &last_color, &counter, &temp](const auto& row) { + std::ranges::for_each(row, + [&vec_c, &last_color, &counter, &temp](const auto& value) { + if (value == last_color && temp == 0) { + counter++; + } + else { + vec_c.emplace_back(last_color, counter); + last_color = value; + counter = 1; + temp = 0; + } + }); + temp = 1; + }); + vec_c.emplace_back(last_color, counter); // add last pair to vec_c + + return vec_c; +} + +std::array, height> decompressGrayscale(std::vector> &vec_bitmap) +{ + std::array, height> arr_dec; + auto bitmap_iter = arr_dec.front().begin(); + // is an iterator that will traverse all elements of arr_dec to fill them with values. + // auto is used to automatically infer the type of bitmap_iter based on the assigned value (arr_dec.front().begin()) + // front() returns a reference to the first element of this array, which is also an array. + // begin() is called on this "inner" array, returning an iterator pointing to its first element. + for (const auto& pair : vec_bitmap){ + std::fill_n(bitmap_iter, pair.second, pair.first); + std::advance(bitmap_iter, pair.second); + } + return arr_dec; +} + + +/* +std::vector> compressGrayscale2(const std::array, width>& bitmap) +{ + std::vector> vec_c; + vec_c.reserve(width * height); + auto last_color = bitmap[0][0]; + int counter = 0; + + for (const auto& row : bitmap) + { + std::transform(row.begin(), row.end(), std::back_inserter(vec_c), + [&counter, &last_color, &bitmap](uint8_t value) mutable { + if (value == last_color) { + counter++; + } + else { + last_color = value; + counter = 1; + return std::pair(last_color, counter); + } + return std::pair(last_color, counter); + }); + + // vec_c.erase(unique(vec_c.begin(), vec_c.end(), + // [](const auto & pair1, const auto &pair2){ + // + // return pair1.first == pair2.first; + // }) + //, vec_c.end()); + } + return vec_c; +} + + +/*bitmap = { { { 0, 0, 0, 1, 1, 2, 3, 0, 0, 0 }, + { 0, 0, 4, 4, 4, 1, 1, 1, 1, 1 }, + { 2, 2, 2, 2, 2, 1, 2, 2, 2, 2 } } }; +// vec_c = {0, 4}, {1, 2}, {2, 1}, {3, 1}, {0, 27}, {4, 3}, {1, 5}, {0, 22}, {2, 5}, {1, 1}, {2, 4}, {0, 182}, + + + +int main() +{ + std::array, width> + bitmap = { { { 0, 0, 0, 1, 1, 2, 3, 0, 0, 0 }, + { 0, 0, 4, 4, 4, 1, 1, 1, 1, 1 }, + { 2, 2, 2, 2, 2, 1, 2, 2, 2, 2 } } }; + + std::vector> vec_c = compressGrayscale(bitmap); + std::for_each(begin(vec_c), end(vec_c), [vec_c](const std::pair& p) { + std::cout << "{" << static_cast(p.first) << ", " << static_cast(p.second) << "}, "; + +// for (auto it = vec_c.begin(); it != vec_c.end(); ++it) { + // std::cout << "{" << it->first << ", " << it->second << "}, "; + //} + }); + std::cout << std::endl; + return 0; +} +*/ \ No newline at end of file diff --git a/homework/grayscale-image/compression.hpp b/homework/grayscale-image/compression.hpp new file mode 100644 index 000000000..7e5c88266 --- /dev/null +++ b/homework/grayscale-image/compression.hpp @@ -0,0 +1,10 @@ +#pragma once +#include +#include +#include + +constexpr size_t width = 32; // 32; +constexpr size_t height = 32; // 32; +// height = 240 , width = 160 +std::vector> compressGrayscale(const std::array, height>& arr_bitmap); +std::array, height> decompressGrayscale(std::vector>& vec_bitmap); \ No newline at end of file diff --git a/homework/grayscale-image/main.cpp b/homework/grayscale-image/main.cpp new file mode 100644 index 000000000..e5478d365 --- /dev/null +++ b/homework/grayscale-image/main.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include +#include "compression.hpp" + +// TODO: include +void printMap(std::array, 32ULL> decompressed); + +std::array, 32> generateNinja() { + return { + std::array{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 11, 29, 52, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 51, 29, 10, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 2, 12, 6, 21, 43, 64, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 47, 25, 8, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 33, 93, 61, 46, 57, 87, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 93, 76, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 15, 53, 71, 79, 81, 127, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 87, 52, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 7, 33, 75, 91, 107, 153, 162, 162, 142, 150, 162, 162, 162, 162, 162, 148, 145, 162, 89, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 33, 93, 61, 90, 156, 180, 185, 185, 61, 129, 185, 185, 185, 185, 185, 112, 93, 185, 100, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 87, 113, 0, 0, 13, 47, 43, 101, 178, 184, 185, 185, 163, 172, 185, 185, 185, 185, 185, 169, 166, 185, 100, 34, 0, 0, 0, 0, 118, 88, 0}, + {0, 85, 112, 72, 39, 0, 0, 35, 92, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 161, 162, 163, 89, 34, 0, 0, 32, 57, 142, 107, 0}, + {0, 41, 64, 137, 80, 0, 0, 35, 44, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 55, 59, 66, 49, 34, 0, 0, 97, 158, 81, 54, 0}, + {0, 8, 21, 78, 105, 108, 0, 35, 44, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 55, 59, 66, 49, 34, 0, 121, 121, 107, 42, 25, 0}, + {0, 0, 4, 45, 93, 103, 42, 42, 44, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 55, 59, 66, 49, 61, 90, 130, 115, 66, 4, 0, 0}, + {0, 0, 0, 0, 44, 62, 77, 54, 44, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 55, 59, 66, 49, 95, 158, 76, 43, 0, 0, 0, 0}, + {0, 0, 0, 0, 5, 23, 60, 70, 67, 43, 50, 51, 51, 51, 51, 51, 51, 51, 52, 57, 62, 61, 44, 68, 72, 68, 29, 12, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 10, 41, 62, 64, 50, 47, 47, 47, 47, 47, 47, 47, 47, 47, 51, 54, 57, 61, 67, 61, 41, 8, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 49, 58, 58, 45, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 51, 77, 63, 47, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 18, 42, 49, 51, 51, 51, 54, 57, 57, 62, 66, 66, 66, 62, 42, 18, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 24, 35, 47, 46, 48, 51, 51, 52, 54, 54, 58, 61, 61, 56, 54, 57, 38, 23, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 44, 51, 44, 45, 51, 51, 51, 51, 51, 54, 57, 57, 48, 48, 66, 49, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 44, 51, 44, 45, 51, 51, 51, 51, 51, 51, 52, 57, 48, 48, 66, 49, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 44, 51, 44, 45, 51, 51, 51, 51, 51, 51, 52, 57, 48, 48, 66, 49, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 44, 51, 44, 45, 51, 51, 51, 51, 51, 51, 52, 57, 48, 47, 64, 48, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 44, 51, 44, 45, 51, 51, 51, 51, 51, 51, 52, 57, 48, 46, 57, 46, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 35, 43, 44, 42, 60, 85, 85, 85, 85, 85, 85, 85, 86, 60, 43, 46, 43, 34, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 28, 34, 34, 41, 59, 82, 92, 88, 82, 82, 82, 83, 85, 60, 40, 34, 34, 27, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 45, 51, 89, 77, 51, 51, 51, 53, 66, 51, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 45, 51, 89, 77, 51, 51, 54, 57, 66, 51, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 45, 51, 76, 67, 51, 51, 54, 58, 66, 51, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 45, 51, 51, 51, 51, 51, 54, 58, 66, 51, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 45, 51, 51, 47, 43, 43, 51, 58, 66, 51, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 44, 47, 47, 40, 29, 29, 41, 51, 56, 47, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 41, 41, 41, 28, 0, 0, 29, 41, 41, 41, 36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }; +} + +int main() { + auto ninja = generateNinja(); + // printMap(ninja); + auto compressed = compressGrayscale(ninja); + + for (auto& p : compressed) { + std::cout << "{" << static_cast(p.first) << ", " << static_cast(p.second) << "}, "; + } + std::cout << "\n" + << std::endl; + + auto decompressed = decompressGrayscale(compressed); + // printMap(decompressed); + std::for_each(decompressed.begin(), decompressed.end(), [](const auto& row) { + std::copy(row.begin(), row.end(), std::ostream_iterator(std::cout, " ")); + std::cout << std::endl; + }); + + std::cout << std::endl; + + return 0; +} diff --git a/homework/grayscale-image/ninja.pgm b/homework/grayscale-image/ninja.pgm new file mode 100644 index 000000000..bcd3e5564 --- /dev/null +++ b/homework/grayscale-image/ninja.pgm @@ -0,0 +1,36 @@ +P2 +# Created by GIMP version 2.10.14 PNM plug-in +32 32 +255 +0 0 0 0 0 0 0 0 0 0 36 42 42 42 42 42 42 42 42 42 42 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 11 29 52 56 56 56 56 56 56 56 56 56 56 51 29 10 0 0 0 0 0 0 0 0 +0 0 0 0 0 2 12 6 21 43 64 68 68 68 68 68 68 68 68 68 68 64 47 25 8 0 0 0 0 0 0 0 +0 0 0 0 0 33 93 61 46 57 87 93 93 93 93 93 93 93 93 93 93 93 93 93 76 0 0 0 0 0 0 0 +0 0 0 0 0 15 53 71 79 81 127 136 136 136 136 136 136 136 136 136 136 136 136 87 52 0 0 0 0 0 0 0 +0 0 0 0 0 7 33 75 91 107 153 162 162 142 150 162 162 162 162 162 148 145 162 89 34 0 0 0 0 0 0 0 +0 0 0 0 0 33 93 61 90 156 180 185 185 61 129 185 185 185 185 185 112 93 185 100 34 0 0 0 0 0 0 0 +0 87 113 0 0 13 47 43 101 178 184 185 185 163 172 185 185 185 185 185 169 166 185 100 34 0 0 0 0 118 88 0 +0 85 112 72 39 0 0 35 92 161 161 161 161 161 161 161 161 161 161 161 161 162 163 89 34 0 0 32 57 142 107 0 +0 41 64 137 80 0 0 35 44 51 51 51 51 51 51 51 51 51 51 51 55 59 66 49 34 0 0 97 158 81 54 0 +0 8 21 78 105 108 0 35 44 51 51 51 51 51 51 51 51 51 51 51 55 59 66 49 34 0 121 121 107 42 25 0 +0 0 4 45 93 103 42 42 44 51 51 51 51 51 51 51 51 51 51 51 55 59 66 49 61 90 130 115 66 4 0 0 +0 0 0 0 44 62 77 54 44 51 51 51 51 51 51 51 51 51 51 51 55 59 66 49 95 158 76 43 0 0 0 0 +0 0 0 0 5 23 60 70 67 43 50 51 51 51 51 51 51 51 52 57 62 61 44 68 72 68 29 12 0 0 0 0 +0 0 0 0 0 10 41 62 64 50 47 47 47 47 47 47 47 47 47 51 54 57 61 67 61 41 8 0 0 0 0 0 +0 0 0 0 0 0 0 49 58 58 45 42 42 42 42 42 42 42 42 42 42 51 77 63 47 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 18 42 49 51 51 51 54 57 57 62 66 66 66 62 42 18 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 24 35 47 46 48 51 51 52 54 54 58 61 61 56 54 57 38 23 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 44 51 44 45 51 51 51 51 51 54 57 57 48 48 66 49 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 44 51 44 45 51 51 51 51 51 51 52 57 48 48 66 49 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 44 51 44 45 51 51 51 51 51 51 52 57 48 48 66 49 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 44 51 44 45 51 51 51 51 51 51 52 57 48 47 64 48 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 44 51 44 45 51 51 51 51 51 51 52 57 48 46 57 46 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 35 43 44 42 60 85 85 85 85 85 85 85 86 60 43 46 43 34 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 28 34 34 41 59 82 92 88 82 82 82 83 85 60 40 34 34 27 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 45 51 89 77 51 51 51 53 66 51 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 45 51 89 77 51 51 54 57 66 51 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 45 51 76 67 51 51 54 58 66 51 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 45 51 51 51 51 51 54 58 66 51 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 45 51 51 47 43 43 51 58 66 51 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 37 44 47 47 40 29 29 41 51 56 47 36 0 0 0 0 0 0 0 0 0 0 +0 0 0 0 0 0 0 0 0 0 36 41 41 41 28 0 0 29 41 41 41 36 0 0 0 0 0 0 0 0 0 0 \ No newline at end of file diff --git a/homework/grayscale-image/ninja.png b/homework/grayscale-image/ninja.png new file mode 100644 index 0000000000000000000000000000000000000000..de60ca801af2ba4b8b6b1adc8b92848fde10f1d1 GIT binary patch literal 28514 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 Qv(K8Eik5N(%p&Cf0Qzm8oB#j- literal 0 HcmV?d00001 diff --git a/homework/grayscale-image/test.cpp b/homework/grayscale-image/test.cpp new file mode 100644 index 000000000..e7df45855 --- /dev/null +++ b/homework/grayscale-image/test.cpp @@ -0,0 +1,191 @@ +#include +#include +#include // for std::pair<> +#include + +// TODO: include +#include "compression.hpp" +#include "gtest/gtest.h" + +void expectBitmap(const std::vector>& bitmap, size_t fraction) { + for (int j = 0; j < fraction; j++) { + for (int i = j; i < height * fraction; i += fraction) { + EXPECT_EQ(bitmap[i].first, j); + EXPECT_EQ(bitmap[i].second, width / fraction); + } + } +} + +std::vector> getBitmap(size_t fraction) { + std::vector> bitmap; + bitmap.reserve(height * fraction); + for (size_t i = 0; i < height; ++i) { + for (size_t j = 0; j < fraction; ++j) { + bitmap.push_back({j, width / fraction}); + } + } + + return bitmap; +} + +TEST(compressionTests, ShouldCompressWholeLines) { + std::array, height> arr; + for (int i = 0; i < height; ++i) + for (int j = 0; j < width; ++j) + arr[i][j] = 0; + + auto bitmap = compressGrayscale(arr); + ASSERT_EQ(bitmap.size(), height); + expectBitmap(bitmap, 1); +} + +TEST(compressionTests, ShouldCompressHalfLines) { + std::array, height> arr; + for (int i = 0; i < height; ++i) { + for (int j = 0; j < width / 2; ++j) + arr[i][j] = 0; + for (int j = width / 2; j < width; ++j) + arr[i][j] = 1; + } + + auto bitmap = compressGrayscale(arr); + expectBitmap(bitmap, 2); +} + +TEST(compressionTests, ShouldCompressQuaterLines) { + std::array, height> arr; + for (int i = 0; i < height; ++i) { + for (int j = 0; j < width / 4; ++j) + arr[i][j] = 0; + for (int j = width / 4; j < width / 2; ++j) + arr[i][j] = 1; + for (int j = width / 2; j < width / (4.0 / 3.0); ++j) + arr[i][j] = 2; + for (int j = width / (4.0 / 3.0); j < width; ++j) + arr[i][j] = 3; + } + + auto bitmap = compressGrayscale(arr); + ASSERT_EQ(bitmap.size(), height * 4); + expectBitmap(bitmap, 4); +} + +TEST(compressionTests, ShouldCompressOneEighthLines) { + std::array, height> arr; + for (int i = 0; i < height; ++i) { + for (int j = 0; j < width / 8; ++j) + arr[i][j] = 0; + for (int j = width / 8; j < width / 4; ++j) + arr[i][j] = 1; + for (int j = width / 4; j < width / (8.0 / 3.0); ++j) + arr[i][j] = 2; + for (int j = width / (8.0 / 3.0); j < width / 2; ++j) + arr[i][j] = 3; + for (int j = width / 2; j < width / (8.0 / 5.0); ++j) + arr[i][j] = 4; + for (int j = width / (8.0 / 5.0); j < width / (4.0 / 3.0); ++j) + arr[i][j] = 5; + for (int j = width / (4.0 / 3.0); j < width / (8.0 / 7.0); ++j) + arr[i][j] = 6; + for (int j = width / (8.0 / 7.0); j < width; ++j) + arr[i][j] = 7; + } + + auto bitmap = compressGrayscale(arr); + ASSERT_EQ(bitmap.size(), height * 8); + expectBitmap(bitmap, 8); +} + +TEST(compressionTests, ShouldDecompressWholeLines) { + constexpr size_t fraction = 1; + auto bitmap = getBitmap(fraction); + + auto map = decompressGrayscale(bitmap); + ASSERT_EQ(bitmap.size(), height); + for (const auto& row : map) { + for (const uint8_t color : row) { + EXPECT_EQ(color, 0); + } + } +} + +TEST(compressionTests, ShouldDecompressHalfLines) { + constexpr size_t fraction = 2; + auto bitmap = getBitmap(fraction); + + auto map = decompressGrayscale(bitmap); + ASSERT_EQ(map.size(), height); + for (const auto& row : map) { + for (size_t i = 0; i < row.size() / fraction; ++i) { + EXPECT_EQ(row[i], 0); + } + for (size_t i = row.size() / fraction; i < row.size(); ++i) { + EXPECT_EQ(row[i], 1); + } + } +} + +TEST(compressionTests, ShouldDecompressQuaterLines) { + constexpr size_t fraction = 4; + auto bitmap = getBitmap(fraction); + + auto map = decompressGrayscale(bitmap); + ASSERT_EQ(map.size(), height); + for (const auto& row : map) { + for (size_t i = 0; i < row.size() / fraction; ++i) { + EXPECT_EQ(row[i], 0); + } + for (size_t i = row.size() / fraction; i < row.size() / fraction * 2; ++i) { + EXPECT_EQ(row[i], 1); + } + for (size_t i = row.size() / fraction * 2; i < row.size() / (fraction / 3.0); ++i) { + EXPECT_EQ(row[i], 2); + } + for (size_t i = row.size() / (fraction / 3.0); i < row.size(); ++i) { + EXPECT_EQ(row[i], 3); + } + } +} + +TEST(compressionTests, ShouldDecompressOneEighthLines) { + constexpr size_t fraction = 8; + auto bitmap = getBitmap(fraction); + + auto map = decompressGrayscale(bitmap); + ASSERT_EQ(map.size(), height); + for (const auto& row : map) { + for (size_t i = 0; i < row.size() / fraction; ++i) { + EXPECT_EQ(row[i], 0); + } + for (size_t i = row.size() / (fraction / 1.0); i < row.size() / (fraction / 2.0); ++i) { + EXPECT_EQ(row[i], 1); + } + for (size_t i = row.size() / (fraction / 2.0); i < row.size() / (fraction / 3.0); ++i) { + EXPECT_EQ(row[i], 2); + } + for (size_t i = row.size() / (fraction / 3.0); i < row.size() / (fraction / 4.0); ++i) { + EXPECT_EQ(row[i], 3); + } + for (size_t i = row.size() / (fraction / 4.0); i < row.size() / (fraction / 5.0); ++i) { + EXPECT_EQ(row[i], 4); + } + for (size_t i = row.size() / (fraction / 5.0); i < row.size() / (fraction / 6.0); ++i) { + EXPECT_EQ(row[i], 5); + } + for (size_t i = row.size() / (fraction / 6.0); i < row.size() / (fraction / 7.0); ++i) { + EXPECT_EQ(row[i], 6); + } + for (size_t i = row.size() / (fraction / 7.0); i < row.size(); ++i) { + EXPECT_EQ(row[i], 7); + } + } +} + +TEST(compressionTests, ShouldCompressAndDecompress) { + constexpr size_t fraction = 16; + auto input = getBitmap(fraction); + auto map = decompressGrayscale(input); + auto bitmap = compressGrayscale(map); + ASSERT_TRUE(bitmap.size() == input.size()); + EXPECT_EQ(bitmap, input); +}