Skip to content

Commit

Permalink
changed name of directory
Browse files Browse the repository at this point in the history
  • Loading branch information
PaSawi committed Aug 3, 2023
1 parent 796317b commit c34bdaf
Show file tree
Hide file tree
Showing 8 changed files with 584 additions and 0 deletions.
27 changes: 27 additions & 0 deletions homework/grayscale-image/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)
142 changes: 142 additions & 0 deletions homework/grayscale-image/README.md
Original file line number Diff line number Diff line change
@@ -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:

<img width="300px" src="https://github.com/coders-school/stl/raw/master/img/example.png" alt="Przykład pikseli" class="plain">

```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.

___
<!-- .slide: style="font-size: 0.9em" -->

## `compressGrayscale()` - opis

Napisz funkcję `compressGrayscale()`.
Powinna ona przyjąć jeden argument typu `std::array<std::array<uint8_t, 240>, 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<std::pair<uint8_t, uint8_t>>` 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).

___
<!-- .slide: style="font-size: 0.9em" -->

## `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<std::pair<uint8_t, uint8_t>>` natomiast zwróci `std::array<std::array<uint8_t, 240>, 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.

<img width="450px" src="https://github.com/coders-school/stl/raw/master/img/ascii_art_mug.jpg" alt="ASCII art z kubkiem" class="plain">

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+
<FC+2333333349>=,DHD
)>@2/////////369=C=)
1::-**********3M?/
*1333699>BBB>*
#/.033466:==869&
#,3,-3333369900B1"
#,3,-3333334900B1"
#,3,-3333334900B1"
#,3,-333333490/@0"
#,3,-333333490.9."
#+,*<UUUUUUUV<+.+"
"");R\XRRRSU<(""
%-3YM3335B3$
%-3YM3369B3$
%-3LC336:B3$
%-333336:B3$
%-33/++3:B3$
%,//( )38/$
$))) )))$
```

___

### Refaktoryzacja testów

Jest to zadanie dla chętnych (bez dodatkowych punktów).

Napisz nową funkcję, która zastąpi te brzydkie pętle w testach:

```cpp
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;
}
```

Wystarczy zauważyć zależność, że w zależności od tego, jaka część linii jest wypełniania to pętle przyjmują określoną strukturę.
Ma to być jedna uniwersalna funkcja, która będzie używa w testach `ShouldCompressWholeLine`, `ShouldCompressHalfLine`, `ShouldCompressQuaterLine`, itd.
Ma to działać podobnie jak funkcja `getBitmap()`.
Po wydzieleniu i refaktoringu (czyli upiększeniu, aby kod był czytelniejszy i lepszy do ponownego użycia) funkcji generującej, postaraj się dopisać także przypadki dla 1/16, 1/32 i 1/64 mapy.
109 changes: 109 additions & 0 deletions homework/grayscale-image/compression.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <algorithm>
#include <ranges>
#include "compression.hpp"

std::vector<std::pair<uint8_t, uint8_t>> compressGrayscale(const std::array<std::array<uint8_t, width>, height>& arr_bitmap)
{
std::vector<std::pair<uint8_t, uint8_t>> 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<std::array<uint8_t, width>, height> decompressGrayscale(std::vector<std::pair<uint8_t, uint8_t>> &vec_bitmap)
{
std::array<std::array<uint8_t, width>, 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<std::pair<uint8_t, uint8_t>> compressGrayscale2(const std::array<std::array<uint8_t, height>, width>& bitmap)
{
std::vector<std::pair<uint8_t, uint8_t>> 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<uint8_t, uint8_t>(last_color, counter);
}
return std::pair<uint8_t, uint8_t>(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<std::array<uint8_t, height>, 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<std::pair<uint8_t, uint8_t>> vec_c = compressGrayscale(bitmap);
std::for_each(begin(vec_c), end(vec_c), [vec_c](const std::pair<uint8_t, uint8_t>& p) {
std::cout << "{" << static_cast<int>(p.first) << ", " << static_cast<int>(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;
}
*/
10 changes: 10 additions & 0 deletions homework/grayscale-image/compression.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once
#include <array>
#include <iostream>
#include <vector>

constexpr size_t width = 32; // 32;
constexpr size_t height = 32; // 32;
// height = 240 , width = 160
std::vector<std::pair<uint8_t, uint8_t>> compressGrayscale(const std::array<std::array<uint8_t, width>, height>& arr_bitmap);
std::array<std::array<uint8_t, width>, height> decompressGrayscale(std::vector<std::pair<uint8_t, uint8_t>>& vec_bitmap);
69 changes: 69 additions & 0 deletions homework/grayscale-image/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <algorithm>
#include <array>
#include <forward_list>
#include <iostream>
#include <iterator>
#include "compression.hpp"

// TODO: include
void printMap(std::array<std::array<uint8_t, 32ULL>, 32ULL> decompressed);

std::array<std::array<uint8_t, 32>, 32> generateNinja() {
return {
std::array<uint8_t, 32>{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<int>(p.first) << ", " << static_cast<int>(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<int>(std::cout, " "));
std::cout << std::endl;
});

std::cout << std::endl;

return 0;
}
Loading

0 comments on commit c34bdaf

Please sign in to comment.