diff --git a/.timetracker b/.timetracker index 5713386..dc60918 100644 --- a/.timetracker +++ b/.timetracker @@ -1 +1 @@ -{"total":15240,"sessions":[{"begin":"2023-03-12T13:44:37-06:00","end":"2023-03-12T13:48:55-06:00","duration":258},{"begin":"2023-03-12T13:49:38-06:00","end":"2023-03-12T13:55:19-06:00","duration":341},{"begin":"2023-03-12T13:55:28-06:00","end":"2023-03-12T14:12:39-06:00","duration":1030},{"begin":"2023-03-12T14:12:42-06:00","end":"2023-03-12T14:53:07-06:00","duration":2424},{"begin":"2023-03-12T14:56:47-06:00","end":"2023-03-12T15:03:52-06:00","duration":425},{"begin":"2023-03-12T15:05:24-06:00","end":"2023-03-12T15:32:37-06:00","duration":1632},{"begin":"2023-03-12T15:32:42-06:00","end":"2023-03-12T15:48:18-06:00","duration":936},{"begin":"2023-03-12T15:49:19-06:00","end":"2023-03-12T15:59:42-06:00","duration":623},{"begin":"2023-03-12T16:00:06-06:00","end":"2023-03-12T16:07:04-06:00","duration":417},{"begin":"2023-03-12T16:07:33-06:00","end":"2023-03-12T16:13:28-06:00","duration":354},{"begin":"2023-03-12T16:17:33-06:00","end":"2023-03-12T16:36:24-06:00","duration":1131},{"begin":"2023-03-12T16:38:20-06:00","end":"2023-03-12T17:24:24-06:00","duration":2764},{"begin":"2023-03-12T17:25:18-06:00","end":"2023-03-12T17:27:19-06:00","duration":121},{"begin":"2023-03-12T18:08:42-06:00","end":"2023-03-12T18:08:57-06:00","duration":15},{"begin":"2023-03-12T18:09:00-06:00","end":"2023-03-12T18:11:02-06:00","duration":122},{"begin":"2023-03-12T19:14:25-06:00","end":"2023-03-12T19:17:15-06:00","duration":170},{"begin":"2023-03-12T19:18:33-06:00","end":"2023-03-12T19:20:50-06:00","duration":137},{"begin":"2023-03-12T19:21:10-06:00","end":"2023-03-12T19:24:03-06:00","duration":173},{"begin":"2023-03-12T19:25:08-06:00","end":"2023-03-12T19:33:11-06:00","duration":483},{"begin":"2023-03-12T19:35:37-06:00","end":"2023-03-12T19:37:53-06:00","duration":135},{"begin":"2023-03-12T19:39:51-06:00","end":"2023-03-12T19:41:53-06:00","duration":122},{"begin":"2023-03-12T19:48:10-06:00","end":"2023-03-12T20:11:58-06:00","duration":1427}]} \ No newline at end of file +{"total":17589,"sessions":[{"begin":"2023-03-12T13:44:37-06:00","end":"2023-03-12T13:48:55-06:00","duration":258},{"begin":"2023-03-12T13:49:38-06:00","end":"2023-03-12T13:55:19-06:00","duration":341},{"begin":"2023-03-12T13:55:28-06:00","end":"2023-03-12T14:12:39-06:00","duration":1030},{"begin":"2023-03-12T14:12:42-06:00","end":"2023-03-12T14:53:07-06:00","duration":2424},{"begin":"2023-03-12T14:56:47-06:00","end":"2023-03-12T15:03:52-06:00","duration":425},{"begin":"2023-03-12T15:05:24-06:00","end":"2023-03-12T15:32:37-06:00","duration":1632},{"begin":"2023-03-12T15:32:42-06:00","end":"2023-03-12T15:48:18-06:00","duration":936},{"begin":"2023-03-12T15:49:19-06:00","end":"2023-03-12T15:59:42-06:00","duration":623},{"begin":"2023-03-12T16:00:06-06:00","end":"2023-03-12T16:07:04-06:00","duration":417},{"begin":"2023-03-12T16:07:33-06:00","end":"2023-03-12T16:13:28-06:00","duration":354},{"begin":"2023-03-12T16:17:33-06:00","end":"2023-03-12T16:36:24-06:00","duration":1131},{"begin":"2023-03-12T16:38:20-06:00","end":"2023-03-12T17:24:24-06:00","duration":2764},{"begin":"2023-03-12T17:25:18-06:00","end":"2023-03-12T17:27:19-06:00","duration":121},{"begin":"2023-03-12T18:08:42-06:00","end":"2023-03-12T18:08:57-06:00","duration":15},{"begin":"2023-03-12T18:09:00-06:00","end":"2023-03-12T18:11:02-06:00","duration":122},{"begin":"2023-03-12T19:14:25-06:00","end":"2023-03-12T19:17:15-06:00","duration":170},{"begin":"2023-03-12T19:18:33-06:00","end":"2023-03-12T19:20:50-06:00","duration":137},{"begin":"2023-03-12T19:21:10-06:00","end":"2023-03-12T19:24:03-06:00","duration":173},{"begin":"2023-03-12T19:25:08-06:00","end":"2023-03-12T19:33:11-06:00","duration":483},{"begin":"2023-03-12T19:35:37-06:00","end":"2023-03-12T19:37:53-06:00","duration":135},{"begin":"2023-03-12T19:39:51-06:00","end":"2023-03-12T19:41:53-06:00","duration":122},{"begin":"2023-03-12T19:48:10-06:00","end":"2023-03-12T20:11:58-06:00","duration":1427},{"begin":"2023-03-15T19:53:49-06:00","end":"2023-03-15T20:08:22-06:00","duration":873},{"begin":"2023-03-15T20:08:26-06:00","end":"2023-03-15T20:33:02-06:00","duration":1476}]} \ No newline at end of file diff --git a/example/converted_test1.png b/example/converted_test1.png index 8527cd9..f7c86b3 100644 Binary files a/example/converted_test1.png and b/example/converted_test1.png differ diff --git a/example/converted_test2.png b/example/converted_test2.png index 41055c8..48e7c47 100644 Binary files a/example/converted_test2.png and b/example/converted_test2.png differ diff --git a/example/converted_test3.jpg b/example/converted_test3.jpg index e94db1f..171da0f 100644 Binary files a/example/converted_test3.jpg and b/example/converted_test3.jpg differ diff --git a/example/example.cpp b/example/example.cpp index c93ab49..b6540f3 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -8,10 +8,19 @@ int main() { std::array test_images = {"test1.png", "test2.png", "test3.jpg"}; - for (auto &image_path : test_images) { + std::vector data = {"PRES 1025mb", "TEMP 25.0c", + "HUMID 50%", "ALT 102416ft" }; + + std::vector data2 = {"TESTING"}; + + for (auto &image_path : test_images) { // Do this for each image SstvImage image(SstvImage::Mode::ROBOT_36_COLOR, image_path, "converted_" + image_path); + image.AddCallSign("N0CALL"); + image.AddText(data); + image.AddText(data2, false, SstvImage::Color(0, 0, 0), SstvImage::Color(1, 1, 1)); + image.AdjustColors(); // Optional, needed if getting rgb pixel values SstvImage::Pixel pixel; if (!image.GetPixel(128, 91, pixel)) { diff --git a/include/sstv-image-tools.h b/include/sstv-image-tools.h index 307db6b..887fe5a 100644 --- a/include/sstv-image-tools.h +++ b/include/sstv-image-tools.h @@ -9,24 +9,26 @@ class SstvImage { public: - enum class Mode { - ROBOT_8_BW, // 160x120 (4:3) Black and White - ROBOT_12_BW, // 160x120 (4:3) Black and White - ROBOT_24_BW, // 320x240 (4:3) Black and White - ROBOT_36_BW, // 320x240 (4:3) Black and White - ROBOT_12_COLOR, // 160x120 (4:3) Color - ROBOT_24_COLOR, // 320x240 (4:3) Color - ROBOT_36_COLOR, // 320x240 (4:3) Color - ROBOT_72_COLOR, // 640x480 (4:3) Color - CUSTOM_TEST }; + enum class Mode { + ROBOT_8_BW, // 160x120 (4:3) Black and White + ROBOT_12_BW, // 160x120 (4:3) Black and White + ROBOT_24_BW, // 320x240 (4:3) Black and White + ROBOT_36_BW, // 320x240 (4:3) Black and White + ROBOT_12_COLOR, // 160x120 (4:3) Color + ROBOT_24_COLOR, // 320x240 (4:3) Color + ROBOT_36_COLOR, // 320x240 (4:3) Color + ROBOT_72_COLOR, // 640x480 (4:3) Color + CUSTOM_TEST + }; struct Color { - int r = -1; - int g = -1; - int b = -1; + float r = -1.0; + float g = -1.0; + float b = -1.0; float gray_scale_value = -1; - Color(int r, int g, int b); // 0 - 255 - Color(int gray_scale_value); // 0.0 - 1.0 + Color(float r, float g, float b); // 0 - 255 + Color(int gray_scale_value); // 0.0 - 1.0, not yet implemented + Magick::ColorRGB GetMagickColor() const; }; struct Pixel { @@ -50,23 +52,30 @@ class SstvImage { void Write(); /** @todo should pass the destination path here*/ void AddCallSign(const std::string &callsign, - const SstvImage::Color &color = {-2, -2, -2}); - void AddMessage(std::string message); + const SstvImage::Color &fill_color = {0.0, 1.0, 0}, + const SstvImage::Color &stroke_color = {1.0, 0, 0}); + + void AddText(const std::vector &text, + const bool left_column = true, + const SstvImage::Color fill_color = {0.0, 1.0, 0.0}, + const SstvImage::Color stroke_color = {1.0, 0.0, 0.0}); + + void AdjustColors(); bool GetPixel(const int x, const int y, SstvImage::Pixel &pixel); int GetWidth() const { return width_; } int GetHeight() const { return height_; } - void AdjustColors(); - private: void Scale(); - + std::pair WorkspaceToImageCoordinates(int x, int y); int width_ = 0; int height_ = 0; double height_scaler_ = 1; + const int kCallSignFontHeight_ = 25; + const int kTextFontHeight_ = 10; std::string source_path_ = ""; std::string destination_path_ = ""; diff --git a/src/sstv-image-tools.cpp b/src/sstv-image-tools.cpp index 634820e..ad8cf89 100644 --- a/src/sstv-image-tools.cpp +++ b/src/sstv-image-tools.cpp @@ -1,15 +1,24 @@ #include "sstv-image-tools.h" #include + #include #include -SstvImage::Color::Color(int red, int green, int blue) { +SstvImage::Color::Color(float red, float green, float blue) { + if (red < 0.0 || red > 1.0 || green < 0.0 || green > 1.0 || blue < 0.0 || + blue > 1.0) { + throw SstvImageToolsException("Invalid color value"); + } this->r = red; this->g = green; this->b = blue; } +Magick::ColorRGB SstvImage::Color::GetMagickColor() const { + return Magick::ColorRGB(r, g, b); +} + SstvImage::SstvImage(SstvImage::Mode mode, std::string source_image_path, std::string destination_image_path, bool crop) : mode_(mode), crop_(crop) { @@ -38,29 +47,68 @@ void SstvImage::Write() { } void SstvImage::AddCallSign(const std::string &callsign, - const SstvImage::Color &color) { - (void)color; + const SstvImage::Color &fill_color, + const SstvImage::Color &stroke_color) { + (void)fill_color; + (void)stroke_color; - int font_size = 25 * height_scaler_; + int font_size = kCallSignFontHeight_ * height_scaler_; std::vector draw_list( - {Magick::DrawableFont("Arial-Bold"), - Magick::DrawablePointSize(font_size), - Magick::DrawableText(0, 0 + font_size * 0.8, callsign), - Magick::DrawableStrokeColor("red"), - Magick::DrawableStrokeWidth(2), - Magick::DrawableStrokeAntialias(true), - Magick::DrawableFillColor("green")} -); + {Magick::DrawableFont("Arial-Bold"), Magick::DrawablePointSize(font_size), + Magick::DrawableText(0, 0 + font_size * 0.8, callsign), + Magick::DrawableStrokeColor(stroke_color.GetMagickColor()), + Magick::DrawableStrokeWidth(2), Magick::DrawableStrokeAntialias(true), + Magick::DrawableFillColor(fill_color.GetMagickColor())}); image_.draw(draw_list); } +void SstvImage::AddText(const std::vector &text, + const bool left_column, + const SstvImage::Color fill_color, + const SstvImage::Color stroke_color) { + int font_size = 10 * height_scaler_; + + std::vector draw_list( + {Magick::DrawableFont("Arial-Bold"), Magick::DrawablePointSize(font_size), + Magick::DrawableFillColor(fill_color.GetMagickColor()), + Magick::DrawableStrokeColor(stroke_color.GetMagickColor()), + Magick::DrawableStrokeWidth(1)}); + + int y = left_column ? 0 : GetWidth() / 2; + + int i = 1; + for (auto line : text) { + draw_list.push_back(Magick::DrawableText( + y, GetHeight() + font_size * 0.8 - (i * font_size), line)); + i++; + } + + image_.draw(draw_list); +} + +void SstvImage::AdjustColors() { + image_.quantizeColorSpace(Magick::RGBColorspace); + image_.quantizeColors(256); + image_.quantize(); + image_.modifyImage(); + image_.gamma(1.7); + image_.enhance(); + + /* + This is a hack to deal with the color space issues when reading pixel values. + Not a fan of this solution, and it needs to be fixed, but it works for now. + */ + image_.write("tmp.png"); + image_.read("tmp.png"); +} + bool SstvImage::GetPixel(const int x, const int y, SstvImage::Pixel &pixel) { if (x < 0 || x >= width_ || y < 0 || y >= height_) { return false; } - + MagickCore::Quantum *pixels = image_.getPixels(0, 0, width_, height_); unsigned index = (y * width_ + x) * image_.channels(); @@ -70,20 +118,6 @@ bool SstvImage::GetPixel(const int x, const int y, SstvImage::Pixel &pixel) { return true; } -void SstvImage::AdjustColors() { - /* - This is a hack to deal with the color space issues. Not a fan of this solution. - */ - image_.quantizeColorSpace(Magick::RGBColorspace); - image_.quantizeColors(256); - image_.quantize(); - image_.modifyImage(); - image_.gamma(1.7); - image_.enhance(); - image_.write("tmp.png"); - image_.read("tmp.png"); -} - void SstvImage::Scale() { width_ = 0; height_ = 0; @@ -123,42 +157,4 @@ void SstvImage::Scale() { crop_size.aspect(true); image_.resize(crop_size); } - - //image_.enhance(); - /* - image_.quantizeColorSpace(Magick::RGBColorspace); - image_.quantizeColors(256); - image_.quantize(); - image_.modifyImage(); - image_.colorSpace(Magick::sRGBColorspace); - image_.type(Magick::TrueColorType); - if (image_.colorSpace() == Magick::sRGBColorspace){ - std::cout << "sRGBColorspace" << std::endl; - } else if (image_.colorSpace() == Magick::RGBColorspace) { - std::cout << "RGBColorspace" << std::endl; - } else if (image_.colorSpace() == Magick::CMYColorspace) { - std::cout << "CMYColorspace" << std::endl; - } else if (image_.colorSpace() == Magick::GRAYColorspace) { - std::cout << "GRAYColorspace" << std::endl; - } else { - std::cout << "Unknown colorspace" << std::endl; - } - - std::cout << "Channels: " << image_.channels() << std::endl; - image_.channel(Magick::RedChannel); - */ - - //image_.quantizeColorSpace(Magick::RGBColorspace); // THESE - //image_.quantizeColors(256); // THESE - //image_.quantize(); // THESE - //image_.normalize(); - //image_.enhance(); Made things a lot worse - - /* - Known working solution: - image_.quantizeColorSpace(Magick::RGBColorspace); - image_.quantizeColors(256); - image_.quantize(); - image_.modifyImage(); - */ } \ No newline at end of file