Skip to content

Commit df92f20

Browse files
committed
Add naive (i.e., direct) logging of raw images
1 parent af64638 commit df92f20

File tree

9 files changed

+591
-2
lines changed

9 files changed

+591
-2
lines changed

.clang-format

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
BasedOnStyle: Google
2+
3+
# Make it slightly more similar to Rust.
4+
# Based loosely on https://gist.github.com/YodaEmbedding/c2c77dc693d11f3734d78489f9a6eea4
5+
AccessModifierOffset: -2
6+
AlignAfterOpenBracket: BlockIndent
7+
AllowAllArgumentsOnNextLine: false
8+
AllowShortBlocksOnASingleLine: false
9+
AllowShortCaseLabelsOnASingleLine: false
10+
AllowShortFunctionsOnASingleLine: Empty
11+
AllowShortIfStatementsOnASingleLine: Never
12+
AlwaysBreakAfterReturnType: None
13+
AlwaysBreakBeforeMultilineStrings: true
14+
BinPackArguments: false
15+
BreakStringLiterals: false
16+
ColumnLimit: 100
17+
ContinuationIndentWidth: 4
18+
DerivePointerAlignment: false
19+
EmptyLineBeforeAccessModifier: LogicalBlock
20+
IndentWidth: 4
21+
IndentWrappedFunctionNames: true
22+
InsertTrailingCommas: Wrapped
23+
MaxEmptyLinesToKeep: 1
24+
NamespaceIndentation: All
25+
PointerAlignment: Left
26+
ReflowComments: false
27+
SeparateDefinitionBlocks: Always
28+
SpacesBeforeTrailingComments: 1
29+
30+
# Don't change include blocks, we want to control this manually.
31+
# Sorting headers however is allowed as all our headers should be standalone.
32+
IncludeBlocks: Preserve
33+
SortIncludes: CaseInsensitive

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
build
2+
compile_commands.json
3+
*.vrs

