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

Update decoder objects for per-frame mode switching #94

Merged
merged 3 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
27 changes: 13 additions & 14 deletions src/exe/cimbar/cimbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ int encode(const FilenameIterable& infiles, const std::string& outpath, int ecc,
}

template <typename FilenameIterable>
int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bool, int)>& decodefun, bool no_deskew, bool undistort, int preprocess, int color_correct)
int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, unsigned, bool, int)>& decodefun, bool no_deskew, bool undistort, unsigned color_mode, int preprocess, int color_correct)
{
int err = 0;
for (const string& inf : infiles)
Expand Down Expand Up @@ -152,7 +152,7 @@ int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bo
shouldPreprocess = true;
}

int bytes = decodefun(img, shouldPreprocess, color_correct);
int bytes = decodefun(img, color_mode, shouldPreprocess, color_correct);
if (!bytes)
err |= 4;
}
Expand All @@ -162,10 +162,10 @@ int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bo
// see also "decodefun" for non-fountain decodes, defined as a lambda inline below.
// this one needs its own function since it's a template (:
template <typename SINK>
std::function<int(cv::UMat,bool,int)> fountain_decode_fun(SINK& sink, Decoder& d)
std::function<int(cv::UMat,unsigned,bool,int)> fountain_decode_fun(SINK& sink, Decoder& d)
{
return [&sink, &d] (cv::UMat m, bool pre, int cc) {
return d.decode_fountain(m, sink, pre, cc);
return [&sink, &d] (cv::UMat m, unsigned cm, bool pre, int cc) {
return d.decode_fountain(m, sink, cm, pre, cc);
};
}

Expand Down Expand Up @@ -247,8 +247,7 @@ int main(int argc, char** argv)
int preprocess = result["preprocess"].as<int>();

unsigned color_mode = legacy_mode? 0 : 1;
bool coupled = legacy_mode;
Decoder d(ecc, colorBits, color_mode, coupled);
Decoder d(ecc, colorBits);

if (no_fountain)
{
Expand All @@ -257,13 +256,13 @@ int main(int argc, char** argv)

// simpler encoding, just the basics + ECC. No compression, fountain codes, etc.
std::ofstream f(outpath);
std::function<int(cv::UMat,bool,int)> decodefun = [&f, &d] (cv::UMat m, bool pre, int cc) {
return d.decode(m, f, pre, cc);
std::function<int(cv::UMat,unsigned,bool,int)> decodefun = [&f, &d] (cv::UMat m, unsigned cm, bool pre, int cc) {
return d.decode(m, f, cm, pre, cc);
};
if (useStdin)
return decode(StdinLineReader(), decodefun, no_deskew, undistort, preprocess, color_correct);
return decode(StdinLineReader(), decodefun, no_deskew, undistort, color_mode, preprocess, color_correct);
else
return decode(infiles, decodefun, no_deskew, undistort, preprocess, color_correct);
return decode(infiles, decodefun, no_deskew, undistort, color_mode, preprocess, color_correct);
}

// else, the good stuff
Expand All @@ -273,16 +272,16 @@ int main(int argc, char** argv)
if (compressionLevel <= 0)
{
fountain_decoder_sink<std::ofstream> sink(outpath, chunkSize, true);
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, color_mode, preprocess, color_correct);
}
else // default case, all bells and whistles
{
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize, true);

if (useStdin)
res = decode(StdinLineReader(), fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
res = decode(StdinLineReader(), fountain_decode_fun(sink, d), no_deskew, undistort, color_mode, preprocess, color_correct);
else
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, color_mode, preprocess, color_correct);
}
if (not color_correction_file.empty())
d.save_ccm(color_correction_file);
Expand Down
6 changes: 3 additions & 3 deletions src/exe/cimbar_recv/recv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ int main(int argc, char** argv)
string mode = result["mode"].as<string>();
legacy_mode = (mode == "4c") or (mode == "4C");
}
unsigned color_mode = legacy_mode? 0 : 1;

unsigned fps = result["fps"].as<unsigned>();
if (fps == 0)
Expand Down Expand Up @@ -104,8 +105,7 @@ int main(int argc, char** argv)
window.auto_scale_to_window();

Extractor ext;
unsigned color_mode = legacy_mode? 0 : 1;
Decoder dec(-1, -1, color_mode, legacy_mode);
Decoder dec(-1, -1);

unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc, colorBits+cimbar::Config::symbol_bits(), legacy_mode);
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize);
Expand Down Expand Up @@ -147,7 +147,7 @@ int main(int argc, char** argv)
shouldPreprocess = true;

