-
Notifications
You must be signed in to change notification settings - Fork 68
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
Add mode autodetection + update mode toggle to function as autodetect off/on #32
Changes from 7 commits
a2f4dbd
f19f124
d168253
1d4ea5a
f4067b2
af01b58
86eda96
a19a56b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,9 @@ | |
class MultiThreadedDecoder | ||
{ | ||
public: | ||
MultiThreadedDecoder(std::string data_path, bool legacy_mode); | ||
MultiThreadedDecoder(std::string data_path, int mode_val); | ||
|
||
inline static clock_t count = 0; | ||
inline static clock_t bytes = 0; | ||
inline static clock_t perfect = 0; | ||
inline static clock_t decoded = 0; | ||
|
@@ -29,7 +30,10 @@ class MultiThreadedDecoder | |
|
||
void stop(); | ||
|
||
bool legacy_mode() const; | ||
int mode() const; | ||
bool set_mode(int mode_val); | ||
int detected_mode() const; | ||
|
||
unsigned num_threads() const; | ||
unsigned backlog() const; | ||
unsigned files_in_flight() const; | ||
|
@@ -41,21 +45,26 @@ class MultiThreadedDecoder | |
int do_extract(const cv::Mat& mat, cv::Mat& img); | ||
void save(const cv::Mat& img); | ||
|
||
static unsigned fountain_chunk_size(int mode_val); | ||
|
||
protected: | ||
bool _legacyMode; | ||
int _modeVal; | ||
int _detectedMode; | ||
|
||
Decoder _dec; | ||
unsigned _numThreads; | ||
turbo::thread_pool _pool; | ||
concurrent_fountain_decoder_sink<cimbar::zstd_decompressor<std::ofstream>> _writer; | ||
std::string _dataPath; | ||
}; | ||
|
||
inline MultiThreadedDecoder::MultiThreadedDecoder(std::string data_path, bool legacy_mode) | ||
: _legacyMode(legacy_mode) | ||
, _dec(cimbar::Config::ecc_bytes(), cimbar::Config::color_bits(), legacy_mode? 0 : 1, legacy_mode) | ||
inline MultiThreadedDecoder::MultiThreadedDecoder(std::string data_path, int mode_val) | ||
: _modeVal(mode_val) | ||
, _detectedMode(0) | ||
, _dec(cimbar::Config::ecc_bytes(), cimbar::Config::color_bits()) | ||
, _numThreads(std::max<int>(((int)std::thread::hardware_concurrency()/2), 1)) | ||
, _pool(_numThreads, 1) | ||
, _writer(data_path, cimbar::Config::fountain_chunk_size(cimbar::Config::ecc_bytes(), cimbar::Config::symbol_bits() + cimbar::Config::color_bits(), legacy_mode)) | ||
, _writer(data_path, fountain_chunk_size(mode_val)) | ||
, _dataPath(data_path) | ||
{ | ||
FountainInit::init(); | ||
|
@@ -87,7 +96,9 @@ inline int MultiThreadedDecoder::do_extract(const cv::Mat& mat, cv::Mat& img) | |
|
||
inline bool MultiThreadedDecoder::add(cv::Mat mat) | ||
{ | ||
return _pool.try_execute( [&, mat] () { | ||
++count; | ||
bool legacy_mode = _modeVal == 4 or (_modeVal == 0 and count%2 == 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Crucially, we do this count -> legacy_mode calculation on the main thread, not on the background threads. 😶 |
||
return _pool.try_execute( [&, mat, legacy_mode] () { | ||
cv::Mat img; | ||
int res = do_extract(mat, img); | ||
if (res == Extractor::FAILURE) | ||
|
@@ -96,12 +107,16 @@ inline bool MultiThreadedDecoder::add(cv::Mat mat) | |
// if extracted image is small, we'll need to run some filters on it | ||
clock_t begin = clock(); | ||
bool should_preprocess = (res == Extractor::NEEDS_SHARPEN); | ||
int color_correction = _legacyMode? 1 : 2; | ||
unsigned decodeRes = _dec.decode_fountain(img, _writer, should_preprocess, color_correction); | ||
int color_correction = legacy_mode? 1 : 2; | ||
unsigned color_mode = legacy_mode? 0 : 1; | ||
unsigned decodeRes = _dec.decode_fountain(img, _writer, color_mode, should_preprocess, color_correction); | ||
bytes += decodeRes; | ||
++decoded; | ||
decodeTicks += clock() - begin; | ||
|
||
if (decodeRes and _modeVal == 0) | ||
_detectedMode = legacy_mode? 4 : 68; | ||
|
||
if (decodeRes >= 6900) | ||
++perfect; | ||
} ); | ||
|
@@ -121,9 +136,35 @@ inline void MultiThreadedDecoder::stop() | |
_pool.stop(); | ||
} | ||
|
||
inline bool MultiThreadedDecoder::legacy_mode() const | ||
unsigned MultiThreadedDecoder::fountain_chunk_size(int mode_val) | ||
{ | ||
return cimbar::Config::fountain_chunk_size(cimbar::Config::ecc_bytes(), cimbar::Config::symbol_bits() + cimbar::Config::color_bits(), mode_val==4); | ||
} | ||
|
||
inline int MultiThreadedDecoder::mode() const | ||
{ | ||
return _modeVal; | ||
} | ||
|
||
inline bool MultiThreadedDecoder::set_mode(int mode_val) | ||
{ | ||
if (_modeVal == mode_val) | ||
return true; | ||
|
||
if (mode_val != 0 and _writer.chunk_size() != fountain_chunk_size(mode_val)) | ||
return false; // if so, we need to reset to change it | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the past (for example when from 4-color to 8-color, in 0.5.x), we had to throw away the MultiThreadedDecoder object and get a new one any time we changed anything. Since we have an "autodetect mode" now, we want to only do that when we must (since it currently requires throwing away progress). The fountain_chunk_size being different is the condition. Which is to say, if the modes had the same fountain_chunk_size, we would be able to make continuous progress on the stream no matter what mode we were in. But there are other things that might prevent that, and in any case there are good reasons |
||
|
||
// reset detectedMode iff we're switching back to autodetect | ||
if (mode_val == 0) | ||
_detectedMode = 0; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
_modeVal = mode_val; | ||
return true; | ||
} | ||
|
||
inline int MultiThreadedDecoder::detected_mode() const | ||
{ | ||
return _legacyMode; | ||
return _detectedMode; | ||
} | ||
|
||
inline unsigned MultiThreadedDecoder::num_threads() const | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
#include "cimb_translator/CimbReader.h" | ||
#include "encoder/Decoder.h" | ||
#include "extractor/Scanner.h" | ||
#include "serialize/format.h" | ||
|
||
#include <jni.h> | ||
#include <android/log.h> | ||
|
@@ -123,8 +124,8 @@ namespace { | |
void drawDebugInfo(cv::Mat& mat, MultiThreadedDecoder& proc) | ||
{ | ||
std::stringstream sstop; | ||
sstop << "cfc using " << proc.num_threads() << " thread(s). " << proc.legacy_mode() << "..." << proc.backlog() << "? "; | ||
sstop << (MultiThreadedDecoder::bytes / std::max<double>(1, MultiThreadedDecoder::decoded)) << "b v0.6.0f"; | ||
sstop << "cfc using " << proc.num_threads() << " thread(s). " << proc.mode() << ":" << proc.detected_mode() << "..." << proc.backlog() << "? "; | ||
sstop << (MultiThreadedDecoder::bytes / std::max<double>(1, MultiThreadedDecoder::decoded)) << "b v0.6.1"; | ||
std::stringstream ssmid; | ||
ssmid << "#: " << MultiThreadedDecoder::perfect << " / " << MultiThreadedDecoder::decoded << " / " << MultiThreadedDecoder::scanned << " / " << _calls; | ||
std::stringstream ssperf; | ||
|
@@ -170,13 +171,12 @@ Java_org_cimbar_camerafilecopy_MainActivity_processImageJNI(JNIEnv *env, jobject | |
Mat &mat = *(Mat *) matAddr; | ||
string dataPath = jstring_to_cppstr(env, dataPathObj); | ||
int modeVal = (int)modeInt; | ||
bool legacyMode = modeVal <= 8; // current scheme: old 4c = 4, old 8c = 8, new = bigger number | ||
|
||
std::shared_ptr<MultiThreadedDecoder> proc; | ||
{ | ||
std::lock_guard<std::mutex> lock(_mutex); | ||
if (!_proc or _proc->legacy_mode() != legacyMode) | ||
_proc = std::make_shared<MultiThreadedDecoder>(dataPath, legacyMode); | ||
if (!_proc or !_proc->set_mode(modeVal)) | ||
_proc = std::make_shared<MultiThreadedDecoder>(dataPath, modeVal); | ||
proc = _proc; | ||
} | ||
|
||
|
@@ -205,6 +205,9 @@ Java_org_cimbar_camerafilecopy_MainActivity_processImageJNI(JNIEnv *env, jobject | |
|
||
// return a decoded file to prompt the user to save it, if there is a new one | ||
string result; | ||
if (proc->detected_mode()) // repurpose str for special message passing | ||
result = fmt::format("/{}", proc->detected_mode()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit ridiculous. But it also works. 🤔 |
||
|
||
std::vector<string> all_decodes = proc->get_done(); | ||
for (string& s : all_decodes) | ||
if (_completed.find(s) == _completed.end()) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface is to allow us to switch modes while keeping the MultiThreadedDecoder around when the mode doesn't switch (rather than tearing it down, resetting progress). So we can click the toggle button to our heart's content...