| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <stb/stb_image.h> | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <cctype> | ||
| 7 | #include <cstddef> | ||
| 8 | #include <cstdint> | ||
| 9 | #include <filesystem> | ||
| 10 | #include <fstream> | ||
| 11 | #include <stdexcept> | ||
| 12 | #include <string> | ||
| 13 | #include <utility> | ||
| 14 | #include <vector> | ||
| 15 | |||
| 16 | namespace shakirova_e_sobel_edge_detection { | ||
| 17 | |||
| 18 |
1/2✓ Branch 1 taken 72 times.
✗ Branch 2 not taken.
|
360 | struct ImgContainer { |
| 19 | int width{0}; | ||
| 20 | int height{0}; | ||
| 21 | std::vector<int> pixels; | ||
| 22 | |||
| 23 | 72 | ImgContainer() = default; | |
| 24 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | ImgContainer(int w, int h) : width(w), height(h), pixels(static_cast<size_t>(w) * static_cast<size_t>(h), 0) {} |
| 25 | ImgContainer(int w, int h, std::vector<int> px) : width(w), height(h), pixels(std::move(px)) {} | ||
| 26 | |||
| 27 | [[nodiscard]] bool IsValid() const { | ||
| 28 |
3/6✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 72 times.
|
72 | return width > 2 && height > 2 && static_cast<int>(pixels.size()) == width * height; |
| 29 | } | ||
| 30 | |||
| 31 | [[nodiscard]] int &At(int x, int y) { | ||
| 32 | return pixels[(y * width) + x]; | ||
| 33 | } | ||
| 34 | [[nodiscard]] const int &At(int x, int y) const { | ||
| 35 | return pixels[(y * width) + x]; | ||
| 36 | } | ||
| 37 | |||
| 38 | friend bool operator==(const ImgContainer &lhs, const ImgContainer &rhs) { | ||
| 39 | return lhs.width == rhs.width && lhs.height == rhs.height && lhs.pixels == rhs.pixels; | ||
| 40 | } | ||
| 41 | |||
| 42 | 72 | static ImgContainer FromFile(const std::string &path) { | |
| 43 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
|
144 | if (!std::filesystem::exists(path)) { |
| 44 | ✗ | throw std::runtime_error("File not found: " + path); | |
| 45 | } | ||
| 46 | 72 | const std::string ext = GetExt(path); | |
| 47 |
5/8✓ Branch 0 taken 36 times.
✓ Branch 1 taken 36 times.
✓ Branch 2 taken 36 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 36 times.
|
72 | if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".bmp") { |
| 48 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | return LoadRaster(path); |
| 49 | } | ||
| 50 |
1/2✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
|
36 | if (ext == ".txt") { |
| 51 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | return LoadTXT(path); |
| 52 | } | ||
| 53 | ✗ | throw std::runtime_error("Unsupported format: " + ext + " (supported: .png .jpg .bmp .txt)"); | |
| 54 | } | ||
| 55 | |||
| 56 | private: | ||
| 57 | static int ToGray(uint8_t r, uint8_t g, uint8_t b) { | ||
| 58 | 9849600 | return static_cast<int>((0.299 * r) + (0.587 * g) + (0.114 * b)); | |
| 59 | } | ||
| 60 | |||
| 61 |
1/2✓ Branch 0 taken 72 times.
✗ Branch 1 not taken.
|
72 | static std::string GetExt(const std::string &path) { |
| 62 | const auto pos = path.rfind('.'); | ||
| 63 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | if (pos == std::string::npos) { |
| 64 | ✗ | return ""; | |
| 65 | } | ||
| 66 | 72 | std::string ext = path.substr(pos); | |
| 67 | 288 | std::ranges::transform(ext, ext.begin(), [](unsigned char c) { return std::tolower(c); }); | |
| 68 | 72 | return ext; | |
| 69 | } | ||
| 70 | |||
| 71 | 36 | static ImgContainer LoadRaster(const std::string &path) { | |
| 72 | 36 | int w = 0; | |
| 73 | 36 | int h = 0; | |
| 74 | 36 | int ch = 0; | |
| 75 | 36 | uint8_t *data = stbi_load(path.c_str(), &w, &h, &ch, STBI_rgb); | |
| 76 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (data == nullptr) { |
| 77 | ✗ | throw std::runtime_error("stb_image failed to load: " + path); | |
| 78 | } | ||
| 79 | 36 | ImgContainer img(w, h); | |
| 80 |
2/2✓ Branch 0 taken 9849600 times.
✓ Branch 1 taken 36 times.
|
9849636 | for (int i = 0; i < w * h; ++i) { |
| 81 | 9849600 | img.pixels[i] = ToGray(data[static_cast<ptrdiff_t>(i) * 3], data[(static_cast<ptrdiff_t>(i) * 3) + 1], | |
| 82 | 9849600 | data[(static_cast<ptrdiff_t>(i) * 3) + 2]); | |
| 83 | } | ||
| 84 |
1/2✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
|
36 | stbi_image_free(data); |
| 85 | 36 | return img; | |
| 86 | } | ||
| 87 | |||
| 88 | 36 | static ImgContainer LoadTXT(const std::string &path) { | |
| 89 | 36 | std::ifstream f(path); | |
| 90 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
|
36 | if (!f.is_open()) { |
| 91 | ✗ | throw std::runtime_error("Cannot open file: " + path); | |
| 92 | } | ||
| 93 | 36 | int w = 0; | |
| 94 | 36 | int h = 0; | |
| 95 |
2/4✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 36 times.
✗ Branch 5 not taken.
|
36 | f >> w >> h; |
| 96 |
2/4✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 36 times.
|
36 | if (w <= 0 || h <= 0) { |
| 97 | ✗ | throw std::runtime_error("Invalid dimensions in: " + path); | |
| 98 | } | ||
| 99 | ImgContainer img(w, h); | ||
| 100 |
2/2✓ Branch 0 taken 684 times.
✓ Branch 1 taken 36 times.
|
720 | for (int i = 0; i < w * h; ++i) { |
| 101 |
1/2✓ Branch 1 taken 684 times.
✗ Branch 2 not taken.
|
684 | f >> img.pixels[i]; |
| 102 | } | ||
| 103 | 36 | return img; | |
| 104 | 36 | } | |
| 105 | }; | ||
| 106 | |||
| 107 | } // namespace shakirova_e_sobel_edge_detection | ||
| 108 |