// decode
int bytes = dec.decode_fountain(img, sink, shouldPreprocess);
int bytes = dec.decode_fountain(img, sink, color_mode, shouldPreprocess);
if (bytes > 0)
std::cerr << "got some bytes " << bytes << std::endl;
}
Expand Down
15 changes: 7 additions & 8 deletions src/lib/cimb_translator/CimbDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,10 @@ namespace {
}
}

CimbDecoder::CimbDecoder(unsigned symbol_bits, unsigned color_bits, unsigned color_mode, bool dark, uchar ahashThreshold)
CimbDecoder::CimbDecoder(unsigned symbol_bits, unsigned color_bits, bool dark, uchar ahashThreshold)
: _symbolBits(symbol_bits)
, _numSymbols(1 << symbol_bits)
, _numColors(1 << color_bits)
, _colorMode(color_mode)
, _dark(dark)
, _ahashThreshold(ahashThreshold)
{
Expand Down Expand Up @@ -160,12 +159,12 @@ unsigned CimbDecoder::check_color_distance(std::tuple<uchar,uchar,uchar> a, std:
return color_diff(a, b);
}

std::tuple<uchar,uchar,uchar> CimbDecoder::get_color(int i) const
std::tuple<uchar,uchar,uchar> CimbDecoder::get_color(int i, unsigned color_mode) const
{
return cimbar::getColor(i, _numColors, _colorMode);
return cimbar::getColor(i, _numColors, color_mode);
}

unsigned CimbDecoder::get_best_color(float r, float g, float b) const
unsigned CimbDecoder::get_best_color(float r, float g, float b, unsigned color_mode) const
{
// transform color with ccm
if (internal_ccm().active())
Expand All @@ -188,7 +187,7 @@ unsigned CimbDecoder::get_best_color(float r, float g, float b) const
float best_distance = 1000000;
for (unsigned i = 0; i < _numColors; ++i)
{
std::tuple<uchar,uchar,uchar> candidate = get_color(i);
std::tuple<uchar,uchar,uchar> candidate = get_color(i, color_mode);
unsigned distance = check_color_distance(c, candidate);
if (distance < best_distance)
{
Expand All @@ -208,12 +207,12 @@ std::tuple<uchar,uchar,uchar> CimbDecoder::avg_color(const Cell& color_cell) con
return center.mean_rgb();
}

unsigned CimbDecoder::decode_color(const Cell& color_cell) const
unsigned CimbDecoder::decode_color(const Cell& color_cell, unsigned color_mode) const
Copy link
Owner Author

Choose a reason for hiding this comment

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

My least favorite part of the changeset...

{
if (_numColors <= 1)
return 0;
auto [r, g, b] = avg_color(color_cell);
return get_best_color(r, g, b);
return get_best_color(r, g, b, color_mode);
}

bool CimbDecoder::expects_binary_threshold() const
Expand Down
9 changes: 4 additions & 5 deletions src/lib/cimb_translator/CimbDecoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
class CimbDecoder
{
public:
CimbDecoder(unsigned symbol_bits, unsigned color_bits, unsigned color_mode=1, bool dark=true, uchar ahashThreshold=0);
CimbDecoder(unsigned symbol_bits, unsigned color_bits, bool dark=true, uchar ahashThreshold=0);

const color_correction& get_ccm() const;
void update_color_correction(cv::Matx<float, 3, 3>&& ccm);
Expand All @@ -22,10 +22,10 @@ class CimbDecoder
unsigned decode_symbol(const cv::Mat& cell, unsigned& drift_offset, unsigned& best_distance, unsigned cooldown=0xFF) const;
unsigned decode_symbol(const bitmatrix& cell, unsigned& drift_offset, unsigned& best_distance, unsigned cooldown=0xFF) const;

std::tuple<uchar,uchar,uchar> get_color(int i) const;
std::tuple<uchar,uchar,uchar> get_color(int i, unsigned color_mode) const;
std::tuple<uchar,uchar,uchar> avg_color(const Cell& color_cell) const;
unsigned get_best_color(float r, float g, float b) const;
unsigned decode_color(const Cell& cell) const;
unsigned get_best_color(float r, float g, float b, unsigned color_mode) const;
unsigned decode_color(const Cell& cell, unsigned color_mode) const;

bool expects_binary_threshold() const;
unsigned symbol_bits() const;
Expand All @@ -44,7 +44,6 @@ class CimbDecoder
unsigned _symbolBits;
unsigned _numSymbols;
unsigned _numColors;
unsigned _colorMode;
bool _dark;
uchar _ahashThreshold;
};
11 changes: 6 additions & 5 deletions src/lib/cimb_translator/CimbReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,29 +90,30 @@ namespace {
}
}

CimbReader::CimbReader(const cv::Mat& img, CimbDecoder& decoder, bool needs_sharpen, int color_correction)
CimbReader::CimbReader(const cv::Mat& img, CimbDecoder& decoder, unsigned color_mode, bool needs_sharpen, int color_correction)
: _image(img)
, _fountainColorHeader(0U)
, _cellSize(Config::cell_size() + 2)
, _positions(Config::cell_spacing(), Config::cells_per_col(), Config::cell_offset(), Config::corner_padding())
, _decoder(decoder)
, _good(_image.cols >= Config::image_size() and _image.rows >= Config::image_size())
, _colorCorrection(color_correction)
, _colorMode(color_mode)
Copy link
Owner Author

Choose a reason for hiding this comment

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

CimbReader is created per frame (the _image(img) is a hint of that), so storing color_mode is no problem.

{
_grayscale = preprocessSymbolGrid(img, needs_sharpen);
if (_good and color_correction == 1)
simpleColorCorrection(_image, decoder);
}

CimbReader::CimbReader(const cv::UMat& img, CimbDecoder& decoder, bool needs_sharpen, int color_correction)
: CimbReader(img.getMat(cv::ACCESS_READ), decoder, needs_sharpen, color_correction)
CimbReader::CimbReader(const cv::UMat& img, CimbDecoder& decoder, unsigned color_mode, bool needs_sharpen, int color_correction)
: CimbReader(img.getMat(cv::ACCESS_READ), decoder, color_mode, needs_sharpen, color_correction)
{
}

unsigned CimbReader::read_color(const PositionData& pos) const
{
Cell color_cell(_image, pos.x, pos.y, Config::cell_size(), Config::cell_size());
return _decoder.decode_color(color_cell);
return _decoder.decode_color(color_cell, _colorMode);
}

unsigned CimbReader::read(PositionData& pos)
Expand Down Expand Up @@ -219,7 +220,7 @@ void CimbReader::init_ccm(unsigned color_bits, unsigned interleave_blocks, unsig
cv::Mat arow = (cv::Mat_<float>(1,3) << std::get<1>(it.second), std::get<2>(it.second), std::get<3>(it.second));
actual.push_back(arow);

cimbar::RGB cc = _decoder.get_color(it.first);
cimbar::RGB cc = _decoder.get_color(it.first, _colorMode);
cv::Mat drow = (cv::Mat_<float>(1,3) << std::get<0>(cc), std::get<1>(cc), std::get<2>(cc));
desired.push_back(drow);
}
Expand Down
5 changes: 3 additions & 2 deletions src/lib/cimb_translator/CimbReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
class CimbReader
{
public:
CimbReader(const cv::Mat& img, CimbDecoder& decoder, bool needs_sharpen=false, int color_correction=2);
CimbReader(const cv::UMat& img, CimbDecoder& decoder, bool needs_sharpen=false, int color_correction=2);
CimbReader(const cv::Mat& img, CimbDecoder& decoder, unsigned color_mode, bool needs_sharpen=false, int color_correction=2);
CimbReader(const cv::UMat& img, CimbDecoder& decoder, unsigned color_mode, bool needs_sharpen=false, int color_correction=2);

unsigned read(PositionData& pos);
unsigned read_color(const PositionData& pos) const;
Expand All @@ -34,4 +34,5 @@ class CimbReader
CimbDecoder& _decoder;
bool _good;
int _colorCorrection;
unsigned _colorMode;
};
64 changes: 46 additions & 18 deletions src/lib/cimb_translator/test/CimbDecoderTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace {
cv::Rect crop(1+x, 1+y, tile10.cols-2, tile10.rows-2);
cv::Mat tile8 = tile10(crop);

bits |= cd.decode_color(tile8) << cd.symbol_bits();
bits |= cd.decode_color(tile8, 1) << cd.symbol_bits();
return bits;
}
}
Expand Down Expand Up @@ -74,32 +74,60 @@ TEST_CASE( "CimbDecoderTest/testPrethresholdDecode", "[unit]" )
}
}