CMakeLists.txt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
3+
project(vrs_example LANGUAGES CXX)
4+
5+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
6+
7+
# if(NOT DEFINED CMAKE_CXX_STANDARD)
8+
# endif()
9+
10+
set(CMAKE_CXX_STANDARD 17)
11+
12+
include(FetchContent)
13+
FetchContent_Declare(rerun_sdk URL https://build.rerun.io/commit/f3a5ae2/rerun_cpp_sdk.zip)
14+
FetchContent_MakeAvailable(rerun_sdk)
15+
16+
include(FetchContent)
17+
FetchContent_Declare(vrslib URL https://github.com/facebookresearch/vrs/archive/refs/tags/v1.1.0.zip)
18+
FetchContent_MakeAvailable(vrslib)
19+
20+
find_package(Eigen3 REQUIRED)
21+
find_package(OpenCV REQUIRED)
22+
23+
add_executable(log_vrs src/main.cpp src/FramePlayer.cpp src/utils.cpp)
24+
target_link_libraries(log_vrs Eigen3::Eigen ${OpenCV_LIBS} rerun_sdk vrslib vrs_utils)
25+
target_include_directories(log_vrs PRIVATE src)

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1-
# cpp-example-vrs
2-
Example of loading a VRS file to Rerun using the C++ SDK
1+
# C++ Example: VRS Viewer
2+
3+
This is an example that shows how to use [Rerun](https://github.com/rerun-io/rerun)'s C++ API to log and view VRS files.
4+
5+
## How to build and run
6+
7+
```bash
8+
mkdir build
9+
cd build
10+
cmake ..
11+
cmake --build .
12+
```

src/FramePlayer.cpp

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/* Modified from https://github.com/facebookresearch/vrs
2+
*
3+
* Copyright (c) Meta Platforms, Inc. and affiliates.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#include "FramePlayer.h"
19+
#include "utils.h"
20+
21+
#include <iostream>
22+
#include <sstream>
23+
24+
#include <vrs/IndexRecord.h>
25+
26+
namespace rerun_vrs {
27+
28+
using namespace std;
29+
30+
RerunFramePlayer::RerunFramePlayer(StreamId id, rerun::RecordingStream& rec)
31+
: id_{id}, rec_{rec} {}
32+
33+
bool RerunFramePlayer::onDataLayoutRead(
34+
const CurrentRecord& record, size_t blockIndex, DataLayout& layout
35+
) {
36+
if (!enabled_)
37+
return false;
38+
39+
/* std::cout << "onDataLayoutRead " << std::endl; */
40+
ostringstream buffer;
41+
layout.printLayoutCompact(buffer);
42+
/* std::cout << buffer.str() << std::endl; */
43+
// TODO write this information to a markdown file
44+
45+
// TODO figure out image width and height ?
46+
47+
/* descriptions_.setDescription(record.recordType, blockIndex, text); */
48+
/* if (firstImage_ && record.recordType == Record::Type::CONFIGURATION) { */
49+
/* vrs::DataPieceString* deviceType =
50+
* layout.findDataPieceString("device_type"); */
51+
/* if (deviceType != nullptr) { */
52+
/* widget_->setDeviceType(deviceType->get()); */
53+
/* } */
54+
/* } */
55+
return true; // read next blocks, if any
56+
}
57+
58+
bool RerunFramePlayer::onImageRead(
59+
const CurrentRecord& record, size_t /*blockIndex*/, const ContentBlock& contentBlock
60+
) {
61+
/* std::cout << "onImageRead" << std::endl; */
62+
const auto& spec = contentBlock.image();
63+
shared_ptr<PixelFrame> frame = getFrame(true);
64+
const auto& imageFormat = spec.getImageFormat();
65+
const auto& imageFormatStr = spec.getImageFormatAsString();
66+
bool frameValid = false;
67+
/* std::cout << spec.getWidth() << "x" << spec.getHeight() << " " */
68+
/* << spec.getPixelFormatAsString() << " " << spec.getImageFormatAsString() */
69+
/* << std::endl; */
70+
71+
if (imageFormat == vrs::ImageFormat::RAW) {
72+
frameValid = PixelFrame::readRawFrame(frame, record.reader, spec);
73+
if (frameValid) {
74+
// Log image to Rerun
75+
// NOTE currently we need to construct a vector to log an image, this will
76+
// change in the future, see https://github.com/rerun-io/rerun/issues/3794
77+
78+
rec_.log(
79+
add_quotes(id_.getName()).c_str(),
80+
rerun::Image(
81+
{spec.getHeight(), spec.getWidth(), spec.getChannelCountPerPixel()},
82+
std::move(frame->getBuffer())
83+
)
84+
);
85+
} else {
86+
std::cout << "Failed reading raw frame." << std::endl;
87+
}
88+
/* if (!firstImage_ && frameValid) { */
89+
/* job = make_unique<ImageJob>(vrs::ImageFormat::RAW); */
90+
/* } */
91+
} else {
92+
std::cout << "Image format \"" << imageFormatStr
93+
<< "\" not supported. Disabling player." << std::endl;
94+
enabled_ = false;
95+
}
96+
return true; // read next blocks, if any
97+
98+
/* } else if (imageFormat_ == vrs::ImageFormat::VIDEO) { */
99+
/* // Video codec decompression happens here, but pixel format conversion is
100+
* asynchronous */
101+
/* PixelFrame::init(frame, contentBlock.image()); */
102+
/* frameValid = (tryToDecodeFrame(*frame, record, contentBlock) == 0); */
103+
/* if (!firstImage_ && frameValid) { */
104+
/* job = make_unique<ImageJob>(vrs::ImageFormat::VIDEO); */
105+
/* } */
106+
/* } else { */
107+
/* if (firstImage_) { */
108+
/* frameValid = PixelFrame::readFrame(frame, record.reader, contentBlock);
109+
*/
110+
/* } else { */
111+
/* // decoding & pixel format conversion happen asynchronously */
112+
/* job = make_unique<ImageJob>(imageFormat_); */
113+
/* job->buffer.resize(contentBlock.getBlockSize()); */
114+
/* frameValid = (record.reader->read(job->buffer) == 0); */
115+
/* } */
116+
/* } */
117+
/* if (frameValid && job) { */
118+
/* job->frame = std::move(frame); */
119+
/* imageJobs_.startThreadIfNeeded(&FramePlayer::imageJobsThreadActivity,
120+
* this); */
121+
/* imageJobs_.sendJob(std::move(job)); */
122+
/* return true; */
123+
/* } */
124+
/* if (firstImage_) { */
125+
/* fmt::print( */
126+
/* "Found '{} - {}': {}, {}", */
127+
/* record.streamId.getNumericName(), */
128+
/* record.streamId.getTypeName(), */
129+
/* getCurrentRecordFormatReader()->recordFormat.asString(), */
130+
/* spec.asString()); */
131+
/* if (frameValid && spec.getImageFormat() != vrs::ImageFormat::RAW) { */
132+
/* fmt::print(" - {}", frame->getSpec().asString()); */
133+
/* } */
134+
/* blankMode_ = false; */
135+
/* } */
136+
/* if (frameValid) { */
137+
/* convertFrame(frame); */
138+
/* if (firstImage_) { */
139+
/* if (needsConvertedFrame_) { */
140+
/* fmt::print(" -> {}", frame->getSpec().asString()); */
141+
/* } */
142+
/* if (estimatedFps_ != 0) { */
143+
/* fmt::print(", {} fps", estimatedFps_); */
144+
/* } */
145+
/* frame->blankFrame(); */
146+
/* blankMode_ = true; */
147+
/* } */
148+
/* widget_->swapImage(frame); */
149+
/* } */
150+
/* recycle(frame, !needsConvertedFrame_); */
151+
/* if (firstImage_) { */
152+
/* fmt::print("\n"); */
153+
/* firstImage_ = false; */
154+
/* } */
155+
}
156+
157+
void RerunFramePlayer::convertFrame(shared_ptr<PixelFrame>& frame) {
158+
if (blankMode_) {
159+
makeBlankFrame(frame);
160+
} else {
161+
shared_ptr<PixelFrame> convertedFrame =
162+
needsConvertedFrame_ ? getFrame(false) : nullptr;
163+
PixelFrame::normalizeFrame(frame, convertedFrame, false);
164+
needsConvertedFrame_ = (frame != convertedFrame); // for next time!
165+
if (needsConvertedFrame_) {
166+
recycle(frame, true);
167+
frame = std::move(convertedFrame);
168+
}
169+
}
170+
}
171+
172+
void RerunFramePlayer::makeBlankFrame(shared_ptr<PixelFrame>& frame) {
173+
frame->init(vrs::PixelFormat::GREY8, frame->getWidth(), frame->getHeight());
174+
frame->blankFrame();
175+
}
176+
177+
shared_ptr<PixelFrame> RerunFramePlayer::getFrame(bool inputNotConvertedFrame) {
178+
vector<shared_ptr<PixelFrame>>& frames =
179+
inputNotConvertedFrame ? inputFrames_ : convertedframes_;
180+
if (frames.empty()) {
181+
return nullptr;
182+
}
183+
shared_ptr<PixelFrame> frame = std::move(frames.back());
184+
frames.pop_back();
185+
return frame;
186+
}
187+
188+
void RerunFramePlayer::recycle(shared_ptr<PixelFrame>& frame, bool inputNotConvertedFrame) {
189+
if (frame) {
190+
{
191+
vector<shared_ptr<PixelFrame>>& frames =
192+
inputNotConvertedFrame ? inputFrames_ : convertedframes_;
193+
if (frames.size() < 10) {
194+
frames.emplace_back(std::move(frame));
195+
}
196+
}
197+
frame.reset();
198+
}
199+
}
200+
201+
void RerunFramePlayer::imageJobsThreadActivity() {
202+
unique_ptr<ImageJob> job;
203+
while (imageJobs_.waitForJob(job)) {
204+
shared_ptr<PixelFrame> frame = std::move(job->frame);
205+
// if we're behind, we just drop images except the last one!
206+
while (imageJobs_.getJob(job)) {
207+
recycle(frame, true);
208+
frame = std::move(job->frame);
209+
}
210+
bool frameValid = false;
211+
if (job->imageFormat == vrs::ImageFormat::RAW ||
212+
job->imageFormat == vrs::ImageFormat::VIDEO) {
213+
frameValid = (frame != nullptr);
214+
} else {
215+
if (!frame) {
216+
frame = make_shared<PixelFrame>();
217+
}
218+
frameValid = frame->readCompressedFrame(job->buffer, job->imageFormat);
219+
}
220+
if (frameValid) {
221+
convertFrame(frame);
222+
/* widget_->swapImage(frame); */
223+
}
224+
recycle(frame, !frameValid || !needsConvertedFrame_);
225+
}
226+
}
227+
228+
} // namespace rerun_vrs

src/FramePlayer.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/* Modified from https://github.com/facebookresearch/vrs
2+
*
3+
* Copyright (c) Meta Platforms, Inc. and affiliates.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
#pragma once
19+
20+
#include <memory>
21+
22+
#include <vrs/helpers/JobQueue.h>
23+
#include <vrs/RecordFormat.h>
24+
#include <vrs/utils/PixelFrame.h>
25+
#include <vrs/utils/VideoFrameHandler.h>
26+
#include <vrs/utils/VideoRecordFormatStreamPlayer.h>
27+
#include <rerun.hpp>
28+
29+
/* #include "MetaDataCollector.h" */
30+
31+
enum class FileReaderState;
32+
33+
namespace rerun_vrs {
34+
35+
using ::vrs::ContentBlock;
36+
using ::vrs::CurrentRecord;
37+
using ::vrs::DataLayout;
38+
using ::vrs::StreamId;
39+
using ::vrs::StreamPlayer;
40+
using ::vrs::utils::PixelFrame;
41+
using ::vrs::utils::VideoRecordFormatStreamPlayer;
42+
43+
struct ImageJob {
44+
ImageJob(vrs::ImageFormat imageFormat) : imageFormat{imageFormat} {}
45+
46+
vrs::ImageFormat imageFormat;
47+
std::shared_ptr<PixelFrame> frame;
48+
std::vector<uint8_t> buffer;
49+
};
50+
51+
class RerunFramePlayer : public VideoRecordFormatStreamPlayer {
52+
public:
53+
explicit RerunFramePlayer(StreamId id, rerun::RecordingStream& rec);
54+
55+
bool onDataLayoutRead(const CurrentRecord& r, size_t blockIndex, DataLayout&) override;
56+
bool onImageRead(const CurrentRecord& r, size_t blockIndex, const ContentBlock&) override;
57+
58+
StreamId getId() const {
59+
return id_;
60+
}
61+
62+
void setEstimatedFps(int estimatedFps) {
63+
estimatedFps_ = estimatedFps;
64+
}
65+
66+
void imageJobsThreadActivity();
67+
68+
private:
69+
rerun::RecordingStream& rec_;
70+
std::vector<std::shared_ptr<PixelFrame>> inputFrames_;
71+
std::vector<std::shared_ptr<PixelFrame>> convertedframes_;
72+
bool needsConvertedFrame_{false};
73+
StreamId id_;
74+
/* MetaDataCollector descriptions_; */
75+
bool blankMode_{true};
76+
bool enabled_{true};
77+
bool firstImage_{true};
78+
int estimatedFps_;
79+
/* Fps dataFps_; */
80+
FileReaderState state_{};
81+
82+
vrs::JobQueueWithThread<std::unique_ptr<ImageJob>> imageJobs_;
83+
84+
void convertFrame(std::shared_ptr<PixelFrame>& frame);
85+
void makeBlankFrame(std::shared_ptr<PixelFrame>& frame);
86+
std::shared_ptr<PixelFrame> getFrame(bool inputNotConvertedFrame);
87+
void recycle(std::shared_ptr<PixelFrame>& frame, bool inputNotConvertedFrame);
88+
};
89+
90+
} // namespace rerun_vrs

0 commit comments

Comments
 (0)