blocky/blocky/main.cc

289 lines
7.5 KiB
C++

#include <cassert>
#include <exception>
#include <iomanip>
#include <iostream>
#include <optional>
#include <tuple>
#include "common.hh"
#include "bytes.hh"
#include "features.hh"
#include <args.hxx>
#include "video_decoder.hh"
#include "video_encoder.hh"
using namespace blky;
args::ArgumentParser parser(
"liblocky command line tool",
"liblocky allow you to embed a bit array into video data secretly");
args::MapFlag<std::string, DataFlow> from {
parser, kDataFlowList, "input layer specifier", {"from"}, kDataFlowMap, args::Options::Required};
args::MapFlag<std::string, DataFlow> to {
parser, kDataFlowList, "output layer specifier", {"to"}, kDataFlowMap, args::Options::Required};
args::Group src_group {
parser, "source specifier", args::Group::Validators::Xor, args::Options::Required};
args::Flag src_stdin {src_group, "src-stdin", "read from stdin", {"src-stdin", "stdin"}};
args::Flag src_stdin_hex {src_group, "src-stdin-hex", "read hex text from stdin", {"src-stdin-hex", "stdin-hex"}};
args::ValueFlag<std::string> src_video {src_group, "path", "video input", {"src-video"}};
args::Group dst_group {
parser, "destination specifier", args::Group::Validators::Xor, args::Options::Required};
args::Flag dst_stdout {dst_group, "dst-stdout", "write to stdout", {"dst-stdout", "stdout"}};
args::Flag dst_stdout_hex {dst_group, "dst-stdout-hex", "write to stdout as hex text", {"dst-stdout-hex", "stdout-hex"}};
args::ValueFlag<std::string> dst_video {dst_group, "path", "video output", {"dst-video"}};
args::Group param_group {
parser, "general parameters", args::Group::Validators::DontCare
};
args::ValueFlag<std::tuple<uint32_t, uint32_t>> param_block_num {
param_group,
"int>0,int>0",
"number of features",
{"feature-num"},
{16, 16}
};
args::ValueFlag<uint32_t> param_block_first {
param_group,
"int>=0",
"an index of first block where feature will be embedded. used when encoding",
{"feature-first-index"},
0
};
args::ValueFlag<uint8_t> param_feat_bits {
param_group,
"int>0",
"number of bits that can be represented by a single feature",
{"feature-bits"},
1
};
args::ValueFlag<uint8_t> param_seed {
param_group,
"int>0",
"seed number for hopping randomization",
{"seed"},
123
};
args::Group probgen_group {
parser, "params for feature probability generator", args::Group::Validators::DontCare
};
args::ValueFlag<double> probgen_false_positive {
probgen_group,
"0<=double<=1",
"false positive ratio in feature probability generation",
{"probgen-false-positive"},
0
};
args::ValueFlag<double> probgen_false_negative {
probgen_group,
"0<=double<=1",
"false negative ratio in feature probability generation",
{"probgen-false-negative"},
0
};
args::ValueFlag<uint64_t> probgen_seed {
probgen_group,
"int>0",
"random seed",
{"probgen-seed"},
1
};
args::Flag probgen_normalize {
probgen_group,
"probgen-normalize",
"normalize probabilities",
{"probgen-normalize"},
};
std::vector<uint8_t> bytes;
std::vector<uint32_t> features;
std::vector<double> feature_probs;
std::optional<VideoDecoder> decoder;
int main(int argc, char** argv)
try {
parser.ParseCLI(argc, argv);
// read input
switch (args::get(from)) {
case kBytes:
if (src_stdin) {
std::string temp;
std::cin >> temp;
bytes = {temp.begin(), temp.end()};
} else if (src_stdin_hex) {
for (;;) {
char buf[2];
std::cin >> buf[0] >> buf[1];
if (std::cin.eof()) break;
bytes.push_back(ToHex(buf[0]) << 4 | ToHex(buf[1]));
}
} else {
throw std::runtime_error {"invalid source format for bytes"};
}
break;
case kFeatures:
if (src_stdin) {
features = ReadAll<uint32_t>(std::cin);
} else {
throw std::runtime_error {"invalid source format for features"};
}
break;
case kFeatureProbs:
if (src_stdin) {
feature_probs = ReadAll<double>(std::cin);
} else {
throw std::runtime_error {"invalid source format for feature probs"};
}
break;
case kVideo:
if (src_video) {
decoder.emplace(args::get(src_video));
} else {
throw std::runtime_error {"invalid source format for video"};
}
}
if (args::get(from) < args::get(to)) {
// execute encoding
switch (args::get(from)) {
case kBytes:
if (args::get(to) == kBytes) break;
features = BytesEncoder(
bytes,
args::get(param_block_num),
args::get(param_feat_bits),
args::get(param_block_first),
args::get(param_seed));
/* fallthrough */
case kFeatures:
if (args::get(to) == kFeatures) break;
if (args::get(to) == kFeatureProbs) {
feature_probs = GenerateFeatureProbs(
features,
args::get(param_block_num),
args::get(probgen_false_positive),
args::get(probgen_false_negative),
args::get(probgen_seed),
probgen_normalize);
break;
}
// TODO embed into video
assert(false);
/* fallthrough */
case kVideo:
if (args::get(to) == kVideo) break;
assert(false);
case kFeatureProbs:
throw std::runtime_error("couldn't start flow from the data");
}
} else if (args::get(from) > args::get(to)) {
// execute decoding
switch (args::get(from)) {
case kVideo:
if (args::get(to) == kVideo) break;
// TODO extract feature probs // features = XX
assert(false);
case kFeatureProbs:
if (args::get(to) == kFeatureProbs) break;
features = PathfindFeatures(
feature_probs,
args::get(param_block_num),
args::get(param_feat_bits),
args::get(param_seed));
/* fallthrough */
case kFeatures:
if (args::get(to) == kFeatures) break;
bytes = BytesDecoder(
features,
args::get(param_block_num),
args::get(param_feat_bits),
args::get(param_seed));
/* fallthrough */
case kBytes:
if (args::get(to) == kBytes) break;
assert(false);
}
}
// output
switch (args::get(to)) {
case kBytes:
if (dst_stdout) {
std::cout << std::string {bytes.begin(), bytes.end()} << std::endl;
} else if (dst_stdout_hex) {
for (auto c : bytes) {
std::cout << std::hex << (int) c;
}
std::cout << std::endl;
} else {
throw std::runtime_error {"invalid destination format for bytes"};
}
break;
case kFeatures:
if (dst_stdout) {
for (auto& f : features) std::cout << f << "\n";
} else {
throw std::runtime_error {"invalid destination format for features"};
}
break;
case kFeatureProbs:
if (dst_stdout) {
const auto size = args::get(param_block_num);
const auto cols = std::get<0>(size) * std::get<1>(size);
for (size_t i = 0; i < feature_probs.size();) {
for (size_t j = 0; i < feature_probs.size() && j < cols; ++i, ++j) {
std::cout << feature_probs[i] << " ";
}
std::cout << "\n";
}
} else {
throw std::runtime_error {"invalid destination format for feature probs"};
}
break;
case kVideo:
break;
}
return 0;
} catch (const args::Help&) {
std::cout << parser << std::endl;
return 0;
} catch (const args::ParseError& e) {
std::cerr << e.what() << std::endl;
std::cerr << parser << std::endl;
return 1;
} catch (const args::ValidationError& e) {
std::cerr << e.what() << std::endl;
std::cerr << parser << std::endl;
return 1;
} catch (const std::runtime_error& e) {
std::cerr << "runtime error: " << e.what() << std::endl;
return 1;
}