TEST_CASE( "CimbDecoderTest/test_get_best_color__dark", "[unit]" )
TEST_CASE( "CimbDecoderTest/test_get_best_color_mode0", "[unit]" )
{
CimbDecoder cd(4, 2);

// obvious ones
assertEquals(3, cd.get_best_color(255, 0, 255));
assertEquals(2, cd.get_best_color(255, 255, 0));
assertEquals(1, cd.get_best_color(0, 255, 255));
assertEquals(0, cd.get_best_color(0, 255, 0));
assertEquals(2, cd.get_best_color(255, 0, 255, 0));
assertEquals(1, cd.get_best_color(255, 255, 0, 0));
assertEquals(0, cd.get_best_color(0, 255, 255, 0));
assertEquals(3, cd.get_best_color(0, 255, 0, 0));

// arbitrary edge cases. We can't really say anything about the value of these colors, but we can at least pick a consistent one
assertEquals(0, cd.get_best_color(0, 0, 0));
assertEquals(0, cd.get_best_color(70, 70, 70));
assertEquals(0, cd.get_best_color(0, 0, 0, 0));
assertEquals(0, cd.get_best_color(70, 70, 70, 0));

// these we can use!
assertEquals(0, cd.get_best_color(20, 200, 20));
assertEquals(0, cd.get_best_color(50, 155, 50));
assertEquals(3, cd.get_best_color(20, 200, 20, 0));
assertEquals(3, cd.get_best_color(50, 155, 50, 0));

assertEquals(3, cd.get_best_color(200, 30, 200));
assertEquals(3, cd.get_best_color(155, 50, 155));
assertEquals(2, cd.get_best_color(200, 30, 200, 0));
assertEquals(2, cd.get_best_color(155, 50, 155, 0));

assertEquals(2, cd.get_best_color(200, 155, 20));
assertEquals(2, cd.get_best_color(155, 155, 50));
assertEquals(1, cd.get_best_color(200, 155, 20, 0));
assertEquals(1, cd.get_best_color(155, 155, 50, 0));

assertEquals(1, cd.get_best_color(50, 155, 200));
assertEquals(1, cd.get_best_color(50, 155, 155));
assertEquals(0, cd.get_best_color(50, 155, 200, 0));
assertEquals(0, cd.get_best_color(50, 155, 155, 0));
}

