blob: 0122c94216bc4e3cd586c0d7f5d1a81b0cd28bdf [file] [log] [blame]
#ifndef _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_
#define _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_
#include <string>
#include <vector>
#include "aos/vision/image/image_types.h"
#include "aos/vision/math/segment.h"
#include "aos/vision/math/vector.h"
namespace aos {
namespace vision {
// Abstract away rendering to avoid compiling gtk for arm.
// This should match a reduced cairo rendering api.
class RenderInterface {
public:
RenderInterface() {}
RenderInterface(RenderInterface &&other) = delete;
RenderInterface(const RenderInterface &other) = delete;
~RenderInterface() {}
virtual void Translate(double x, double y) = 0;
virtual void SetSourceRGB(double r, double g, double b) = 0;
virtual void MoveTo(double x, double y) = 0;
virtual void LineTo(double x, double y) = 0;
virtual void Circle(double x, double y, double r) = 0;
// negative in x, y, text_x, text_y measures from max in those value
virtual void Text(int x, int y, int text_x, int text_y,
const std::string &text) = 0;
virtual void Stroke() = 0;
};
// Interface for a list of overlays to be drawn onto a debug image.
// These will be passed into the running vision algorithms to output debug info,
// so they must not have costly side-effects.
class OverlayBase {
public:
OverlayBase() {}
virtual ~OverlayBase() {}
// Draws this overlay to the given canvas.
virtual void Draw(RenderInterface *render, double /* width */,
double /* height */) = 0;
// Clears the entire overlay.
virtual void Reset() = 0;
PixelRef color = {255, 0, 0};
double scale = 1.0;
};
// A lambda that renders directly to the render interface.
class LambdaOverlay : public OverlayBase {
public:
std::function<void(RenderInterface *, double, double)> draw_fn;
void Draw(RenderInterface *render, double width, double height) override {
if (draw_fn) draw_fn(render, width, height);
}
void Reset() override {}
};
// Lines rendered in a coordinate system where the origin is the center
// of the screen
class LinesOverlay : public OverlayBase {
public:
LinesOverlay() : OverlayBase() {}
~LinesOverlay() {}
// build a segment for this line
void add_line(Vector<2> st, Vector<2> ed) { add_line(st, ed, color); }
// build a segment for this line
void add_line(Vector<2> st, Vector<2> ed, PixelRef newColor) {
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(st, ed), newColor));
}
void add_point(Vector<2> pt) { add_point(pt, color); }
// add a new point connected to the last point in the line
void add_point(Vector<2> pt, PixelRef newColor) {
if (lines_.empty()) {
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(pt, pt), newColor));
} else {
Vector<2> st = lines_.back().first.B();
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(st, pt), newColor));
}
}
void Draw(RenderInterface *render, double w, double h) override {
render->Translate(w / 2.0, h / 2.0);
for (const auto &ln : lines_) {
PixelRef localColor = ln.second;
render->SetSourceRGB(localColor.r / 255.0, localColor.g / 255.0,
localColor.b / 255.0);
render->MoveTo(scale * ln.first.A().x(), -scale * ln.first.A().y());
render->LineTo(scale * ln.first.B().x(), -scale * ln.first.B().y());
render->Stroke();
}
}
// Empting the list will blank the whole overlay
void Reset() override { lines_.clear(); }
private:
// lines in this over lay
std::vector<std::pair<Segment<2>, PixelRef>> lines_;
};
// Lines rendered in pixel coordinates (Should match up with the screen.)
class PixelLinesOverlay : public OverlayBase {
public:
PixelLinesOverlay() : OverlayBase() {}
~PixelLinesOverlay() {}
// build a segment for this line
void AddLine(Vector<2> st, Vector<2> ed) { AddLine(st, ed, color); }
// build a segment for this line
void AddLine(Vector<2> st, Vector<2> ed, PixelRef newColor) {
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(st, ed), newColor));
}
void DrawCross(aos::vision::Vector<2> center, int width,
aos::vision::PixelRef color) {
using namespace aos::vision;
AddLine(Vector<2>(center.x() - width / 2, center.y()),
Vector<2>(center.x() + width / 2, center.y()), color);
AddLine(Vector<2>(center.x(), center.y() - width / 2),
Vector<2>(center.x(), center.y() + width / 2), color);
}
void DrawBBox(const ImageBBox &box, aos::vision::PixelRef color) {
using namespace aos::vision;
AddLine(Vector<2>(box.minx, box.miny), Vector<2>(box.maxx, box.miny),
color);
AddLine(Vector<2>(box.maxx, box.miny), Vector<2>(box.maxx, box.maxy),
color);
AddLine(Vector<2>(box.maxx, box.maxy), Vector<2>(box.minx, box.maxy),
color);
AddLine(Vector<2>(box.minx, box.maxy), Vector<2>(box.minx, box.miny),
color);
}
// Build a circle as a point and radius.
void DrawCircle(Vector<2> center, double radius, PixelRef newColor) {
circles_.emplace_back(std::pair<Vector<2>, std::pair<double, PixelRef>>(
center, std::pair<double, PixelRef>(radius, newColor)));
}
void StartNewProfile() { start_profile = true; }
// add a new point connected to the last point in the line
void AddPoint(Vector<2> pt, PixelRef newColor) {
if (lines_.empty() || start_profile) {
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(pt, pt), newColor));
start_profile = false;
} else {
Vector<2> st = lines_.back().first.B();
lines_.emplace_back(
std::pair<Segment<2>, PixelRef>(Segment<2>(st, pt), newColor));
}
}
void Draw(RenderInterface *render, double /*width*/,
double /*hieght*/) override {
for (const auto &ln : lines_) {
PixelRef localColor = ln.second;
render->SetSourceRGB(localColor.r / 255.0, localColor.g / 255.0,
localColor.b / 255.0);
render->MoveTo(ln.first.A().x(), ln.first.A().y());
render->LineTo(ln.first.B().x(), ln.first.B().y());
render->Stroke();
}
for (const auto &circle : circles_) {
PixelRef localColor = circle.second.second;
render->SetSourceRGB(localColor.r / 255.0, localColor.g / 255.0,
localColor.b / 255.0);
render->Circle(circle.first.x(), circle.first.y(), circle.second.first);
render->Stroke();
}
}
// Empting the list will blank the whole overlay.
void Reset() override {
lines_.clear();
circles_.clear();
}
private:
// Lines in this overlay.
std::vector<std::pair<Segment<2>, PixelRef>> lines_;
std::vector<std::pair<Vector<2>, std::pair<double, PixelRef>>> circles_;
bool start_profile = false;
};
// Circles rendered in a coordinate system where the origin is the center
// of the screen.
class CircleOverlay : public OverlayBase {
public:
CircleOverlay() : OverlayBase() {}
~CircleOverlay() {}
// build a circle as a point and radius
std::pair<Vector<2>, double> *add_circle(Vector<2> center, double radius) {
circles_.emplace_back(std::pair<Vector<2>, double>(center, radius));
return &(circles_.back());
}
void Draw(RenderInterface *render, double w, double h) {
render->Translate(w / 2.0, h / 2.0);
render->SetSourceRGB(color.r / 255.0, color.g / 255.0, color.b / 255.0);
for (const auto &circle : circles_) {
render->Circle(scale * circle.first.x(), -scale * circle.first.y(),
scale * circle.second);
render->Stroke();
}
}
// empting the list will blank the whole overlay
void Reset() { circles_.clear(); }
private:
// circles in this overlay
std::vector<std::pair<Vector<2>, double>> circles_;
};
} // vision
} // aos
#endif // _AOS_VISION_IMAGE_DEBUG_OVERLAY_H_