Skip to content

Commit 7b54894

Browse files
authored
Merge pull request #91 from sz3/mode-b
Functionality to split encoding/decoding of colors and symbols
2 parents 368c2c4 + 7206b91 commit 7b54894

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+832
-225
lines changed

src/exe/cimbar/cimbar.cpp

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,11 @@ namespace {
101101
}
102102

103103
template <typename FilenameIterable>
104-
int encode(const FilenameIterable& infiles, const std::string& outpath, int ecc, int color_bits, int compression_level, bool no_fountain)
104+
int encode(const FilenameIterable& infiles, const std::string& outpath, int ecc, int color_bits, int compression_level, bool legacy_mode, bool no_fountain)
105105
{
106106
Encoder en(ecc, cimbar::Config::symbol_bits(), color_bits);
107+
if (legacy_mode)
108+
en.set_legacy_mode();
107109
for (const string& f : infiles)
108110
{
109111
if (f.empty())
@@ -117,7 +119,7 @@ int encode(const FilenameIterable& infiles, const std::string& outpath, int ecc,
117119
}
118120

119121
template <typename FilenameIterable>
120-
int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bool, bool)>& decode, bool no_deskew, bool undistort, int preprocess, bool color_correct)
122+
int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bool, int)>& decodefun, bool no_deskew, bool undistort, int preprocess, int color_correct)
121123
{
122124
int err = 0;
123125
for (const string& inf : infiles)
@@ -150,17 +152,19 @@ int decode(const FilenameIterable& infiles, const std::function<int(cv::UMat, bo
150152
shouldPreprocess = true;
151153
}
152154

153-
int bytes = decode(img, shouldPreprocess, color_correct);
155+
int bytes = decodefun(img, shouldPreprocess, color_correct);
154156
if (!bytes)
155157
err |= 4;
156158
}
157159
return err;
158160
}
159161

162+
// see also "decodefun" for non-fountain decodes, defined as a lambda inline below.
163+
// this one needs its own function since it's a template (:
160164
template <typename SINK>
161-
std::function<int(cv::UMat,bool,bool)> fountain_decode_fun(SINK& sink, Decoder& d)
165+
std::function<int(cv::UMat,bool,int)> fountain_decode_fun(SINK& sink, Decoder& d)
162166
{
163-
return [&sink, &d] (cv::UMat m, bool pre, bool cc) {
167+
return [&sink, &d] (cv::UMat m, bool pre, int cc) {
164168
return d.decode_fountain(m, sink, pre, cc);
165169
};
166170
}
@@ -177,8 +181,10 @@ int main(int argc, char** argv)
177181
("o,out", "Output file prefix (encoding) or directory (decoding).", cxxopts::value<string>())
178182
("c,color-bits", "Color bits. [0-3]", cxxopts::value<int>()->default_value(turbo::str::str(colorBits)))
179183
("e,ecc", "ECC level", cxxopts::value<unsigned>()->default_value(turbo::str::str(ecc)))
184+
("m,mode", "Select a cimbar mode. B (the default) is new to 0.6.x. 4C is the 0.5.x config. [B,4C]", cxxopts::value<string>()->default_value("B"))
180185
("z,compression", "Compression level. 0 == no compression.", cxxopts::value<int>()->default_value(turbo::str::str(compressionLevel)))
181-
("color-correct", "Toggle decoding color correction. 1 == on. 0 == off.", cxxopts::value<int>()->default_value("1"))
186+
("color-correct", "Toggle decoding color correction. 2 == full (fountain mode only). 1 == simple. 0 == off.", cxxopts::value<int>()->default_value("2"))
187+
("color-correction-file", "Debug -- save color correction matrix generated during fountain decode, or use it for non-fountain decodes", cxxopts::value<string>())
182188
("encode", "Run the encoder!", cxxopts::value<bool>())
183189
("no-deskew", "Skip the deskew step -- treat input image as already extracted.", cxxopts::value<bool>())
184190
("no-fountain", "Disable fountain encode/decode. Will also disable compression.", cxxopts::value<bool>())
@@ -216,48 +222,70 @@ int main(int argc, char** argv)
216222
compressionLevel = result["compression"].as<int>();
217223
ecc = result["ecc"].as<unsigned>();
218224

225+
bool legacy_mode = false;
226+
if (result.count("mode"))
227+
{
228+
string mode = result["mode"].as<string>();
229+
legacy_mode = (mode == "4c") or (mode == "4C");
230+
}
231+
219232
if (encodeFlag)
220233
{
221234
if (useStdin)
222-
return encode(StdinLineReader(), outpath, ecc, colorBits, compressionLevel, no_fountain);
235+
return encode(StdinLineReader(), outpath, ecc, colorBits, compressionLevel, legacy_mode, no_fountain);
223236
else
224-
return encode(infiles, outpath, ecc, colorBits, compressionLevel, no_fountain);
237+
return encode(infiles, outpath, ecc, colorBits, compressionLevel, legacy_mode, no_fountain);
225238
}
226239

227240
// else, decode
228241
bool no_deskew = result.count("no-deskew");
229242
bool undistort = result.count("undistort");
230243
int color_correct = result["color-correct"].as<int>();
244+
string color_correction_file;
245+
if (result.count("color-correction-file"))
246+
color_correction_file = result["color-correction-file"].as<string>();
231247
int preprocess = result["preprocess"].as<int>();
232248

233-
Decoder d(ecc, colorBits);
249+
unsigned color_mode = legacy_mode? 0 : 1;
250+
bool coupled = legacy_mode;
251+
Decoder d(ecc, colorBits, color_mode, coupled);
234252

235253
if (no_fountain)
236254
{
255+
if (not color_correction_file.empty())
256+
d.load_ccm(color_correction_file);
257+
237258
// simpler encoding, just the basics + ECC. No compression, fountain codes, etc.
238259
std::ofstream f(outpath);
239-
std::function<int(cv::UMat,bool,bool)> fun = [&f, &d] (cv::UMat m, bool pre, bool cc) {
260+
std::function<int(cv::UMat,bool,int)> decodefun = [&f, &d] (cv::UMat m, bool pre, int cc) {
240261
return d.decode(m, f, pre, cc);
241262
};
242263
if (useStdin)
243-
return decode(StdinLineReader(), fun, no_deskew, undistort, preprocess, color_correct);
264+
return decode(StdinLineReader(), decodefun, no_deskew, undistort, preprocess, color_correct);
244265
else
245-
return decode(infiles, fun, no_deskew, undistort, preprocess, color_correct);
266+
return decode(infiles, decodefun, no_deskew, undistort, preprocess, color_correct);
246267
}
247268

248269
// else, the good stuff
249-
unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc);
270+
int res = -200;
271+
272+
unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc, colorBits+cimbar::Config::symbol_bits(), legacy_mode);
250273
if (compressionLevel <= 0)
251274
{
252275
fountain_decoder_sink<std::ofstream> sink(outpath, chunkSize, true);
253-
return decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
276+
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
254277
}
278+
else // default case, all bells and whistles
279+
{
280+
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize, true);
255281

