Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix + cimbar_recv, a test decoder app + ... #61

Merged
merged 10 commits into from
Mar 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ set(PROJECTS

src/exe/cimbar
src/exe/cimbar_extract
src/exe/cimbar_recv
src/exe/cimbar_send
src/exe/build_image_assets
)
Expand Down
39 changes: 39 additions & 0 deletions src/exe/cimbar_recv/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
cmake_minimum_required(VERSION 3.10)

project(cimbar_recv)

set (SOURCES
recv.cpp
)

add_executable (
cimbar_recv
${SOURCES}
)

target_link_libraries(cimbar_recv

cimb_translator
extractor

correct_static
wirehair
zstd

GL
glfw
${OPENCV_LIBS}
opencv_videoio
)

add_custom_command(
TARGET cimbar_recv POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:cimbar_recv> cimbar_recv.dbg
COMMAND ${CMAKE_STRIP} -g $<TARGET_FILE:cimbar_recv>
)

install(
TARGETS cimbar_recv
DESTINATION bin
)

146 changes: 146 additions & 0 deletions src/exe/cimbar_recv/recv.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/* This code is subject to the terms of the Mozilla Public License, v.2.0. http://mozilla.org/MPL/2.0/. */
#include "cimb_translator/Config.h"
#include "compression/zstd_decompressor.h"
#include "encoder/Decoder.h"
#include "extractor/Extractor.h"
#include "fountain/fountain_decoder_sink.h"
#include "gui/window_glfw.h"

#include "cxxopts/cxxopts.hpp"
#include "serialize/str.h"

#include <GLFW/glfw3.h>
#include <opencv2/videoio.hpp>

#include <chrono>
#include <iostream>
#include <string>
#include <thread>
using std::string;

namespace {

template <typename TP>
TP wait_for_frame_time(unsigned delay, const TP& start)
{
unsigned millis = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start).count();
if (delay > millis)
std::this_thread::sleep_for(std::chrono::milliseconds(delay-millis));
return std::chrono::high_resolution_clock::now();
}
}


int main(int argc, char** argv)
{
cxxopts::Options options("cimbar video decoder", "Use the camera to decode data!");

unsigned colorBits = cimbar::Config::color_bits();
unsigned ecc = cimbar::Config::ecc_bytes();
unsigned defaultFps = 60;
options.add_options()
("i,in", "Video source.", cxxopts::value<string>())
("o,out", "Output directory (decoding).", cxxopts::value<string>())
("c,colorbits", "Color bits. [0-3]", cxxopts::value<int>()->default_value(turbo::str::str(colorBits)))
("e,ecc", "ECC level", cxxopts::value<unsigned>()->default_value(turbo::str::str(ecc)))
("f,fps", "Target decode FPS", cxxopts::value<unsigned>()->default_value(turbo::str::str(defaultFps)))
("h,help", "Print usage")
;
options.show_positional_help();
options.parse_positional({"in", "out"});
options.positional_help("<in> <out>");

auto result = options.parse(argc, argv);
if (result.count("help") or !result.count("in") or !result.count("out"))
{
std::cout << options.help() << std::endl;
return 0;
}

string source = result["in"].as<string>();
string outpath = result["out"].as<string>();

colorBits = std::min(3, result["colorbits"].as<int>());
ecc = result["ecc"].as<unsigned>();
unsigned fps = result["fps"].as<unsigned>();
if (fps == 0)
fps = defaultFps;
unsigned delay = 1000 / fps;

cv::VideoCapture vc(source.c_str());
if (!vc.isOpened())
{
std::cerr << "failed to open video device :(" << std::endl;
return 70;
}
vc.set(cv::CAP_PROP_FRAME_WIDTH, 1920);
vc.set(cv::CAP_PROP_FRAME_HEIGHT, 1200);
vc.set(cv::CAP_PROP_FPS, fps);

// set max camera res, and use aspect ratio for window size...

std::cout << fmt::format("width: {}, height {}, exposure {}", vc.get(cv::CAP_PROP_FRAME_WIDTH), vc.get(cv::CAP_PROP_FRAME_HEIGHT), vc.get(cv::CAP_PROP_EXPOSURE)) << std::endl;

double ratio = vc.get(cv::CAP_PROP_FRAME_WIDTH) / vc.get(cv::CAP_PROP_FRAME_HEIGHT);
int height = 600;
int width = height * ratio;
std::cout << "got dimensions " << width << "," << height << std::endl;

cimbar::window_glfw window(width, height, "cimbar_recv");
if (!window.is_good())
{
std::cerr << "failed to create window :(" << std::endl;
return 70;
}
window.auto_scale_to_window();

Extractor ext;
Decoder dec;

unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc);
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize);

