diff --git a/.gitignore b/.gitignore index f5b8ab6..5a974b1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Build results [Bb]uild/ .vs/ +.vscode/ out/ CMakeSettings.json \ No newline at end of file diff --git a/README.md b/README.md index 8703257..342b5f9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # **PlotOpenCv C++ library** -**v1.1.0** +**v1.2.0** @@ -16,18 +16,20 @@ - [Library files](#library-files) - [plot class description](#plot-class-description) - [Class declaration](#class-declaration) + - [getVersion method](#getversion-method) + - [Class constructor](#class-constructor) - [addPlot (1D) method](#addplot-for-1d-dataset-method) - [addPlot (2D) method](#addplot-for-2d-dataset-method) - [clean method](#show-method) - [show method](#clean-method) -- [Example](#example) - [Build and connect to your project](#build-and-connect-to-your-project) +- [Example](#example) # Overview -**PlotOpenCv** C++ library provides the visualization of 2-dimensional line charts. This library is built upon the OpenCV, providing users with a convenient and efficient tool for visualizing data through line charts. With **PlotOpenCv**, users can create multiple line charts within a single window and tune various chart parameters, such as line width, color, and more. It utilizes C++17 standard. The library is licensed under the Apache 2.0 license. +**PlotOpenCv** C++ library provides the visualization of 2-dimensional line charts. This library is built upon the OpenCV, providing users with a convenient and efficient tool for visualizing data through line charts. With **PlotOpenCv**, users can create multiple line charts within a single window and tune various chart parameters, such as line width, color, and more. It utilizes C++17 standard. The library is licensed under the **Apache 2.0** license. @@ -42,6 +44,7 @@ | 1.0.2 | 16.04.2024 | - Antialiased line drawing implemented.
- Window size issue fixed.
- Documentation updated. | | 1.0.3 | 17.05.2024 | - Documentation updated. | | 1.1.0 | 19.07.2024 | - CMake structure updated.
- Files renamed. | +| 1.2.0 | 23.07.2024 | - Files structure changed to hide opencv headers.
- Library interface changed. | @@ -57,6 +60,9 @@ src ------------------------- Library source code folder. PlotOpenCvVersion.h ----- Header file with library version. PlotOpenCvVersion.h.in -- File for CMake to generate version header. PlotOpenCv.cpp ---------- C++ implementation file. + Impl -------------------- Folder with plot implementation. + PlotOpenCvImpl.h ---- Plot implementation header file. + PlotOpenCvImpl.cpp -- C++ implementation file. test ------------------------ Folder for test application. CMakeLists.txt ---------- CMake file of test application. main.cpp ---------------- Source code of test application. @@ -77,7 +83,26 @@ namespace cr { namespace utils { -/// plot class. + +/** + * @brief Plot color. +*/ +class PlotColor +{ +public: + /// Class constructor + PlotColor(int b, int g, int r) : b(b), g(g), r(r) {} + /// Blue color 0-255. + int b{0}; + /// Green color 0-255. + int g{0}; + /// Red color 0-255. + int r{0}; +}; + +/** + * @brief Plot class. + */ class Plot { public: @@ -87,8 +112,8 @@ public: /// Class constructor. Plot(std::string name, int width = 1280, int height = 720, - cv::Scalar backgroundColor = cv::Scalar(255, 255, 255), - cv::Scalar scaleLineColor = cv::Scalar(0, 128, 128)); + PlotColor backgroundColor = PlotColor(255, 255, 255), + PlotColor scaleLineColor = PlotColor(0, 128, 128)); /// Class destructor. ~Plot(); @@ -96,12 +121,13 @@ public: /// Render plots on window. template void addPlot(std::vector& points, int id, int start = 0, int end = 0, - cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + PlotColor color = PlotColor(255, 255, 255), int thickness = 1); + /// Method to render plots on window. template void addPlot(std::vector>& points, int id, - int start = 0, int end = 0, - cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + int start = 0, int end = 0, + PlotColor color = PlotColor(255, 255, 255), int thickness = 1); /// Method to clean window. void clean(); @@ -132,7 +158,32 @@ std::cout << "PlotOpenCv class version: " << PlotOpenCv::getVersion(); Console output: ```bash -PlotOpenCv class version: 1.1.0 +PlotOpenCv class version: 1.2.0 +``` + + +## Class constructor + +The **Plot** class constructor requires basic parameters. Constructor declaration: + +```cpp +Plot(std::string name, int width = 1280, int height = 720, + PlotColor backgroundColor = PlotColor(255, 255, 255), + PlotColor scaleLineColor = PlotColor(0, 128, 128)); +``` + +| Parameter | Value | +| --------------- | ------------------------------- | +| name | OpenCV window name. | +| width | Window width, pixels. | +| height | Window height, pixels. | +| backgroundColor | Background color, BGR. | +| scaleLineColor | Scale line color, BGR. | + +Example of class initialization: + +```cpp +Plot graph("Test graph", 1280, 640, PlotColor(50, 50, 50)); ``` @@ -148,7 +199,7 @@ void addPlot(std::vector &points, int id, int start = 0, int end = 0, | Parameter | Value | | --------- | ------------------------------------------------------------ | -| Points | One dimensional vector which includes vertical points.Vector format : {y1, y2, ... } | +| Points | One dimensional vector which includes vertical points. Vector format : {y1, y2, ... } | | id | Identifier for chart on a window. Provides user to update a chart or add new one. | | start | Start index of plot from vector when user wants to plot a specific range from a dataset. Should be 0 for whole dataset.| | end | End index of plot from vector when user wants to plot a specific range from a dataset. Should be 0 for whole dataset. | @@ -162,8 +213,10 @@ void addPlot(std::vector &points, int id, int start = 0, int end = 0, The **addPlot(...)** method serves the purpose of incorporating a new line chart into the existing window. It either introduces a new plot if the provided id is not yet present, or updates an existing plot associated with the given identifier. Method declaration: ```cpp -void addPlot(std::vector> &points, int id, int start = 0, int end = 0, - cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); +template +void addPlot(std::vector>& points, int id, + int start = 0, int end = 0, + PlotColor color = PlotColor(255, 255, 255), int thickness = 1); ``` | Parameter | Value | @@ -231,7 +284,7 @@ src yourLib.cpp ``` -create folder **3rdparty** in your repository and copy **PlotOpenCv** repository folder there. New structure of your repository: +Create folder **3rdparty** in your repository and copy **PlotOpenCv** repository folder there. New structure of your repository: ```bash CMakeLists.txt @@ -321,13 +374,13 @@ The example demonstrates how to use **PlotOpenCv** library. int main() { - plot graph("Test graph", 1280, 720,cv::Scalar(0, 128, 128) cv::Scalar(50, 50, 50)); + plot graph("Test graph", 1280, 720, PlotColor(0, 128, 128) PlotColor(50, 50, 50)); std::vector linePoints(9000); std::vector> linePoints2(5000, std::vector(2)); - graph.addPlot(linePoints,0, 0, 0, cv::Scalar(255,0,0), 5); - graph.addPlot(linePoints2,1, 0, 0, cv::Scalar(0,255,0), 2); + graph.addPlot(linePoints, 0, 0, 0, PlotColor(255,0,0), 5); + graph.addPlot(linePoints2, 1, 0, 0, PlotColor(0,255,0), 2); graph.show(); cv::waitKey(0); @@ -335,11 +388,7 @@ int main() } ``` - - -# Example Charts - -Example charts shows what visual effects user should expect depending on input data. +Example charts shows what visual effects user should expect depending on input data: ![plot_opencv_example_1](./static/plot_opencv_example_1.png) ![plot_opencv_example_2](./static/plot_opencv_example_2.png) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 60acff9..2a38cdc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.13) ## LIBRARY-PROJECT ## name and version ############################################################################### -project(PlotOpenCv VERSION 1.0.4 LANGUAGES CXX) +project(PlotOpenCv VERSION 1.2.0 LANGUAGES CXX) diff --git a/src/Impl/PlotOpenCvImpl.cpp b/src/Impl/PlotOpenCvImpl.cpp new file mode 100644 index 0000000..2f2968e --- /dev/null +++ b/src/Impl/PlotOpenCvImpl.cpp @@ -0,0 +1,362 @@ +#include "PlotOpenCvImpl.h" + + + +template +T map(T x, T in_min, T in_max, T out_min, T out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + + + +cr::utils::PlotImpl::PlotImpl(std::string name, int width, int height, + cv::Scalar backgroundColor, cv::Scalar scaleLineColor) +{ + // Create a window. + cv::namedWindow(name, cv::WINDOW_AUTOSIZE); + + // Background image (white) for window. + m_image = new cv::Mat(height, width, CV_8UC3, backgroundColor); + + // Remember window params. + m_width = width; + m_height = height; + m_backgroundColor = backgroundColor; + m_horizontalScaleLineColor = scaleLineColor; + m_name = name; + + //Draw horizontal scale line. + cv::line(*m_image, cv::Point(0, height / 2), + cv::Point(width, height / 2), m_horizontalScaleLineColor, 5, cv::LINE_AA); + + // Draw grid lines. + for (int i = 0; i < height; i += 50) + { + cv::line(*m_image, cv::Point(0, i), + cv::Point(width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + } + for (int i = 0; i < width; i += 50) + { + cv::line(*m_image, cv::Point(i, 0), + cv::Point(i, height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + } +} + + + +cr::utils::PlotImpl::~PlotImpl() +{ + delete m_image; +} + + + +void cr::utils::PlotImpl::renderPlot(Plot2D plot) +{ + // Ratio/offset for horizontal axis. + float dt = 0.0f; + + // Fit signal into window. + dt = (float)m_width / (float)plot.m_length; + + // Temporary points to draw a line. + cv::Point currentPoint; + cv::Point previousPoint; + + // Update plot params with respect window. + plot.m_outMax = m_height / 2; + + // Offset value for dataset that has negative values. + if (plot.m_inMin < 0) + { + plot.m_offsetY = static_cast(plot.m_outMax - (plot.m_outMax / 2)); + } + + // 1D vector drawing. + if (plot.m_type == 0) + { + // Points on horizontal axis. + float t = 0; + // Lines between points. + for (int i = 1; i < plot.m_length; ++i) + { + // line between two points + previousPoint.x = static_cast(t); + previousPoint.y = static_cast(::map(plot.m_points1D.at(i - 1), plot.m_inMin, + plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); + t += dt; + currentPoint.x = static_cast(t); + currentPoint.y = static_cast(::map(plot.m_points1D.at(i), plot.m_inMin, + plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); + + cv::line(*m_image, previousPoint, currentPoint, plot.m_color, + plot.m_thickness, cv::LINE_AA); + } + } + // 2D vector drawing. + else + { + // Lines between points + for (int i = 1; i < plot.m_points2D.size(); ++i) + { + // line between two points + previousPoint.x = static_cast(((plot.m_points2D)[i - 1][0] - plot.m_offsetX) * dt); + previousPoint.y = static_cast(::map((plot.m_points2D)[i - 1][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); + currentPoint.x = static_cast(((plot.m_points2D)[i][0] - plot.m_offsetX) * dt); + currentPoint.y = static_cast(::map((plot.m_points2D)[i][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); + cv::line(*m_image, previousPoint, currentPoint, plot.m_color, plot.m_thickness, cv::LINE_AA); + } + } +} + + + +template +void cr::utils::PlotImpl::addPlot(std::vector& points, int id, int start, int end, + cv::Scalar color, int thickness) +{ + // Check if id is already contained. + for (const auto& pair : m_plots) + { + const int& i = pair.first; + if (i == id) + { + // Remove plot from list. + auto it = m_plots.find(i); + if (it != m_plots.end()) + m_plots.erase(it); + } + } + + // Add ploting object to list. + std::vector doubles; + for (int i =0;i(points[i])); + } + + m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness))); + + } + + + + template + void cr::utils::PlotImpl::addPlot(std::vector>& points, int id, int start, int end, + cv::Scalar color, int thickness) +{ + // Check if id is already contained. + for (const auto& pair : m_plots) + { + const int& i = pair.first; + if (i == id) + { + // Remove plot from list. + auto it = m_plots.find(i); + if (it != m_plots.end()) + m_plots.erase(it); + } + } + + // Convert points vector to double. + std::vector> doubles; + std::vector temp(2); + for (int i = 0; i < points.size(); i++) + { + temp[0] = static_cast(points[i][0]); + temp[1] = static_cast(points[i][1]); + + doubles.push_back(temp); + } + + // Add ploting object to list. + m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness))); + +} + + + +void cr::utils::PlotImpl::clean() +{ + // Clean window. + m_image->setTo(m_backgroundColor); + + // Draw horizontal scale line. + cv::line(*m_image, cv::Point(0, m_height / 2), + cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA); + + // Draw grid lines. + for (int i = 0; i < m_height; i += 50) + cv::line(*m_image, cv::Point(0, i), + cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + + for (int i = 0; i < m_width; i += 50) + cv::line(*m_image, cv::Point(i, 0), + cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + + // Clear all instances from container. + m_plots.clear(); +} + + + +void cr::utils::PlotImpl::show() +{ + // Clean window. + m_image->setTo(m_backgroundColor); + + // Draw horizontal scale line. + cv::line(*m_image, cv::Point(0, m_height / 2), + cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA); + + // Draw grid lines. + for (int i = 0; i < m_height; i += 50) + { + cv::line(*m_image, cv::Point(0, i), + cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + } + for (int i = 0; i < m_width; i += 50) + { + cv::line(*m_image, cv::Point(i, 0), + cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); + } + + // Iterate over the values (Plot2D instances) in the map + for (const auto& pair : m_plots) + { + const Plot2D& plot = pair.second; + renderPlot(plot); + } + + cv::imshow(m_name, *m_image); +} + + + +cr::utils::PlotImpl::Plot2D::Plot2D(std::vector& points, int id, + int start, int end, cv::Scalar color, int thickness) +{ + m_length = end - start; + + // Invalid range, use complete vector. + if (m_length <= 0) + { + m_length = static_cast(points.size()); + std::copy(points.begin(), points.end(), std::back_inserter(m_points1D)); + } + // Copy specified range of input vector. + else + { + // Define the range you want to copy. + std::vector::iterator startindex = points.begin() + start; + std::vector::iterator endindex = points.begin() + end; + + // Copy the range from sourceVector to destinationVector. + std::copy(startindex, endindex, std::back_inserter(m_points1D)); + } + + // Max and min values are required for vertical scaling. + m_inMax = *max_element(points.begin(), points.end()); + m_inMin = *min_element(points.begin(), points.end()); + + // Update params. + m_id = id; + m_color = color; + m_thickness = thickness; + m_type = 0; +} + + + +cr::utils::PlotImpl::Plot2D::Plot2D(std::vector>& points, int id, + int start, int end, cv::Scalar color, int thickness) +{ + m_inMax = std::numeric_limits::lowest(); + m_inMin = std::numeric_limits::max(); + m_offsetX = std::numeric_limits::max(); + m_length = end - start; + + // Invalid range, use complete vector. + if (m_length <= 0) + { + m_length = static_cast(points.size()); + std::copy(points.begin(), points.end(), std::back_inserter(m_points2D)); + } + // Copy specified range of input vector. + else + { + // Range of copy. + std::vector>::iterator startindex = points.begin() + start; + std::vector>::iterator endindex = points.begin() + end; + + // Copy the range from sourceVector to destinationVector. + std::copy(startindex, endindex, std::back_inserter(m_points2D)); + }; + + // Iterate through the vector within the specified range. + for (int i = start; i < end && i < points.size(); ++i) { + const std::vector& row = points[i]; + + if (row.size() > 1) { + double valueY = row[1]; // y component + double valueX = row[0]; // x component + + // Update maximum value using std::max + m_inMax = std::max(m_inMax, valueY); + + // Update minimum value using std::min + m_inMin = std::min(m_inMin, valueY); + + // Find the smallest value in X direction for horizontal offset. + m_offsetX = std::min(m_offsetX, valueX); + } + } + + // Update params. + m_id = id; + m_thickness = thickness; + m_color = color; + m_type = 1; +} + +cr::utils::PlotImpl::Plot2D::~Plot2D() {} + +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector& Points, int id, + int start, int end, cv::Scalar color, int thickness); + + +template void cr::utils::PlotImpl::addPlot(std::vector>& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points,int id, + int start, int end, cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, + int id, int start, int end, + cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, + int id, int start, int end, + cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, int id, + int start, int end, + cv::Scalar color, int thickness); +template void cr::utils::PlotImpl::addPlot(std::vector< std::vector>& Points, + int id, int start, int end, + cv::Scalar color, int thickness); \ No newline at end of file diff --git a/src/Impl/PlotOpenCvImpl.h b/src/Impl/PlotOpenCvImpl.h new file mode 100644 index 0000000..e1f8082 --- /dev/null +++ b/src/Impl/PlotOpenCvImpl.h @@ -0,0 +1,153 @@ +#pragma once +#include +#include +#include +#include +#include + + + +namespace cr +{ +namespace utils +{ +/** + * @brief plot class. + */ +class PlotImpl +{ +public: + + /** + * @brief Class constructor. + * @param name window name. + * @param width width of window. + * @param height height of window. + * @param backgroundColor color of background. + * @param scaleLineColor color of horizontal scale line. + */ + PlotImpl(std::string name, int width = 1280, int height = 720, + cv::Scalar backgroundColor = cv::Scalar(255, 255, 255), + cv::Scalar scaleLineColor = cv::Scalar(0, 128, 128)); + + /** + * @brief Class destructor. + */ + ~PlotImpl(); + + /** + * @brief Render plots on window. + * @param Points vector of points for plot. + * @param id ploting id for a line chart, can be used to update existing plot + or add new one. + * @param start beginning index for plotting in given vector. + * @param end ending index for plotting in given vector. + * @param color printing color of plot. + * @param thickness line thickness for plot. + */ + template + void addPlot(std::vector& points, int id, int start = 0, int end = 0, + cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + /** + * @brief Method to render plots on window. + * @param Points 2D vector of points for plot. + * @param id ploting id for a line chart, can be used to update existing plot + or add new one. + * @param start beginning index for plotting in given vector. + * @param end ending index for plotting in given vector. + * @param color printing color of plot. + * @param thickness line thickness for plot + */ + template + void addPlot(std::vector>& points, int id, + int start = 0, int end = 0, + cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + + /** + * @brief Method to clean window. + */ + void clean(); + + /** + * @brief Method to show window. + */ + void show(); + +private: + + /** + * @brief plot struct. + */ + struct Plot2D + { + /** + * @brief struct constructor. + */ + Plot2D(std::vector& points, int id, + int start, int end, cv::Scalar color, int tickness); + + /** + * @brief struct constructor. + */ + Plot2D(std::vector>& points, int id, + int start, int end, + cv::Scalar color, int tickness); + + /** + * @brief Class destructor. + */ + ~Plot2D(); + + /// Id of plot. + int m_id{ 0 }; + /// Max value from data set. + double m_inMax{ 0.0 }; + /// Min value from data set. + double m_inMin{ 0.0 }; + /// Max value for scaling. + double m_outMax{ 0.0 }; + /// Min value for scaling. + double m_outMin{ 0 }; + /// Vertical offset. + int m_offsetY{ 0 }; + /// Horizontal offset. + double m_offsetX{ 0.0 }; + /// Color of plot. + cv::Scalar m_color; + /// Tickness of plot line. + int m_thickness{ 0 }; + /// Plot type(0 - one dimentional (only vertical), 1 - two dimentional). + int m_type{ 0 }; + /// Number of points on plot. + int m_length{ 0 }; + /// Dataset for one dimentional plot. + std::vector m_points1D{}; + /// Dataset for two dimentional plot. + std::vector> m_points2D{}; + }; + + /// Background image for plotting. + cv::Mat* m_image; + /// Grid sizes in pixel for plotting background + int m_gridSize{ 50 }; + /// Window width + int m_width{ 0 }; + /// Window heigth + int m_height{ 0 }; + /// Color of background + cv::Scalar m_backgroundColor; + /// Color of horizontal scale line + cv::Scalar m_horizontalScaleLineColor; + /// Window name + std::string m_name{ "" }; + // Create a std::map to store Plot2D instances with integer IDs + std::map m_plots; + + /** + * @brief Method to render a plot on window. + * @param plot object to draw. + */ + void renderPlot(Plot2D plot); +}; +} +} \ No newline at end of file diff --git a/src/PlotOpenCv.cpp b/src/PlotOpenCv.cpp index b866870..9ef5e54 100644 --- a/src/PlotOpenCv.cpp +++ b/src/PlotOpenCv.cpp @@ -1,13 +1,6 @@ #include "PlotOpenCv.h" #include "PlotOpenCvVersion.h" - - - -template -T map(T x, T in_min, T in_max, T out_min, T out_max) -{ - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} +#include "Impl/PlotOpenCvImpl.h" @@ -19,352 +12,89 @@ std::string cr::utils::Plot::getVersion() cr::utils::Plot::Plot(std::string name, int width, int height, - cv::Scalar backgroundColor, cv::Scalar scaleLineColor) + cr::utils::PlotColor backgroundColor, cr::utils::PlotColor scaleLineColor) { - // Create a window. - cv::namedWindow(name, cv::WINDOW_AUTOSIZE); - - // Background image (white) for window. - m_image = new cv::Mat(height, width, CV_8UC3, backgroundColor); - - // Remember window params. - m_width = width; - m_height = height; - m_backgroundColor = backgroundColor; - m_horizontalScaleLineColor = scaleLineColor; - m_name = name; - //Draw horizontal scale line. - cv::line(*m_image, cv::Point(0, height / 2), - cv::Point(width, height / 2), m_horizontalScaleLineColor, 5, cv::LINE_AA); - - // Draw grid lines. - for (int i = 0; i < height; i += 50) - { - cv::line(*m_image, cv::Point(0, i), - cv::Point(width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - } - for (int i = 0; i < width; i += 50) - { - cv::line(*m_image, cv::Point(i, 0), - cv::Point(i, height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - } + m_impl = new PlotImpl(name, width, height, + cv::Scalar(backgroundColor.b, backgroundColor.g, backgroundColor.r), + cv::Scalar(scaleLineColor.b, scaleLineColor.g, scaleLineColor.r)); } cr::utils::Plot::~Plot() { - delete m_image; -} - - - -void cr::utils::Plot::renderPlot(Plot2D plot) -{ - // Ratio/offset for horizontal axis. - float dt = 0.0f; - - // Fit signal into window. - dt = (float)m_width / (float)plot.m_length; - - // Temporary points to draw a line. - cv::Point currentPoint; - cv::Point previousPoint; - - // Update plot params with respect window. - plot.m_outMax = m_height / 2; - - // Offset value for dataset that has negative values. - if (plot.m_inMin < 0) - { - plot.m_offsetY = static_cast(plot.m_outMax - (plot.m_outMax / 2)); - } - - // 1D vector drawing. - if (plot.m_type == 0) - { - // Points on horizontal axis. - float t = 0; - // Lines between points. - for (int i = 1; i < plot.m_length; ++i) - { - // line between two points - previousPoint.x = static_cast(t); - previousPoint.y = static_cast(::map(plot.m_points1D.at(i - 1), plot.m_inMin, - plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); - t += dt; - currentPoint.x = static_cast(t); - currentPoint.y = static_cast(::map(plot.m_points1D.at(i), plot.m_inMin, - plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); - - cv::line(*m_image, previousPoint, currentPoint, plot.m_color, - plot.m_thickness, cv::LINE_AA); - } - } - // 2D vector drawing. - else - { - // Lines between points - for (int i = 1; i < plot.m_points2D.size(); ++i) - { - // line between two points - previousPoint.x = static_cast(((plot.m_points2D)[i - 1][0] - plot.m_offsetX) * dt); - previousPoint.y = static_cast(::map((plot.m_points2D)[i - 1][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); - currentPoint.x = static_cast(((plot.m_points2D)[i][0] - plot.m_offsetX) * dt); - currentPoint.y = static_cast(::map((plot.m_points2D)[i][1], plot.m_inMin, plot.m_inMax, plot.m_outMax, plot.m_outMin) + plot.m_offsetY); - cv::line(*m_image, previousPoint, currentPoint, plot.m_color, plot.m_thickness, cv::LINE_AA); - } - } + delete m_impl; } template void cr::utils::Plot::addPlot(std::vector& points, int id, int start, int end, - cv::Scalar color, int thickness) + cr::utils::PlotColor color, int thickness) { - // Check if id is already contained. - for (const auto& pair : m_plots) - { - const int& i = pair.first; - if (i == id) - { - // Remove plot from list. - auto it = m_plots.find(i); - if (it != m_plots.end()) - m_plots.erase(it); - } - } - - // Add ploting object to list. - std::vector doubles; - for (int i =0;i(points[i])); - } - - m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness))); - - } + m_impl->addPlot(points, id, start, end, cv::Scalar(color.b, color.g, color.r), thickness); +} template void cr::utils::Plot::addPlot(std::vector>& points, int id, int start, int end, - cv::Scalar color, int thickness) + cr::utils::PlotColor color, int thickness) { - // Check if id is already contained. - for (const auto& pair : m_plots) - { - const int& i = pair.first; - if (i == id) - { - // Remove plot from list. - auto it = m_plots.find(i); - if (it != m_plots.end()) - m_plots.erase(it); - } - } - - // Convert points vector to double. - std::vector> doubles; - std::vector temp(2); - for (int i = 0; i < points.size(); i++) - { - temp[0] = static_cast(points[i][0]); - temp[1] = static_cast(points[i][1]); - - doubles.push_back(temp); - } - - // Add ploting object to list. - m_plots.insert(std::make_pair(id, Plot2D(doubles, id, start, end, color, thickness))); - + m_impl->addPlot(points, id, start, end, cv::Scalar(color.b, color.g, color.r), thickness); } void cr::utils::Plot::clean() { - // Clean window. - m_image->setTo(m_backgroundColor); - - // Draw horizontal scale line. - cv::line(*m_image, cv::Point(0, m_height / 2), - cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA); - - // Draw grid lines. - for (int i = 0; i < m_height; i += 50) - cv::line(*m_image, cv::Point(0, i), - cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - - for (int i = 0; i < m_width; i += 50) - cv::line(*m_image, cv::Point(i, 0), - cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - - // Clear all instances from container. - m_plots.clear(); + m_impl->clean(); } void cr::utils::Plot::show() { - // Clean window. - m_image->setTo(m_backgroundColor); - - // Draw horizontal scale line. - cv::line(*m_image, cv::Point(0, m_height / 2), - cv::Point(m_width, m_height / 2), m_horizontalScaleLineColor, 3, cv::LINE_AA); - - // Draw grid lines. - for (int i = 0; i < m_height; i += 50) - { - cv::line(*m_image, cv::Point(0, i), - cv::Point(m_width, i), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - } - for (int i = 0; i < m_width; i += 50) - { - cv::line(*m_image, cv::Point(i, 0), - cv::Point(i, m_height), cv::Scalar(0, 0, 0), 1, cv::LINE_AA); - } - - // Iterate over the values (Plot2D instances) in the map - for (const auto& pair : m_plots) - { - const Plot2D& plot = pair.second; - renderPlot(plot); - } - - cv::imshow(m_name, *m_image); -} - - - -cr::utils::Plot::Plot2D::Plot2D(std::vector& points, int id, - int start, int end, cv::Scalar color, int thickness) -{ - m_length = end - start; - - // Invalid range, use complete vector. - if (m_length <= 0) - { - m_length = static_cast(points.size()); - std::copy(points.begin(), points.end(), std::back_inserter(m_points1D)); - } - // Copy specified range of input vector. - else - { - // Define the range you want to copy. - std::vector::iterator startindex = points.begin() + start; - std::vector::iterator endindex = points.begin() + end; - - // Copy the range from sourceVector to destinationVector. - std::copy(startindex, endindex, std::back_inserter(m_points1D)); - } - - // Max and min values are required for vertical scaling. - m_inMax = *max_element(points.begin(), points.end()); - m_inMin = *min_element(points.begin(), points.end()); - - // Update params. - m_id = id; - m_color = color; - m_thickness = thickness; - m_type = 0; -} - - - -cr::utils::Plot::Plot2D::Plot2D(std::vector>& points, int id, - int start, int end, cv::Scalar color, int thickness) -{ - m_inMax = std::numeric_limits::lowest(); - m_inMin = std::numeric_limits::max(); - m_offsetX = std::numeric_limits::max(); - m_length = end - start; - - // Invalid range, use complete vector. - if (m_length <= 0) - { - m_length = static_cast(points.size()); - std::copy(points.begin(), points.end(), std::back_inserter(m_points2D)); - } - // Copy specified range of input vector. - else - { - // Range of copy. - std::vector>::iterator startindex = points.begin() + start; - std::vector>::iterator endindex = points.begin() + end; - - // Copy the range from sourceVector to destinationVector. - std::copy(startindex, endindex, std::back_inserter(m_points2D)); - }; - - // Iterate through the vector within the specified range. - for (int i = start; i < end && i < points.size(); ++i) { - const std::vector& row = points[i]; - - if (row.size() > 1) { - double valueY = row[1]; // y component - double valueX = row[0]; // x component - - // Update maximum value using std::max - m_inMax = std::max(m_inMax, valueY); - - // Update minimum value using std::min - m_inMin = std::min(m_inMin, valueY); - - // Find the smallest value in X direction for horizontal offset. - m_offsetX = std::min(m_offsetX, valueX); - } - } - - // Update params. - m_id = id; - m_thickness = thickness; - m_color = color; - m_type = 1; + m_impl->show(); } -cr::utils::Plot::Plot2D::~Plot2D() {} template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector>& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points,int id, - int start, int end, cv::Scalar color, int thickness); + int start, int end, cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, int start, int end, - cv::Scalar color, int thickness); + cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, int start, int end, - cv::Scalar color, int thickness); + cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, int start, int end, - cv::Scalar color, int thickness); + cr::utils::PlotColor color, int thickness); template void cr::utils::Plot::addPlot(std::vector< std::vector>& Points, int id, int start, int end, - cv::Scalar color, int thickness); \ No newline at end of file + cr::utils::PlotColor color, int thickness); \ No newline at end of file diff --git a/src/PlotOpenCv.h b/src/PlotOpenCv.h index 16023f0..359ae32 100644 --- a/src/PlotOpenCv.h +++ b/src/PlotOpenCv.h @@ -1,9 +1,6 @@ #pragma once #include #include -#include -#include -#include @@ -11,8 +8,29 @@ namespace cr { namespace utils { + +/// Plot implementation class. +class PlotImpl; + +/** + * @brief Plot color. +*/ +class PlotColor +{ +public: + /// Class constructor + PlotColor(int b, int g, int r) : b(b), g(g), r(r) {} + /// Blue color 0-255. + int b{0}; + /// Green color 0-255. + int g{0}; + /// Red color 0-255. + int r{0}; +}; + + /** - * @brief plot class. + * @brief Plot class. */ class Plot { @@ -20,21 +38,21 @@ class Plot /** * @brief Get string of current library version. - * @return String of current library version in format "Major.Minor.Patch". + * @return String of current library version: "Major.Minor.Patch". */ static std::string getVersion(); /** * @brief Class constructor. - * @param name window name. - * @param width width of window. - * @param height height of window. - * @param backgroundColor color of background. - * @param scaleLineColor color of horizontal scale line. + * @param name Window name. + * @param width Width of window. + * @param height Height of window. + * @param backgroundColor Background color. + * @param scaleLineColor Horizontal scale line color. */ Plot(std::string name, int width = 1280, int height = 720, - cv::Scalar backgroundColor = cv::Scalar(255, 255, 255), - cv::Scalar scaleLineColor = cv::Scalar(0, 128, 128)); + PlotColor backgroundColor = PlotColor(255, 255, 255), + PlotColor scaleLineColor = PlotColor(0, 128, 128)); /** * @brief Class destructor. @@ -43,31 +61,30 @@ class Plot /** * @brief Render plots on window. - * @param Points vector of points for plot. - * @param id ploting id for a line chart, can be used to update existing plot - or add new one. - * @param start beginning index for plotting in given vector. - * @param end ending index for plotting in given vector. + * @param Points Vector of points for plot. + * @param id Plot id for a line chart, can be used to update existing plot or add new one. + * @param start Start index for plotting in given vector. + * @param end End index for plotting in given vector. * @param color printing color of plot. * @param thickness line thickness for plot. */ template void addPlot(std::vector& points, int id, int start = 0, int end = 0, - cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + PlotColor color = PlotColor(255, 255, 255), int thickness = 1); /** * @brief Method to render plots on window. * @param Points 2D vector of points for plot. - * @param id ploting id for a line chart, can be used to update existing plot + * @param id Plot id for a line chart, can be used to update existing plot or add new one. - * @param start beginning index for plotting in given vector. - * @param end ending index for plotting in given vector. - * @param color printing color of plot. + * @param start Start index for plotting in given vector. + * @param end End index for plotting in given vector. + * @param color Printing color of plot. * @param thickness line thickness for plot */ template void addPlot(std::vector>& points, int id, - int start = 0, int end = 0, - cv::Scalar color = cv::Scalar(255, 255, 255), int thickness = 1); + int start = 0, int end = 0, + PlotColor color = PlotColor(255, 255, 255), int thickness = 1); /** * @brief Method to clean window. @@ -81,79 +98,8 @@ class Plot private: - /** - * @brief plot struct. - */ - struct Plot2D - { - /** - * @brief struct constructor. - */ - Plot2D(std::vector& points, int id, - int start, int end, cv::Scalar color, int tickness); - - /** - * @brief struct constructor. - */ - Plot2D(std::vector>& points, int id, - int start, int end, - cv::Scalar color, int tickness); - - /** - * @brief Class destructor. - */ - ~Plot2D(); - - /// Id of plot. - int m_id{ 0 }; - /// Max value from data set. - double m_inMax{ 0.0 }; - /// Min value from data set. - double m_inMin{ 0.0 }; - /// Max value for scaling. - double m_outMax{ 0.0 }; - /// Min value for scaling. - double m_outMin{ 0 }; - /// Vertical offset. - int m_offsetY{ 0 }; - /// Horizontal offset. - double m_offsetX{ 0.0 }; - /// Color of plot. - cv::Scalar m_color; - /// Tickness of plot line. - int m_thickness{ 0 }; - /// Plot type(0 - one dimentional (only vertical), 1 - two dimentional). - int m_type{ 0 }; - /// Number of points on plot. - int m_length{ 0 }; - /// Dataset for one dimentional plot. - std::vector m_points1D{}; - /// Dataset for two dimentional plot. - std::vector> m_points2D{}; - }; - - /// Background image for plotting. - cv::Mat* m_image; - /// Grid sizes in pixel for plotting background - int m_gridSize{ 50 }; - /// Window width - int m_width{ 0 }; - /// Window heigth - int m_height{ 0 }; - /// Color of background - cv::Scalar m_backgroundColor; - /// Color of horizontal scale line - cv::Scalar m_horizontalScaleLineColor; - /// Window name - std::string m_name{ "" }; - // Create a std::map to store Plot2D instances with integer IDs - std::map m_plots; - - /** - * @brief Method to render a plot on window. - * @param plot object to draw. - */ - void renderPlot(Plot2D plot); + /// Pointer to plot implementation class. + PlotImpl* m_impl; }; } } \ No newline at end of file diff --git a/src/PlotOpenCvVersion.h b/src/PlotOpenCvVersion.h index c8e45f4..5e9f81b 100644 --- a/src/PlotOpenCvVersion.h +++ b/src/PlotOpenCvVersion.h @@ -1,7 +1,7 @@ #pragma once #define PLOT_OPENCV_MAJOR_VERSION 1 -#define PLOT_OPENCV_MINOR_VERSION 0 -#define PLOT_OPENCV_PATCH_VERSION 4 +#define PLOT_OPENCV_MINOR_VERSION 2 +#define PLOT_OPENCV_PATCH_VERSION 0 -#define PLOT_OPENCV_VERSION "1.0.4" +#define PLOT_OPENCV_VERSION "1.2.0" diff --git a/test/main.cpp b/test/main.cpp index ac3e8d1..39103d7 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -11,7 +11,7 @@ int main() std::cout << "plotOpenCv v" << Plot::getVersion() << " test" << std::endl; // Create plotting window. - Plot graph("Test graph", 1280, 640, cv::Scalar(50, 50, 50)); + Plot graph("Test graph", 1280, 640, PlotColor(50, 50, 50)); // Prepare 1d vector for plotting. std::vector linePoints(9000); @@ -49,9 +49,9 @@ int main() } // Put charts on graph. - graph.addPlot(linePoints, 0, 0, 0, cv::Scalar(255, 0, 0), 1); - graph.addPlot(linePoints2, 1, 0, 0, cv::Scalar(0, 255, 0), 1); - graph.addPlot(linePoints3, 2, 0, 0, cv::Scalar(0, 0, 255), 1); + graph.addPlot(linePoints, 0, 0, 0, PlotColor(255, 0, 0), 1); + graph.addPlot(linePoints2, 1, 0, 0, PlotColor(0, 255, 0), 1); + graph.addPlot(linePoints3, 2, 0, 0, PlotColor(0, 0, 255), 1); // Prepare 2d vector for plotting. std::vector> linePoints4(9000, std::vector(2)); @@ -66,7 +66,7 @@ int main() } // Put chart on graph. - graph.addPlot(linePoints4, 3, 0, 5000, cv::Scalar(0, 0, 255), 2); + graph.addPlot(linePoints4, 3, 0, 5000, PlotColor(0, 0, 255), 2); // Show graph and wait. graph.show();