256-
// else -- default case, all bells and whistles
257-
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize, true);
282+
if (useStdin)
283+
res = decode(StdinLineReader(), fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
284+
else
285+
res = decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
286+
}
287+
if (not color_correction_file.empty())
288+
d.save_ccm(color_correction_file);
258289

259-
if (useStdin)
260-
return decode(StdinLineReader(), fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
261-
else
262-
return decode(infiles, fountain_decode_fun(sink, d), no_deskew, undistort, preprocess, color_correct);
290+
return res;
263291
}

src/exe/cimbar_recv/recv.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ int main(int argc, char** argv)
4141
options.add_options()
4242
("i,in", "Video source.", cxxopts::value<string>())
4343
("o,out", "Output directory (decoding).", cxxopts::value<string>())
44-
("c,colorbits", "Color bits. [0-3]", cxxopts::value<int>()->default_value(turbo::str::str(colorBits)))
44+
("c,colorbits", "Color bits. [0-3]", cxxopts::value<int>()->default_value(turbo::str::str(colorBits)))
4545
("e,ecc", "ECC level", cxxopts::value<unsigned>()->default_value(turbo::str::str(ecc)))
4646
("f,fps", "Target decode FPS", cxxopts::value<unsigned>()->default_value(turbo::str::str(defaultFps)))
47-
("h,help", "Print usage")
47+
("m,mode", "Select a cimbar mode. B (the default) is new to 0.6.x. 4C is the 0.5.x config. [B,4C]", cxxopts::value<string>()->default_value("B"))
48+
("h,help", "Print usage")
4849
;
4950
options.show_positional_help();
5051
options.parse_positional({"in", "out"});
@@ -62,6 +63,14 @@ int main(int argc, char** argv)
6263