cv::Mat mat;

unsigned count = 0;
std::chrono::time_point start = std::chrono::high_resolution_clock::now();
while (true)
{
++count;

// delay, then try to read frame
start = wait_for_frame_time(delay, start);
if (window.should_close())
break;

if (!vc.read(mat))
{
std::cerr << "failed to read from cam" << std::endl;
continue;
}

cv::UMat img = mat.getUMat(cv::ACCESS_RW);
cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);

// draw some stats on mat?
window.show(mat, 0);

// extract
bool shouldPreprocess = true;
int res = ext.extract(img, img);
if (!res)
{
//std::cerr << "no extract " << mat.cols << "," << mat.rows << std::endl;
continue;
}
else if (res == Extractor::NEEDS_SHARPEN)
shouldPreprocess = true;

// decode
int bytes = dec.decode_fountain(img, sink, shouldPreprocess);
if (bytes > 0)
std::cerr << "got some bytes " << bytes << std::endl;
}

return 0;
}
17 changes: 10 additions & 7 deletions src/lib/extractor/Scanner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace {
{
if (i < 0)
i = N-1;
else if (i > N-1)
else if (i >= N)
i = 0;
return i;
}
Expand Down Expand Up @@ -115,24 +115,27 @@ bool Scanner::sort_top_to_bottom(std::vector<Anchor>& anchors)

// because of how we ordered our edges, the index of the longest edge is also the index of the anchor opposite it.
int top_left = get_longest_edge(edges);
int top_right;
int top_right, bottom_left;

// now, we need to find the order of the other two:
const point<int>& departing_edge = edges[fix_index<3>(top_left - 1)];
point<int> incoming_edge = edges[fix_index<3>(top_left + 1)];
incoming_edge = {-incoming_edge.y(), incoming_edge.x()}; // rotate
point<int> overlap = departing_edge - incoming_edge;

if (overlap.dot(overlap) < edges[departing_edge].dot(edges[departing_edge]))
if (overlap.dot(overlap) < departing_edge.dot(departing_edge))
{
top_right = fix_index<3>(top_left + 1);
bottom_left = fix_index<3>(top_left - 1);
}
else
{
top_right = fix_index<3>(top_left - 1);
bottom_left = fix_index<3>(top_left + 1);
}

// apply the order.
if (&anchors[0] != &anchors[top_left])
std::swap(anchors[0], anchors[top_left]);
if (top_left != 1 and top_right != 1)
std::swap(anchors[1], anchors[2]);
anchors = {anchors[top_left], anchors[top_right], anchors[bottom_left]};
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ground zero -- tried to get clever, wrote a bad test, convinced myself wrong logic was right... 😡

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/extractor/Scanner.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class Scanner
public: // other interesting methods
std::vector<Anchor> deduplicate_candidates(const std::vector<Anchor>& candidates) const;
unsigned filter_candidates(std::vector<Anchor>& candidates) const;
bool sort_top_to_bottom(std::vector<Anchor>& anchors);
static bool sort_top_to_bottom(std::vector<Anchor>& anchors);

template <typename SCANTYPE>
void t1_scan_rows(std::function<void(const Anchor&)> fun, int skip=-1, int y=-1, int yend=-1, int xstart=-1, int xend=-1) const;
Expand Down
28 changes: 18 additions & 10 deletions src/lib/extractor/test/ScannerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,34 +210,42 @@ TEST_CASE( "ScannerTest/testScanEdges", "[unit]" )

TEST_CASE( "ScannerTest/testSortTopToBottom", "[unit]" )
{
cv::Mat img = TestCimbar::loadSample("6bit/4_30_f0_627.jpg");
Scanner sc(img);

std::vector<Anchor> candidates;
candidates.push_back(Anchor(300, 360, 100, 160));
candidates.push_back(Anchor(300, 360, 300, 360));
candidates.push_back(Anchor(100, 160, 300, 360));
assertTrue( sc.sort_top_to_bottom(candidates) ); // make this a static function?
assertTrue( Scanner::sort_top_to_bottom(candidates) );

assertEquals(
"330+-30,330+-30 330+-30,130+-30 130+-30,330+-30",
turbo::str::join(candidates)
"330+-30,330+-30 130+-30,330+-30 330+-30,130+-30",
turbo::str::join(candidates)
);
}

TEST_CASE( "ScannerTest/testSortTopToBottom.2", "[unit]" )
{
cv::Mat img = TestCimbar::loadSample("6bit/4_30_f0_627.jpg");
Scanner sc(img);

std::vector<Anchor> candidates;
candidates.push_back(Anchor(966, 1020, 966, 1020));
candidates.push_back(Anchor(966, 1020, 2, 56));
candidates.push_back(Anchor(2, 56, 966, 1020));
assertTrue( sc.sort_top_to_bottom(candidates) );
assertTrue( Scanner::sort_top_to_bottom(candidates) );

assertEquals(
"993+-27,993+-27 29+-27,993+-27 993+-27,29+-27",
turbo::str::join(candidates)
);
}

TEST_CASE( "ScannerTest/testSortTopToBottom.3", "[unit]" )
{
std::vector<Anchor> candidates;
candidates.push_back(Anchor(383, 437, 994, 1048));
candidates.push_back(Anchor(395, 447, 107, 157));
candidates.push_back(Anchor(1250, 1296, 124, 170));
assertTrue( Scanner::sort_top_to_bottom(candidates) );

assertEquals(
"421+-26,132+-25 1273+-23,147+-23 410+-27,1021+-27",
turbo::str::join(candidates)
);
}
1 change: 1 addition & 0 deletions src/lib/gui/gl_program.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#include <iostream>
#include <string>

namespace cimbar {
Expand Down
8 changes: 8 additions & 0 deletions src/lib/gui/window_glfw.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ class window_glfw
return glfwWindowShouldClose(_w);
}

void auto_scale_to_window()
{
if (!is_good())
return;
auto fun = [](GLFWwindow*, int w, int h){ glViewport(0, 0, w, h); };
glfwSetWindowSizeCallback(_w, fun);
}

void rotate(unsigned i=1)
{
if (_display)
Expand Down
2 changes: 1 addition & 1 deletion web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

body {
background-color: white;
background-image: radial-gradient(circle at top left, rgb(7,0,0),rgb(244,244,244),rgb(255,255,255));
background-image: radial-gradient(circle at top left, rgb(7,0,0),transparent,transparent), repeating-linear-gradient(0deg, rgb(153,197,206) 0px, rgb(153,197,206) 1px, transparent 1px, transparent 30px), repeating-linear-gradient(0deg, rgb(153,197,206) 0px, rgb(153,197,206) 2px, transparent 2px, transparent 150px), repeating-linear-gradient(90deg, rgb(153,197,206) 0px, rgb(153,197,206) 1px, transparent 1px, transparent 30px),repeating-linear-gradient(90deg, rgb(153,197,206) 0px, rgb(153,197,206) 2px, transparent 2px, transparent 150px), linear-gradient(white, white);
background-size: cover;
color: gray;
display: grid;
Expand Down