Changes to aos/vision/debug in preparation for the competition.
- blob_log-source: Support multiple key-commands for seeking to a target
also added JustCheckForTarget which can be implemented to run faster
for slow computer (will implement in followup).
- camera-source + debug_framework: Takes in camera parameters from the
caller.
- debug_window -> Scale text up on smaller monitors.
- overlay.h: Ben changed width to measure point to point on the cross.
And added circles to the pixel-lines overlay.
- tcp-source: Drop packects when the computer can't keep up.
- debug_framework -> detect smaller monitors and scale down the viewport.
Change-Id: Iae65a0799231d006b38694a8a9cb3894e252033c
diff --git a/aos/vision/debug/blob_log-source.cc b/aos/vision/debug/blob_log-source.cc
index f2bfbb5..337a764 100644
--- a/aos/vision/debug/blob_log-source.cc
+++ b/aos/vision/debug/blob_log-source.cc
@@ -9,12 +9,28 @@
#include <string>
#include "aos/vision/blob/codec.h"
+#include "aos/vision/blob/stream_view.h"
+#include "aos/vision/debug/overlay.h"
namespace aos {
namespace vision {
namespace {
+__attribute__((format(printf, 1, 2))) std::string SPrintf(const char *format,
+ ...) {
+ va_list arglist;
+ va_start(arglist, format);
+ size_t count = vsnprintf(nullptr, 0, format, arglist);
+ va_end(arglist);
+
+ char out[count + 1];
+ va_start(arglist, format);
+ vsnprintf(out, count + 1, format, arglist);
+ va_end(arglist);
+ return std::string(&out[0], &out[count]);
+}
+
long GetFileSize(const std::string &filename) {
struct stat stat_buf;
int rc = stat(filename.c_str(), &stat_buf);
@@ -45,7 +61,8 @@
}
bool ReadNext(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
- if (buf_ - &tmp_buf_[0] >= len_) return false;
+ if (buf_ - &tmp_buf_[0] + sizeof(uint32_t) >= static_cast<size_t>(len_))
+ return false;
if (prev_ != nullptr) prev_frames_.emplace_back(prev_);
prev_ = buf_;
DoRead(blob_list, fmt, timestamp);
@@ -56,7 +73,6 @@
if (prev_frames_.empty()) return false;
buf_ = prev_frames_.back();
prev_frames_.pop_back();
- buf_ += sizeof(uint32_t);
DoRead(blob_list, fmt, timestamp);
prev_ = nullptr;
return true;
@@ -64,7 +80,11 @@
private:
void DoRead(BlobList *blob_list, ImageFormat *fmt, uint64_t *timestamp) {
- buf_ += sizeof(uint32_t);
+ uint32_t size_delta = Int32Codec::Read(&buf_);
+ if (buf_ - &tmp_buf_[0] + size_delta > len_) {
+ fprintf(stderr, "Corrupted last record.\n");
+ exit(-1);
+ }
*timestamp = Int64Codec::Read(&buf_);
fmt->w = Int32Codec::Read(&buf_);
fmt->h = Int32Codec::Read(&buf_);
@@ -132,6 +152,60 @@
std::function<bool()> callback_;
};
+class FrameStats {
+ public:
+ void UpdateStats(int64_t timestamp, bool has_target) {
+ if (has_target != last_has_target_) {
+ if (has_target && timestamp > last_timestamp_) {
+ ++frame_count_;
+ }
+ if (!has_target && timestamp < last_timestamp_) {
+ --frame_count_;
+ }
+ }
+ if (has_target && first_frame_timestamp_ == -1) {
+ first_frame_timestamp_ = timestamp;
+ }
+
+ last_timestamp_ = timestamp;
+ last_has_target_ = has_target;
+ }
+
+ void SetStartTimestamp(int64_t start_timestamp) {
+ start_timestamp_ = start_timestamp;
+ }
+
+ std::string Summary() const {
+ return SPrintf(" frame_count: %ld\n time_since_first_target: %7.3f",
+ frame_count_,
+ (last_timestamp_ - GetStartTimestamp()) / 1.0e9);
+ }
+
+ int64_t GetStartTimestamp() const {
+ if (first_frame_timestamp_ != -1) return first_frame_timestamp_;
+ return start_timestamp_;
+ }
+
+ private:
+ int64_t start_timestamp_;
+ int64_t frame_count_ = 0;
+ int64_t first_frame_timestamp_ = -1;
+ int64_t last_timestamp_ = -1;
+ bool last_has_target_ = 0;
+};
+
+// TODO: display this on the screen when they press help.
+const char *kHudText2 = &R"(
+commands:
+ h - display this message.
+ space - pause / unpause at normal speed (No extra mode).
+ s - Skip forward fast to the next target.
+ p - Skip backwards fast to the previous target.
+ left_arrow - single-step backwards.
+ right_arrow - single-step forwards.
+
+)"[1];
+
class BlobLogImageSource : public ImageSource {
public:
void Init(const std::string &blob_log_filename,
@@ -144,26 +218,95 @@
cb_.Reset(1000 / 25, [this]() { return Tick(); });
frame_.ReadNext(image_source_.get());
+ start_timestamp_ = frame_.timestamp;
+ frame_stats_.SetStartTimestamp(start_timestamp_);
+
interface_->NewBlobList(frame_.blob_list, frame_.fmt);
interface_->InstallKeyPress([this](uint32_t keyval) {
if (keyval == GDK_KEY_Left) {
- frame_.ReadPrev(image_source_.get());
- interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ ReadPrevFrame();
+ bool has_target = interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ frame_stats_.UpdateStats(frame_.timestamp, has_target);
} else if (keyval == GDK_KEY_Right) {
- frame_.ReadNext(image_source_.get());
- interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ ReadNextFrame();
+ bool has_target = interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ frame_stats_.UpdateStats(frame_.timestamp, has_target);
+ } else if (keyval == GDK_KEY_space) {
+ if (mode_ != PAUSED) {
+ mode_ = PAUSED;
+ } else {
+ mode_ = NORMAL_MODE;
+ }
+ } else if (keyval == GDK_KEY_s) {
+ controller_.reset(new FastForwardUntilFrameController(this));
+ mode_ = FAST_MODE;
+ } else if (keyval == GDK_KEY_p) {
+ controller_.reset(new FastForwardUntilFrameController(this));
+ mode_ = FAST_MODE_REV;
} else {
return;
}
});
+ interface_->viewer()->AddOverlay(&overlay_);
+ overlay_.draw_fn = [this](RenderInterface *cr, double w, double h) {
+ (void)w;
+ (void)h;
+ cr->SetSourceRGB(1, 0, 0);
+ auto text = SPrintf(" time: %7.3f\n",
+ (frame_.timestamp - start_timestamp_) / 1.0e9);
+ text += frame_stats_.Summary();
+ cr->Text(2, h - 100, 0, 0, text);
+ };
}
bool Tick() {
- frame_.ReadNext(image_source_.get());
- interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ if (need_timestamp_print_) {
+ fprintf(stderr, "time: %g\n",
+ (frame_.timestamp - start_timestamp_) / 1.0e9);
+ need_timestamp_print_ = false;
+ }
+ for (int i = 0; i < GetSpeed(); ++i) {
+ if (Direction()) {
+ ReadNextFrame();
+ } else {
+ ReadPrevFrame();
+ }
+ bool has_target =
+ interface_->JustCheckForTarget(frame_.blob_list, frame_.fmt);
+ frame_stats_.UpdateStats(frame_.timestamp, has_target);
+ if (controller_) {
+ controller_->NewFrame(has_target);
+ }
+ // Draw on the last frame:
+ if (i + 1 >= GetSpeed()) {
+ interface_->NewBlobList(frame_.blob_list, frame_.fmt);
+ }
+ }
return true;
}
+ int GetSpeed() {
+ if (mode_ == PAUSED) return 0;
+ if (mode_ == NORMAL_MODE) return 1;
+ if (mode_ == FAST_MODE || mode_ == FAST_MODE_REV) return 60;
+ return 0;
+ }
+
+ bool Direction() {
+ if (mode_ == FAST_MODE_REV) return false;
+ return true;
+ }
+
+ void ReadNextFrame() {
+ frame_.ReadNext(image_source_.get());
+ need_timestamp_print_ = true;
+ }
+
+ void ReadPrevFrame() {
+ frame_.ReadPrev(image_source_.get());
+ need_timestamp_print_ = true;
+ }
+
const char *GetHelpMessage() override {
return &R"(
format_spec is the name of a file in blob list format.
@@ -172,10 +315,50 @@
}
private:
+ bool need_timestamp_print_ = true;
+ uint64_t start_timestamp_ = 0;
+
+ class Controller {
+ public:
+ virtual ~Controller() {}
+ virtual void NewFrame(bool has_target) = 0;
+ };
+
+ class FastForwardUntilFrameController : public Controller {
+ public:
+ FastForwardUntilFrameController(BlobLogImageSource *proxy)
+ : proxy_(proxy) {}
+
+ void NewFrame(bool has_target) override {
+ if (!has_target) inside_target = false;
+ if (!inside_target && has_target) {
+ proxy_->mode_ = PAUSED;
+ proxy_->controller_.reset(nullptr);
+ }
+ }
+
+ BlobLogImageSource *proxy_;
+ bool inside_target = true;
+ };
+
+ std::unique_ptr<Controller> controller_;
+
+ FrameStats frame_stats_;
+
+ enum Mode {
+ PAUSED,
+ NORMAL_MODE,
+ FAST_MODE,
+ FAST_MODE_REV,
+ };
+ Mode mode_ = PAUSED;
+
+ // LambdaOverlay text_overlay_;
TimeoutCallback cb_;
DebugFrameworkInterface *interface_ = nullptr;
std::unique_ptr<InputFile> image_source_;
BlobStreamFrame frame_;
+ LambdaOverlay overlay_;
};
REGISTER_IMAGE_SOURCE("blob_log", BlobLogImageSource);