6364
colorBits = std::min(3, result["colorbits"].as<int>());
6465
ecc = result["ecc"].as<unsigned>();
66+
67+
bool legacy_mode = false;
68+
if (result.count("mode"))
69+
{
70+
string mode = result["mode"].as<string>();
71+
legacy_mode = (mode == "4c") or (mode == "4C");
72+
}
73+
6574
unsigned fps = result["fps"].as<unsigned>();
6675
if (fps == 0)
6776
fps = defaultFps;
@@ -95,9 +104,10 @@ int main(int argc, char** argv)
95104
window.auto_scale_to_window();
96105

97106
Extractor ext;
98-
Decoder dec;
107+
unsigned color_mode = legacy_mode? 0 : 1;
108+
Decoder dec(-1, -1, color_mode, legacy_mode);
99109

100-
unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc);
110+
unsigned chunkSize = cimbar::Config::fountain_chunk_size(ecc, colorBits+cimbar::Config::symbol_bits(), legacy_mode);
101111
fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> sink(outpath, chunkSize);
102112

103113
cv::Mat mat;

src/exe/cimbar_send/send.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ int main(int argc, char** argv)
4242
("c,colorbits", "Color bits. [0-3]", cxxopts::value<int>()->default_value(turbo::str::str(colorBits)))
4343
("e,ecc", "ECC level", cxxopts::value<unsigned>()->default_value(turbo::str::str(ecc)))
4444
("f,fps", "Target FPS", cxxopts::value<unsigned>()->default_value(turbo::str::str(defaultFps)))
45+
("m,mode", "Select a cimbar mode. B (the default) is new to 0.6.x. 4C is the 0.5.x config. [B,4C]", cxxopts::value<string>()->default_value("B"))
4546
("z,compression", "Compression level. 0 == no compression.", cxxopts::value<int>()->default_value(turbo::str::str(compressionLevel)))
4647
("h,help", "Print usage")
4748
;
@@ -61,6 +62,14 @@ int main(int argc, char** argv)
6162
colorBits = std::min(3, result["colorbits"].as<int>());
6263
compressionLevel = result["compression"].as<int>();
6364
ecc = result["ecc"].as<unsigned>();
65+
66+
bool legacy_mode = false;
67+
if (result.count("mode"))
68+
{
69+
string mode = result["mode"].as<string>();
70+
legacy_mode = (mode == "4c") or (mode == "4C");
71+
}
72+
6473
unsigned fps = result["fps"].as<unsigned>();
6574
if (fps == 0)
6675
fps = defaultFps;
@@ -73,7 +82,7 @@ int main(int argc, char** argv)
7382
return 70;
7483
}
7584

76-
configure(colorBits, ecc, compressionLevel);
85+
configure(colorBits, ecc, compressionLevel, legacy_mode);
7786

7887
std::chrono::time_point start = std::chrono::high_resolution_clock::now();
7988
while (true)
@@ -91,7 +100,9 @@ int main(int argc, char** argv)
91100
continue;
92101
}
93102

94-
if (!encode(reinterpret_cast<unsigned char*>(contents.data()), contents.size(), static_cast<int>(i)))
103+
// start encode_id is 109. This is mostly unimportant (it only needs to wrap between [0,127]), but useful
104+
// for the decoder -- because it gives it a better distribution of colors in the first frame header it sees.
105+
if (!encode(reinterpret_cast<unsigned char*>(contents.data()), contents.size(), static_cast<int>(i+109)))
95106
{
96107
std::cerr << "failed to encode file " << infiles[i] << std::endl;
97108
continue;

src/lib/bit_file/bitbuffer.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ class bitbuffer
125125
return _buffer;
126126
}
127127

128+
void copy_to_buffer(const char* data, unsigned size)
129+
{
130+
_buffer.resize(size, 0);
131+
std::copy(data, data+size, _buffer.data());
132+
}
133+
128134
writer get_writer(size_t pos=0)
129135
{
130136
return writer(*this, pos);

src/lib/bit_file/test/bitbufferTest.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,17 @@ TEST_CASE( "bitbufferTest/testOverwrite", "[unit]" )
144144
assertEquals( '.', bb.buffer()[0x3f5] );
145145
assertEquals( 't', bb.buffer()[0x3f6] );
146146
}
147+
148+
TEST_CASE( "bitbufferTest/testCopyToBuffer", "[unit]" )
149+
{
150+
bitbuffer bb;
151+
152+
std::string hello = "hello";
153+
bb.copy_to_buffer(hello.data(), 5);
154+
155+
assertEquals( 0x68, bb.read(0, 8) );
156+
assertEquals( 0x65, bb.read(8, 8) );
157+
assertEquals( 0x6c, bb.read(16, 8) );
158+
assertEquals( 0x6c, bb.read(24, 8) );
159+
assertEquals( 0x6f, bb.read(32, 8) );
160+
}