TEST_CASE( "CimbDecoderTest/test_get_best_color_mode1", "[unit]" )
{
CimbDecoder cd(4, 2);

// obvious ones
assertEquals(3, cd.get_best_color(255, 0, 255, 1));
assertEquals(2, cd.get_best_color(255, 255, 0, 1));
assertEquals(1, cd.get_best_color(0, 255, 255, 1));
assertEquals(0, cd.get_best_color(0, 255, 0, 1));

// arbitrary edge cases. We can't really say anything about the value of these colors, but we can at least pick a consistent one
assertEquals(0, cd.get_best_color(0, 0, 0, 1));
assertEquals(0, cd.get_best_color(70, 70, 70, 1));

// these we can use!
assertEquals(0, cd.get_best_color(20, 200, 20, 1));
assertEquals(0, cd.get_best_color(50, 155, 50, 1));

assertEquals(3, cd.get_best_color(200, 30, 200, 1));
assertEquals(3, cd.get_best_color(155, 50, 155, 1));

assertEquals(2, cd.get_best_color(200, 155, 20, 1));
assertEquals(2, cd.get_best_color(155, 155, 50, 1));

assertEquals(1, cd.get_best_color(50, 155, 200, 1));
assertEquals(1, cd.get_best_color(50, 155, 155, 1));
}

TEST_CASE( "CimbDecoderTest/testColorDecode", "[unit]" )
Expand All @@ -109,7 +137,7 @@ TEST_CASE( "CimbDecoderTest/testColorDecode", "[unit]" )
cv::Mat tile = cimbar::getTile(4, 2, true, 4, 2);
cv::resize(tile, tile, cv::Size(10, 10));

unsigned color = cd.decode_color(Cell(tile));
unsigned color = cd.decode_color(Cell(tile), 1);
assertEquals(2, color);
unsigned res = decode(cd, tile);
assertEquals(34, res);
Expand All @@ -128,7 +156,7 @@ TEST_CASE( "CimbDecoderTest/testAllColorDecodes", "[unit]" )
cv::Mat tenxten(10, 10, tile.type(), {0,0,0});
tile.copyTo(tenxten(cv::Rect(cv::Point(1, 1), tile.size())));

unsigned color = cd.decode_color(Cell(tenxten));
unsigned color = cd.decode_color(Cell(tenxten), 1);
assertEquals(c, color);
unsigned res = decode(cd, tenxten);
assertEquals(i+16*c, res);
Expand Down
Loading
Loading