Skip to content

Commit

Permalink
Merge pull request #61 from sz3/cimbar-recv
Browse files Browse the repository at this point in the history
Bugfix + `cimbar_recv`, a test decoder app + ...
  • Loading branch information
sz3 authored Mar 26, 2022
2 parents 5fbface + 395d9ea commit 3cd792b
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 19 deletions.
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]};
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

0 comments on commit 3cd792b

Please sign in to comment.