src/lib/chromatic_adaptation/color_correction.h

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,30 @@ class color_correction
2323
return transform().inv() * d * transform();
2424
}
2525

26+
static inline cv::Matx<float, 3, 3> get_moore_penrose_lsm(const cv::Mat& actual, const cv::Mat& desired)
27+
{
28+
// inspired by the python colour-science package. It's not complicated,
29+
// but I didn't know that going in.
30+
// See also:
31+
// https://en.wikipedia.org/wiki/Moore-Penrose_inverse
32+
cv::Mat x, y, z;
33+
cv::transpose(desired, x);
34+
cv::transpose(actual, y);
35+
cv::invert(y, z, cv::DECOMP_SVD);
36+
37+
y = x * z;
38+
return y;
39+
}
40+
2641
public:
2742
color_correction()
28-
: _active(false)
43+
: _active(false)
2944
{
3045
}
3146

3247
color_correction(cv::Matx<float, 3, 3>&& m)
33-
: _m(m)
34-
, _active(true)
48+
: _m(m)
49+
, _active(true)
3550
{
3651
}
3752

src/lib/chromatic_adaptation/test/color_correctionTest.cpp

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ TEST_CASE( "color_correctionTest/testTransform", "[unit]" )
1919
std::stringstream ss;
2020
ss << mat;
2121
assertEquals( "[1.0655777, 0.2109226, -0.013239831;\n"
22-
" 0.023168325, 0.98723376, -0.0046780901;\n"
23-
" 0, 0, 1]", ss.str() );
22+
" 0.023168325, 0.98723376, -0.0046780901;\n"
23+
" 0, 0, 1]", ss.str() );
2424
}
2525

2626
std::tuple<float, float, float> c = color_correction(std::move(mat)).transform(180, 98, 255);
@@ -29,3 +29,54 @@ TEST_CASE( "color_correctionTest/testTransform", "[unit]" )
2929
assertAlmostEquals( 255, std::get<2>(c) );
3030
}
3131

32+
TEST_CASE( "color_correctionTest/testComputeMoorePenrose", "[unit]" )
33+
{
34+
cv::Mat actual = (cv::Mat_<float>(5,3) <<
35+
0, 142.31060606, 0,
36+
0, 148.75, 148.75,
37+
148.75, 148.75, 0,
38+
148.75, 0, 148.75,
39+
255, 255, 255);
40+
41+
cv::Mat desired = (cv::Mat_<float>(5,3) <<
42+
0, 255, 0,
43+
0, 255, 255,
44+
255, 255, 0,
45+
255, 0, 255,
46+
255, 255, 255);
47+
48+
cv::Matx<float, 3, 3> mat = color_correction::get_moore_penrose_lsm(actual, desired);
49+
{
50+
std::stringstream ss;
51+
ss << mat;
52+
assertEquals( "[1.5223049, -0.10023587, -0.19198087;\n"
53+
" -0.20533442, 1.6441474, -0.20533434;\n"
54+
" -0.19198078, -0.10023584, 1.5223049]", ss.str() );
55+
}
56+
}
57+
58+
TEST_CASE( "color_correctionTest/testComputeMoorePenrose.2", "[unit]" )
59+
{
60+
cv::Mat actual = (cv::Mat_<float>(5,3) <<
61+
14.58901515, 115.74431818, 39.88320707,
62+
19.34027778, 124.4375, 115.37152778,
63+
140.70486111, 137.45833333, 65.50694444,
64+
131.59722222, 41.22222222, 104.84027778,
65+
171.625, 163.625, 158.875);
66+
67+
cv::Mat desired = (cv::Mat_<float>(5,3) <<
68+
0, 255, 0,
69+
0, 255, 255,
70+
255, 255, 0,
71+
255, 0, 255,
72+
255, 255, 255);
73+
74+
cv::Matx<float, 3, 3> mat = color_correction::get_moore_penrose_lsm(actual, desired);
75+
{
76+
std::stringstream ss;
77+
ss << mat;
78+
assertEquals( "[2.0261116, -0.21691091, -0.19806443;\n"
79+
" -0.43822661, 2.4562523, -0.41700464;\n"
80+
" -0.55769891, -1.1443435, 3.4819376]", ss.str() );
81+
}
82+
}

0 commit comments

Comments
 (0)