Compare commits
29 Commits
Author | SHA1 | Date | |
---|---|---|---|
af394452c0 | |||
9a115f9990 | |||
dd4f0262d2 | |||
c83a0716c1 | |||
6faa2c0ae0 | |||
8262bf29b2 | |||
0fde521ffe | |||
b61b6dd21b | |||
df6302c699 | |||
4ddd15f34e | |||
83ee7f5f1e | |||
e6e8a68c09 | |||
662c86234f | |||
a6dd9dfa88 | |||
73af0a3069 | |||
8239d6edc9 | |||
0f8134edf5 | |||
85c45e32c0 | |||
eb55cb5d43 | |||
1e2569f144 | |||
7b15cf2347 | |||
74efa6d70c | |||
e0f175c6ba | |||
55a1095490 | |||
ecece4021f | |||
c90ec1a935 | |||
dc67745175 | |||
edd91ef692 | |||
de9eb51fbc |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/build/
|
||||
/exp/
|
15
CMakeLists.txt
Normal file
15
CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
|
||||
project(blocky C CXX)
|
||||
|
||||
option(BLOCKY_STATIC OFF)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_subdirectory(thirdparty)
|
||||
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -pedantic-errors -Wextra")
|
||||
add_subdirectory(conv)
|
||||
add_subdirectory(gen)
|
23
README.md
Normal file
23
README.md
Normal file
@ -0,0 +1,23 @@
|
||||
blocky
|
||||
====
|
||||
|
||||
## CMake command
|
||||
|
||||
When you built openh264 on `/home/user/openh264`:
|
||||
```
|
||||
cmake -DCMAKE_CXX_FLAGS=-isystem\ /home/user/openh264/codec/api\ -L/home/user/openh264 ..
|
||||
```
|
||||
|
||||
## ffmpeg useful commands
|
||||
|
||||
```
|
||||
ffmpeg -i in.mp4 -vframes 300 "%d.png"
|
||||
```
|
||||
|
||||
```
|
||||
ffmpeg -r 30 -i "%d.png" -vcodec libx264 -pix_fmt yuv420p out.mp4
|
||||
```
|
||||
|
||||
```
|
||||
ffmpeg -i src.mp4 -t 10s cut.mp4
|
||||
```
|
29
conv/CMakeLists.txt
Normal file
29
conv/CMakeLists.txt
Normal file
@ -0,0 +1,29 @@
|
||||
# ---- main procedural
|
||||
add_executable(dcode_feat common.hh dcode_feat.cc)
|
||||
target_link_libraries(dcode_feat PRIVATE args)
|
||||
|
||||
add_executable(feat_block common.hh feat_block.cc)
|
||||
target_link_libraries(feat_block PRIVATE args)
|
||||
|
||||
add_executable(block_stego common.hh block_stego.cc)
|
||||
target_link_libraries(block_stego PRIVATE args minimp4 openh264)
|
||||
|
||||
add_executable(stego_aprob common.hh stego_aprob.cc)
|
||||
target_link_libraries(stego_aprob PRIVATE args minimp4 openh264)
|
||||
|
||||
add_executable(aprob_fprob common.hh aprob_fprob.cc)
|
||||
target_link_libraries(aprob_fprob PRIVATE args)
|
||||
|
||||
add_executable(fprob_feat common.hh fprob_feat.cc)
|
||||
target_link_libraries(fprob_feat PRIVATE args)
|
||||
|
||||
add_executable(feat_dcode common.hh feat_dcode.cc)
|
||||
target_link_libraries(feat_dcode PRIVATE args)
|
||||
|
||||
|
||||
# ---- procedural of preprocessing
|
||||
add_executable(aprob_bmap common.hh aprob_bmap.cc)
|
||||
target_link_libraries(aprob_bmap PRIVATE args)
|
||||
|
||||
add_executable(bmap_fmap common.hh bmap_fmap.cc)
|
||||
target_link_libraries(bmap_fmap PRIVATE args)
|
72
conv/aprob_bmap.cc
Normal file
72
conv/aprob_bmap.cc
Normal file
@ -0,0 +1,72 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: aprob -> bmap"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<double> mid {
|
||||
parser, "probability", "desired value of alter-probability to select", {"mid"}, 0.5,
|
||||
};
|
||||
ValueFlag<double> max_dist {
|
||||
parser, "max distance", "maximum distance from mid value", {"max-dist"}, 0.2,
|
||||
};
|
||||
ValueFlag<uint32_t> min_bnum {
|
||||
parser, "number of blocks", "minimum number of blocks to select (prior than max-dist)", {"min-bnum"}, 32,
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
const auto aprob = ReadMatrix<double>(std::cin);
|
||||
|
||||
const auto mid = args::get(param::mid);
|
||||
const auto max_dist = args::get(param::max_dist);
|
||||
const auto min_bnum = args::get(param::min_bnum);
|
||||
|
||||
std::vector<std::pair<size_t, double>> vec;
|
||||
for (auto& probs : aprob) {
|
||||
vec.reserve(probs.size());
|
||||
vec.clear();
|
||||
for (auto prob : probs) {
|
||||
vec.emplace_back(vec.size(), std::abs(prob-mid));
|
||||
}
|
||||
std::sort(vec.begin(), vec.end(), [](auto& a, auto& b) { return a.second < b.second; });
|
||||
|
||||
Enforce(vec.size() >= min_bnum, "cannot satisfy min-bnum limitation");
|
||||
for (auto itr = vec.begin(); itr < vec.end(); ++itr) {
|
||||
if (itr >= vec.begin()+min_bnum) { // min-bnum is satisfied
|
||||
if (itr->second > max_dist) break; // max-dist is unsatisfied
|
||||
}
|
||||
std::cout << itr->first << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
76
conv/aprob_fprob.cc
Normal file
76
conv/aprob_fprob.cc
Normal file
@ -0,0 +1,76 @@
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: alter-probability matrix -> feature probability matrix"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<std::string> fmap {
|
||||
parser, "path", "feature map file path", {"fmap"},
|
||||
};
|
||||
|
||||
ValueFlag<size_t> negative_sample {
|
||||
parser, "samples", "number of samples used to calculate negative factor (deprecated and no effect)", {"negative-sample"}, 16,
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
const auto aprobs = ReadMatrix<double>(std::cin);
|
||||
Enforce(aprobs.size() > 0 && aprobs[0].size() > 0, "empty matrix");
|
||||
|
||||
std::ifstream fmap_st {args::get(param::fmap)};
|
||||
Enforce(!!fmap_st, "fmap path is invalid");
|
||||
const auto fmap = ReadTensor3<uint32_t>(fmap_st);
|
||||
Enforce(fmap.size() > 0 && fmap[0].size() > 0, "empty fmap");
|
||||
|
||||
std::vector<double> negatives;
|
||||
for (size_t t = 0; t < aprobs.size(); ++t) {
|
||||
const auto tidx = t % fmap.size();
|
||||
for (size_t c = 0; c < fmap[tidx].size(); ++c) {
|
||||
const auto& blocks = fmap[tidx][c];
|
||||
|
||||
double positive = 0;
|
||||
for (const auto b : blocks) {
|
||||
positive += aprobs[t][b];
|
||||
}
|
||||
|
||||
if (blocks.size() > 0) {
|
||||
positive /= blocks.size();
|
||||
} else {
|
||||
positive = 1;
|
||||
}
|
||||
|
||||
std::cout << positive << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
328
conv/block_stego.cc
Normal file
328
conv/block_stego.cc
Normal file
@ -0,0 +1,328 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <args.hxx>
|
||||
#include <minimp4.h>
|
||||
|
||||
#include <codec/api/wels/codec_api.h>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: block indices + host -> stego"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<int32_t> bw {
|
||||
parser, "128", "width of blocks (px)", {"block-w"}, 128,
|
||||
};
|
||||
ValueFlag<int32_t> bh {
|
||||
parser, "128", "height of blocks (px)", {"block-h"}, 128,
|
||||
};
|
||||
ValueFlag<int32_t> utime {
|
||||
parser, "10", "duration of each feature (frame)", {"utime"}, 10,
|
||||
};
|
||||
ValueFlag<int32_t> dur {
|
||||
parser, "0", "number of features to be extracted (ut)", {"dur"}, 0,
|
||||
};
|
||||
ValueFlag<int32_t> offset {
|
||||
parser, "0", "number of offset frames to start extraction", {"offset"}, 0,
|
||||
};
|
||||
Flag only_body {
|
||||
parser, "only-body", "cut off head and tail that no feature is embedded", {"only-body"},
|
||||
};
|
||||
|
||||
Flag uvfix {
|
||||
parser, "uvfix", "fix UV values in feature", {"uvfix"},
|
||||
};
|
||||
|
||||
Positional<std::string> dst {
|
||||
parser, "path", "destination video file path",
|
||||
};
|
||||
Positional<std::string> src {
|
||||
parser, "path", "source video file path",
|
||||
};
|
||||
|
||||
ValueFlag<int32_t> bitrate {
|
||||
parser, "bitrate", "bitrate of stego video", {"bitrate"}, 5000*1000,
|
||||
};
|
||||
|
||||
// from stdin
|
||||
std::vector<std::vector<int32_t>> indices;
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Embed(int32_t t, Frame& dst, const Frame& base) {
|
||||
const auto bw = args::get(param::bw);
|
||||
const auto bh = args::get(param::bh);
|
||||
const auto hbw = bw/2;
|
||||
const auto hbh = bh/2;
|
||||
|
||||
const auto bx_cnt = dst.w / bw;
|
||||
const auto by_cnt = dst.h / bh;
|
||||
|
||||
t = t%param::indices.size();
|
||||
for (auto idx : param::indices[t]) {
|
||||
Enforce(idx >= 0, "block index underflow");
|
||||
|
||||
const auto bx = idx%bx_cnt;
|
||||
const auto by = idx/bx_cnt;
|
||||
Enforce(by < by_cnt, "block index overflow");
|
||||
|
||||
for (int32_t y = 0; y < bh; ++y) {
|
||||
for (int32_t x = 0; x < bw; ++x) {
|
||||
const auto off = (by*bh+y)*base.w + (bx*bw+x);
|
||||
dst.Y[off] = base.Y[off];
|
||||
}
|
||||
}
|
||||
|
||||
if (param::uvfix) {
|
||||
for (int32_t y = 0; y < hbh; ++y) {
|
||||
for (int32_t x = 0; x < hbw; ++x) {
|
||||
const auto off = (by*hbh+y)*base.hw + (bx*hbw+x);
|
||||
dst.U[off] = base.U[off];
|
||||
dst.V[off] = base.V[off];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Exec() {
|
||||
const auto bw = args::get(param::bw);
|
||||
const auto bh = args::get(param::bh);
|
||||
const auto ut = args::get(param::utime);
|
||||
const auto dur = args::get(param::dur);
|
||||
const auto offset = args::get(param::offset);
|
||||
Enforce(bw > 0 && bh > 0, "block size must be greater than 0");
|
||||
Enforce(ut > 0, "utime must be greater than 0");
|
||||
|
||||
// read indices
|
||||
param::indices = ReadMatrix<int32_t>(std::cin);
|
||||
Enforce(param::indices.size() > 0, "empty indices");
|
||||
|
||||
// open source video stream
|
||||
const auto srcpath = args::get(param::src);
|
||||
std::ifstream srcst {srcpath.c_str(), std::ifstream::binary | std::ifstream::ate};
|
||||
Enforce(!!srcst, "source video stream is invalid");
|
||||
const int64_t srcsz = srcst.tellg();
|
||||
|
||||
// open destination video stream
|
||||
const auto dstpath = args::get(param::dst);
|
||||
std::ofstream dstst {dstpath.c_str(), std::ifstream::binary};
|
||||
Enforce(!!dstst, "destination video stream is invalid");
|
||||
|
||||
// init decoder
|
||||
ISVCDecoder* dec;
|
||||
Enforce(0 == WelsCreateDecoder(&dec), "decoder creation failure");
|
||||
|
||||
SDecodingParam decp = {};
|
||||
decp.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
|
||||
decp.eEcActiveIdc = ERROR_CON_SLICE_COPY;
|
||||
Enforce(0 == dec->Initialize(&decp), "decoder init failure");
|
||||
|
||||
int declv = WELS_LOG_INFO;
|
||||
dec->SetOption(DECODER_OPTION_TRACE_LEVEL, &declv);
|
||||
|
||||
uint8_t* yuv[3] = {0};
|
||||
SBufferInfo frame = {};
|
||||
|
||||
// demuxer
|
||||
MP4D_demux_t dem = {};
|
||||
MP4D_open(&dem, [](int64_t off, void* buf, size_t sz, void* ptr) {
|
||||
auto& st = *reinterpret_cast<std::ifstream*>(ptr);
|
||||
st.seekg(off);
|
||||
Enforce(!!st, "seek failure");
|
||||
st.read(reinterpret_cast<char*>(buf), sz);
|
||||
Enforce(!!st, "read failure");
|
||||
return 0;
|
||||
}, &srcst, srcsz);
|
||||
|
||||
// find video track
|
||||
size_t ti;
|
||||
for (ti = 0; ti < dem.track_count; ++ti) {
|
||||
const auto& t = dem.track[ti];
|
||||
if (t.handler_type == MP4D_HANDLER_TYPE_VIDE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Enforce(ti < dem.track_count, "no video track");
|
||||
const auto& tra = dem.track[ti];
|
||||
|
||||
// calc params
|
||||
const auto tscale = tra.timescale;
|
||||
const auto dur_t =
|
||||
(static_cast<uint64_t>(tra.duration_hi) << 32) |
|
||||
static_cast<uint64_t>(tra.duration_lo);
|
||||
const auto dursec = static_cast<float>(dur_t)/static_cast<float>(tscale);
|
||||
|
||||
const float fps = static_cast<float>(tra.sample_count)/dursec;
|
||||
const auto fps9 = static_cast<int>(90000/fps);
|
||||
const int32_t w = tra.SampleDescription.video.width;
|
||||
const int32_t h = tra.SampleDescription.video.height;
|
||||
|
||||
// init encoder
|
||||
ISVCEncoder* enc;
|
||||
Enforce(0 == WelsCreateSVCEncoder(&enc), "encoder creation failure");
|
||||
|
||||
SEncParamBase encp = {};
|
||||
encp.iUsageType = CAMERA_VIDEO_REAL_TIME;
|
||||
encp.fMaxFrameRate = fps;
|
||||
encp.iPicWidth = w;
|
||||
encp.iPicHeight = h;
|
||||
encp.iTargetBitrate = args::get(param::bitrate);
|
||||
Enforce(0 == enc->Initialize(&encp), "encoder init failure");
|
||||
|
||||
int enclv = WELS_LOG_INFO;
|
||||
enc->SetOption(ENCODER_OPTION_TRACE_LEVEL, &enclv);
|
||||
|
||||
// init muxer
|
||||
MP4E_mux_t* mux = MP4E_open(
|
||||
false, false, &dstst,
|
||||
[](int64_t off, const void* buf, size_t size, void* ptr) {
|
||||
auto& st = *reinterpret_cast<std::ostream*>(ptr);
|
||||
st.seekp(off);
|
||||
Enforce(!!st, "muxer seek failure");
|
||||
st.write(reinterpret_cast<const char*>(buf), size);
|
||||
Enforce(!!st, "muxer write failure");
|
||||
return 0;
|
||||
});
|
||||
|
||||
mp4_h26x_writer_t writer;
|
||||
Enforce(
|
||||
MP4E_STATUS_OK == mp4_h26x_write_init(&writer, mux, w, h, false),
|
||||
"failed to init mp4_h26x_writer_t");
|
||||
|
||||
// consume SPS
|
||||
std::vector<uint8_t> nal;
|
||||
for (size_t si = 0;; ++si) {
|
||||
int sz;
|
||||
auto sps = reinterpret_cast<const uint8_t*>(MP4D_read_sps(&dem, ti, si, &sz));
|
||||
if (!sps) break;
|
||||
CopyNal(nal, sps, sz);
|
||||
|
||||
const auto ret = dec->DecodeFrameNoDelay(nal.data(), nal.size(), yuv, &frame);
|
||||
Enforce(ret == 0, "SPS decode failure");
|
||||
}
|
||||
|
||||
// consume PPS
|
||||
for (size_t si = 0;; ++si) {
|
||||
int sz;
|
||||
auto pps = reinterpret_cast<const uint8_t*>(MP4D_read_pps(&dem, ti, si, &sz));
|
||||
if (!pps) break;
|
||||
CopyNal(nal, pps, sz);
|
||||
|
||||
const auto ret = dec->DecodeFrameNoDelay(nal.data(), nal.size(), yuv, &frame);
|
||||
Enforce(ret == 0, "PPS decode failure");
|
||||
}
|
||||
|
||||
// decode frame
|
||||
Frame bf = {};
|
||||
int32_t fidx = 0;
|
||||
for (size_t si = 0; si < tra.sample_count; ++si) {
|
||||
unsigned fsz, ftime, fdur;
|
||||
const auto off = MP4D_frame_offset(&dem, ti, si, &fsz, &ftime, &fdur);
|
||||
|
||||
srcst.seekg(off);
|
||||
Enforce(!!srcst, "NAL seek failure");
|
||||
|
||||
nal.resize(fsz);
|
||||
srcst.read(reinterpret_cast<char*>(nal.data()), fsz);
|
||||
Enforce(!!srcst, "NAL read failure");
|
||||
|
||||
// decode all nal blocks
|
||||
for (size_t i = 0; i < nal.size();) {
|
||||
uint32_t sz =
|
||||
(nal[i] << 24) | (nal[i+1] << 16) | (nal[i+2] << 8) | (nal[i+3] << 0);
|
||||
|
||||
nal[i+0] = 0;
|
||||
nal[i+1] = 0;
|
||||
nal[i+2] = 0;
|
||||
nal[i+3] = 1;
|
||||
sz += 4;
|
||||
|
||||
// retrieve a frame
|
||||
const auto ret = dec->DecodeFrameNoDelay(&nal[i], sz, yuv, &frame);
|
||||
Enforce(ret == 0, "frame decode failure");
|
||||
|
||||
// handle decoded frame
|
||||
if (frame.iBufferStatus) {
|
||||
bool encode_frame = !param::only_body;
|
||||
bool keep_frame = false;
|
||||
|
||||
// alter the frame if it's not the first
|
||||
Frame cf = {yuv, frame};
|
||||
if (offset <= fidx && (dur == 0 || fidx-offset < dur*ut)) {
|
||||
const auto t = (fidx-offset)/ut;
|
||||
const auto tf = (fidx-offset)%ut;
|
||||
if (tf > 0) {
|
||||
Embed(t, cf, bf);
|
||||
}
|
||||
encode_frame = true;
|
||||
keep_frame = (tf == 0);
|
||||
}
|
||||
|
||||
// encode
|
||||
if (encode_frame) {
|
||||
SFrameBSInfo info;
|
||||
SSourcePicture pic = cf.GetSourcePic();
|
||||
Enforce(cmResultSuccess == enc->EncodeFrame(&pic, &info),
|
||||
"encode failure");
|
||||
|
||||
// write buffer
|
||||
if (info.eFrameType != videoFrameTypeSkip) {
|
||||
for (int li = 0; li < info.iLayerNum; ++li) {
|
||||
const auto& l = info.sLayerInfo[li];
|
||||
|
||||
uint8_t* buf = l.pBsBuf;
|
||||
for (int ni = 0; ni < l.iNalCount; ++ni) {
|
||||
mp4_h26x_write_nal(
|
||||
&writer, buf, l.pNalLengthInByte[ni], fps9);
|
||||
buf += l.pNalLengthInByte[ni];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// save the frame if it's the first
|
||||
if (keep_frame) {
|
||||
bf = std::move(cf);
|
||||
}
|
||||
++fidx;
|
||||
}
|
||||
i += sz;
|
||||
}
|
||||
}
|
||||
|
||||
// tear down
|
||||
MP4E_close(mux);
|
||||
mp4_h26x_write_close(&writer);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
137
conv/bmap_fmap.cc
Normal file
137
conv/bmap_fmap.cc
Normal file
@ -0,0 +1,137 @@
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: bmap -> fmap"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<uint32_t> fnum {
|
||||
parser, "64", "number of feature kinds", {"fnum"}, 64,
|
||||
};
|
||||
ValueFlag<uint32_t> seed {
|
||||
parser, "0", "random seed to randomize selection of combination (0=no randomize)", {"seed"}, 0,
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static bool NextCombination(auto begin, auto end) noexcept {
|
||||
auto first = std::find(begin, end, true);
|
||||
if (first == end) {
|
||||
return false;
|
||||
}
|
||||
if (!NextCombination(first+1, end)) {
|
||||
if (first+1 >= end || *(first+1)) {
|
||||
return false;
|
||||
}
|
||||
const auto n = std::count(first+2, end, true);
|
||||
std::fill(first+2 , first+2+n, true);
|
||||
std::fill(first+2+n, end , false);
|
||||
*first = false;
|
||||
*(first+1) = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t XorShift(uint32_t* v) noexcept {
|
||||
*v ^= *v << 13;
|
||||
*v ^= *v >> 17;
|
||||
*v ^= *v << 5;
|
||||
return *v;
|
||||
}
|
||||
|
||||
static void Exec() {
|
||||
const auto bmap = ReadMatrix<uint32_t>(std::cin);
|
||||
const auto fnum = args::get(param::fnum);
|
||||
|
||||
for (uint32_t t = 0; t < bmap.size(); ++t) {
|
||||
const auto& blocks = bmap[t];
|
||||
const auto bnum = static_cast<uint32_t>(blocks.size());
|
||||
Enforce(fnum < pow(bnum, 2), "number of blocks is too less");
|
||||
|
||||
// calc skip vector
|
||||
std::vector<uint32_t> skip(fnum);
|
||||
uint32_t nCr = 1;
|
||||
for (uint32_t f = 0, r = 1; f < fnum; ++r) {
|
||||
nCr *= bnum - (r-1);
|
||||
nCr /= r;
|
||||
|
||||
f += nCr;
|
||||
if (f <= fnum || 0 == args::get(param::seed)) {
|
||||
const auto n = fnum - (f-nCr);
|
||||
auto begin = skip.begin() + f-nCr;
|
||||
std::fill(begin, begin+n, 1);
|
||||
} else {
|
||||
uint32_t seed = args::get(param::seed);
|
||||
uint64_t sum = 0;
|
||||
for (uint32_t i = f-nCr; i < fnum; ++i) {
|
||||
skip[i] = XorShift(&seed);
|
||||
sum += skip[i];
|
||||
}
|
||||
sum += XorShift(&seed);
|
||||
|
||||
const auto coe = nCr*1. / sum;
|
||||
for (uint32_t i = f-nCr; i < fnum; ++i) {
|
||||
skip[i] = std::max(uint32_t {1}, static_cast<uint32_t>(skip[i] * coe));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bool> C(bnum);
|
||||
uint32_t r = 0;
|
||||
for (uint32_t f = 0; f < fnum; ++f) {
|
||||
for (uint32_t s = 0; s < skip[f]; ++s) {
|
||||
if (!NextCombination(C.begin(), C.end())) {
|
||||
++r;
|
||||
assert(r <= bnum);
|
||||
std::fill(C.begin(), C.begin()+r, 1);
|
||||
std::fill(C.begin()+r, C.end(), 0);
|
||||
s = 0; // s will be 1 on next iteration
|
||||
}
|
||||
}
|
||||
|
||||
auto itr = C.begin();
|
||||
for (uint32_t i = 0; i < r; ++i, ++itr) {
|
||||
itr = std::find(itr, C.end(), true);
|
||||
if (itr >= C.end()) {
|
||||
for (const auto& b : C) std::cout << !!b << ',';
|
||||
std::cout << std::endl;
|
||||
assert(false);
|
||||
}
|
||||
const auto idx = std::distance(C.begin(), itr);
|
||||
std::cout << blocks[idx] << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << "----\n";
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
111
conv/common.hh
Normal file
111
conv/common.hh
Normal file
@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <codec/api/wels/codec_api.h>
|
||||
|
||||
|
||||
inline void Enforce(bool eval, const std::string& msg) {
|
||||
if (!eval) {
|
||||
throw std::runtime_error {msg};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto ReadMatrix(std::istream& st) noexcept {
|
||||
std::vector<std::vector<T>> ret;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(st, line)) {
|
||||
std::istringstream sst {line};
|
||||
ret.emplace_back(std::istream_iterator<T> {sst},
|
||||
std::istream_iterator<T> {});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
template <typename T>
|
||||
auto ReadTensor3(std::istream& st) noexcept {
|
||||
std::vector<std::vector<std::vector<T>>> ret(1);
|
||||
|
||||
std::string line;
|
||||
while (std::getline(st, line)) {
|
||||
if (line == "----") {
|
||||
ret.push_back({});
|
||||
} else {
|
||||
std::istringstream sst {line};
|
||||
ret.back().emplace_back(std::istream_iterator<T> {sst},
|
||||
std::istream_iterator<T> {});
|
||||
}
|
||||
}
|
||||
if (ret.back().empty()) ret.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ---- MP4 utilities
|
||||
inline void CopyNal(std::vector<uint8_t>& v, const uint8_t* buf, size_t sz) noexcept {
|
||||
v.resize(sz+4);
|
||||
v[0] = 0;
|
||||
v[1] = 0;
|
||||
v[2] = 0;
|
||||
v[3] = 1;
|
||||
std::memcpy(&v[4], buf, sz);
|
||||
}
|
||||
struct Frame {
|
||||
std::vector<uint8_t> Y;
|
||||
std::vector<uint8_t> U;
|
||||
std::vector<uint8_t> V;
|
||||
|
||||
int32_t w, h;
|
||||
int32_t hw, hh;
|
||||
|
||||
Frame() = default;
|
||||
Frame(uint8_t* yuv[3], const SBufferInfo& frame) {
|
||||
w = static_cast<int32_t>(frame.UsrData.sSystemBuffer.iWidth);
|
||||
h = static_cast<int32_t>(frame.UsrData.sSystemBuffer.iHeight);
|
||||
hw = w/2;
|
||||
hh = h/2;
|
||||
|
||||
const auto ystride = static_cast<int32_t>(frame.UsrData.sSystemBuffer.iStride[0]);
|
||||
const auto uvstride = static_cast<int32_t>(frame.UsrData.sSystemBuffer.iStride[1]);
|
||||
|
||||
Y.resize(w*h);
|
||||
for (int32_t y = 0; y < h; ++y) {
|
||||
const auto src = yuv[0] + y*ystride;
|
||||
const auto dst = Y.data() + y*w;
|
||||
std::memcpy(dst, src, w);
|
||||
}
|
||||
|
||||
U.resize(hw*hh);
|
||||
V.resize(hw*hh);
|
||||
for (int32_t y = 0; y < hh; ++y) {
|
||||
const auto srcu = yuv[1] + y*uvstride;
|
||||
const auto srcv = yuv[2] + y*uvstride;
|
||||
const auto dstu = U.data() + y*hw;
|
||||
const auto dstv = V.data() + y*hw;
|
||||
std::memcpy(dstu, srcu, hw);
|
||||
std::memcpy(dstv, srcv, hw);
|
||||
}
|
||||
}
|
||||
|
||||
SSourcePicture GetSourcePic() noexcept {
|
||||
SSourcePicture ret;
|
||||
ret.iPicWidth = w;
|
||||
ret.iPicHeight = h;
|
||||
ret.iColorFormat = videoFormatI420;
|
||||
ret.iStride[0] = w;
|
||||
ret.iStride[1] = hw;
|
||||
ret.iStride[2] = hw;
|
||||
|
||||
ret.pData[0] = Y.data();
|
||||
ret.pData[1] = U.data();
|
||||
ret.pData[2] = V.data();
|
||||
return ret;
|
||||
}
|
||||
};
|
70
conv/dcode_feat.cc
Normal file
70
conv/dcode_feat.cc
Normal file
@ -0,0 +1,70 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: data code -> feature"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<std::string> smap {
|
||||
parser, "path", "step map file path", {"smap"},
|
||||
};
|
||||
|
||||
ValueFlag<uint32_t> first {
|
||||
parser, "0", "first feature", {"first"}, 0
|
||||
};
|
||||
ValueFlag<uint32_t> fnum {
|
||||
parser, "50", "number of feature kinds", {"fnum"}, 50
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
const auto fnum = args::get(param::fnum);
|
||||
Enforce(fnum > 0, "fnum must be greater than 0");
|
||||
|
||||
std::ifstream smap_st {args::get(param::smap)};
|
||||
Enforce(!!smap_st, "smap path is invalid");
|
||||
const auto smap = ReadMatrix<uint32_t>(smap_st);
|
||||
Enforce(smap.size() > 0 && smap[0].size() > 0, "empty smap");
|
||||
|
||||
const auto bn = smap[0].size();
|
||||
for (auto& br : smap) {
|
||||
Enforce(br.size() == bn, "all node should have the same number of branch");
|
||||
}
|
||||
|
||||
uint32_t feat = args::get(param::first);
|
||||
std::cout << feat << '\n';
|
||||
|
||||
for (uint32_t dcode, t = 0; std::cin >> dcode; ++t) {
|
||||
Enforce(dcode < bn, "dcode must be lower than number of branch");
|
||||
Enforce(fnum*(t+1) <= smap.size(), "smap row shortage");
|
||||
feat = smap[t*fnum + feat][dcode];
|
||||
std::cout << feat << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
57
conv/feat_block.cc
Normal file
57
conv/feat_block.cc
Normal file
@ -0,0 +1,57 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: feature -> block"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<std::string> fmap {
|
||||
parser, "path", "path to feature map", {"fmap"},
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
std::ifstream fmap_st {args::get(param::fmap)};
|
||||
Enforce(!!fmap_st, "fmap path is invalid");
|
||||
const auto fmap = ReadTensor3<uint32_t>(fmap_st);
|
||||
Enforce(fmap.size() > 0 && fmap[0].size() > 0, "empty fmap");
|
||||
for (auto& fmap_t : fmap) {
|
||||
Enforce(fmap_t.size() == fmap[0].size(), "fmap is broken");
|
||||
}
|
||||
|
||||
for (size_t feat, t = 0; std::cin >> feat; ++t) {
|
||||
const auto tidx = t % fmap.size();
|
||||
Enforce(feat < fmap[tidx].size(), "feat overflow");
|
||||
for (const auto idx : fmap[tidx][feat]) {
|
||||
std::cout << idx << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
71
conv/feat_dcode.cc
Normal file
71
conv/feat_dcode.cc
Normal file
@ -0,0 +1,71 @@
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: feature code probability matrix -> feature code"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<std::string> smap {
|
||||
parser, "path", "step map file path", {"smap"},
|
||||
};
|
||||
|
||||
ValueFlag<uint32_t> fnum {
|
||||
parser, "50", "number of feature kinds", {"fnum"}, 50
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
const auto fnum = args::get(param::fnum);
|
||||
Enforce(fnum > 0, "fnum must be greater than 0");
|
||||
|
||||
std::ifstream smap_st {args::get(param::smap)};
|
||||
Enforce(!!smap_st, "smap path is invalid");
|
||||
const auto smap = ReadMatrix<uint32_t>(smap_st);
|
||||
Enforce(smap.size() > 0 && smap[0].size() > 0, "empty smap");
|
||||
|
||||
const auto bn = smap[0].size();
|
||||
for (auto& br : smap) {
|
||||
Enforce(br.size() == bn, "all node should have the same number of branch");
|
||||
}
|
||||
|
||||
size_t feat_p;
|
||||
std::cin >> feat_p;
|
||||
for (uint32_t feat, t = 0; std::cin >> feat; ++t) {
|
||||
Enforce(feat < fnum, "feat overflow");
|
||||
Enforce(fnum*(t+1) <= smap.size(), "smap row shortage");
|
||||
|
||||
const auto& row = smap[t*fnum + feat_p];
|
||||
auto itr = std::find(row.begin(), row.end(), feat);
|
||||
Enforce(itr != row.end(), "invalid step detected");
|
||||
std::cout << std::distance(row.begin(), itr) << '\n';
|
||||
feat_p = feat;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
109
conv/fprob_feat.cc
Normal file
109
conv/fprob_feat.cc
Normal file
@ -0,0 +1,109 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: feature probability matrix -> feature series"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<std::string> smap {
|
||||
parser, "path", "step map file path", {"smap"},
|
||||
};
|
||||
|
||||
Flag output_prob {
|
||||
parser, "output-prob", "prints path probability at last", {"output-prob", "prob"},
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
static void Exec() {
|
||||
const auto fprobs = ReadMatrix<double>(std::cin);
|
||||
Enforce(fprobs.size() > 0 && fprobs[0].size() > 0, "empty matrix");
|
||||
|
||||
const auto dur = fprobs.size();
|
||||
const auto n = fprobs[0].size();
|
||||
|
||||
std::ifstream smap_st {args::get(param::smap)};
|
||||
Enforce(!!smap_st, "smap path is invalid");
|
||||
const auto smap = ReadMatrix<uint32_t>(smap_st);
|
||||
Enforce(smap.size() >= dur*n, "smap row shortage");
|
||||
|
||||
struct Step {
|
||||
double prob = -1;
|
||||
size_t from = 0;
|
||||
};
|
||||
std::vector<Step> steps((dur+1)*n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
steps[i].prob = fprobs[0][i];
|
||||
}
|
||||
for (size_t t = 1; t < dur; ++t) {
|
||||
Enforce(fprobs[t].size() == n, "ill-formed matrix");
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const auto& cur = steps[(t-1)*n + i];
|
||||
for (auto j : smap[(t-1)*n+i]) {
|
||||
auto& next = steps[t*n + j];
|
||||
|
||||
const auto sum = cur.prob + fprobs[t][j];
|
||||
if (next.prob < sum) {
|
||||
next.prob = sum;
|
||||
next.from = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double max_prob = -1;
|
||||
size_t max_idx = 0;
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
const auto& step = steps[(dur-1)*n + i];
|
||||
if (max_prob < step.prob) {
|
||||
max_prob = step.prob;
|
||||
max_idx = i;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<size_t> path = {max_idx};
|
||||
path.reserve(dur);
|
||||
for (size_t t = dur-1; t > 0; --t) {
|
||||
path.push_back(steps[t*n + path.back()].from);
|
||||
}
|
||||
for (auto itr = path.rbegin(); itr < path.rend(); ++itr) {
|
||||
std::cout << *itr << '\n';
|
||||
}
|
||||
if (param::output_prob) {
|
||||
std::cout << max_prob/static_cast<double>(path.size())*100 << "%" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
328
conv/stego_aprob.cc
Normal file
328
conv/stego_aprob.cc
Normal file
@ -0,0 +1,328 @@
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <args.hxx>
|
||||
#include <minimp4.h>
|
||||
|
||||
#include <codec/api/wels/codec_api.h>
|
||||
|
||||
#include "common.hh"
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: stego -> alter-probability matrix"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<int32_t> bw {
|
||||
parser, "128", "width of blocks (px)", {"block-w"}, 128,
|
||||
};
|
||||
ValueFlag<int32_t> bh {
|
||||
parser, "128", "height of blocks (px)", {"block-h"}, 128,
|
||||
};
|
||||
ValueFlag<int32_t> utime {
|
||||
parser, "10", "duration of each feature (frame)", {"utime"}, 10,
|
||||
};
|
||||
ValueFlag<int32_t> dur {
|
||||
parser, "0", "number of features to be extracted (ut)", {"dur"}, 0,
|
||||
};
|
||||
ValueFlag<int32_t> offset {
|
||||
parser, "0", "number of offset frames to start extraction", {"offset"}, 0,
|
||||
};
|
||||
|
||||
ValueFlag<int32_t> bmix {
|
||||
parser, "8", "x interval of blockmatch (px)", {"bm-ix"}, 8,
|
||||
};
|
||||
ValueFlag<int32_t> bmiy {
|
||||
parser, "8", "y interval of blockmatch (px)", {"bm-iy"}, 8,
|
||||
};
|
||||
ValueFlag<int32_t> bmsw {
|
||||
parser, "4", "width of blockmatch search region (px)", {"bm-sw"}, 4,
|
||||
};
|
||||
ValueFlag<int32_t> bmsh {
|
||||
parser, "4", "height of blockmatch search region (px)", {"bm-sh"}, 4,
|
||||
};
|
||||
|
||||
enum Output {
|
||||
kProb,
|
||||
kIndex,
|
||||
kLen,
|
||||
kVec,
|
||||
kNull,
|
||||
};
|
||||
const std::unordered_map<std::string, Output> kOutput = {
|
||||
{"prob", kProb},
|
||||
{"index", kIndex},
|
||||
{"len", kLen},
|
||||
{"vec", kVec},
|
||||
{"null", kNull},
|
||||
};
|
||||
MapFlag<std::string, Output> output {
|
||||
parser, "prob", "output type (len, vec, null)", {"output"}, kOutput,
|
||||
};
|
||||
|
||||
Positional<std::string> vpath {
|
||||
parser, "path", "video file path",
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
struct Vec {
|
||||
double x, y, score, len;
|
||||
};
|
||||
|
||||
|
||||
static Vec BlockMatching(const Frame& cf, const Frame& pf, int32_t bx, int32_t by) {
|
||||
const auto bw = args::get(param::bw);
|
||||
const auto bh = args::get(param::bh);
|
||||
const auto bmix = args::get(param::bmix);
|
||||
const auto bmiy = args::get(param::bmiy);
|
||||
const auto bmsw = args::get(param::bmsw);
|
||||
const auto bmsh = args::get(param::bmsh);
|
||||
|
||||
int32_t min_sx = 0, min_sy = 0;
|
||||
double min_score = 1e+100; // INF
|
||||
for (int32_t sy = -bmsh; sy < bmsh; ++sy) {
|
||||
for (int32_t sx = -bmsw; sx < bmsw; ++sx) {
|
||||
double score = 0;
|
||||
for (int32_t y = 0; y < bh; y += bmiy) {
|
||||
for (int32_t x = 0; x < bw; x += bmix) {
|
||||
const auto c_off = (bx+x) + (by+y)*cf.w;
|
||||
const auto p_off = (bx+x+sx) + (by+y+sy)*cf.w;
|
||||
const auto diff = static_cast<double>(cf.Y[c_off] - pf.Y[p_off]);
|
||||
score += diff*diff;
|
||||
}
|
||||
}
|
||||
if (score < min_score) {
|
||||
min_score = score;
|
||||
min_sx = sx;
|
||||
min_sy = sy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto sxf = static_cast<double>(min_sx) / static_cast<double>(bmsw);
|
||||
const auto syf = static_cast<double>(min_sy) / static_cast<double>(bmsh);
|
||||
const auto scf = static_cast<double>(min_score) / static_cast<double>(UINT8_MAX*(bw/bmix)*(bh/bmiy));
|
||||
return { .x = sxf, .y = syf, .score = scf, .len = std::sqrt(sxf*sxf+syf*syf), };
|
||||
}
|
||||
|
||||
static Vec EachBlock(const Frame& cf, const Frame& pf, int32_t bx, int32_t by) {
|
||||
const auto v = BlockMatching(cf, pf, bx, by);
|
||||
switch (args::get(param::output)) {
|
||||
case param::kLen:
|
||||
std::cout << v.len << '\n';
|
||||
break;
|
||||
case param::kVec:
|
||||
std::cout << bx << " " << by << " " << v.x << " " << v.y << " " << v.score << '\n';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static void EachFrame(int32_t t, const Frame& cf, const Frame& pf) {
|
||||
const auto bw = args::get(param::bw);
|
||||
const auto bh = args::get(param::bh);
|
||||
const auto ut = args::get(param::utime);
|
||||
|
||||
Enforce(cf.w == pf.w && cf.h == pf.h, "variable frame size is not allowed");
|
||||
Enforce(cf.w > bw && cf.h > bh, "block size must be less than frame size");
|
||||
|
||||
struct Block {
|
||||
double len, score;
|
||||
};
|
||||
static std::vector<Block> blocks;
|
||||
if (t == 1) {
|
||||
blocks.clear();
|
||||
blocks.resize((cf.w/bw) * (cf.h/bh));
|
||||
}
|
||||
|
||||
auto block = blocks.data();
|
||||
for (int32_t by = 0; by+bh <= cf.h; by+=bh) {
|
||||
for (int32_t bx = 0; bx+bw <= cf.w; bx+=bw) {
|
||||
const auto v = EachBlock(cf, pf, bx, by);
|
||||
block->score += v.score;
|
||||
block->len += v.len;
|
||||
++block;
|
||||
}
|
||||
}
|
||||
|
||||
switch (args::get(param::output)) {
|
||||
case param::kLen:
|
||||
case param::kVec:
|
||||
std::cout << std::endl;
|
||||
break;
|
||||
case param::kIndex:
|
||||
case param::kProb:
|
||||
if (t == ut-1) {
|
||||
for (size_t i = 0; i < blocks.size(); ++i) {
|
||||
const auto len = blocks[i].len/(ut-1)/std::sqrt(2); // length calculation
|
||||
const auto score = blocks[i].score/(ut-1);
|
||||
const auto prob = std::clamp((1-len) * (1-score), 0., 1.);
|
||||
|
||||
if (args::get(param::output) == param::kIndex) {
|
||||
if (prob > 0.95) std::cout << i << ' ';
|
||||
} else {
|
||||
std::cout << prob << ' ';
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void Exec() {
|
||||
const auto bw = args::get(param::bw);
|
||||
const auto bh = args::get(param::bh);
|
||||
const auto ut = args::get(param::utime);
|
||||
const auto dur = args::get(param::dur);
|
||||
const auto offset = args::get(param::offset);
|
||||
Enforce(bw > 0 && bh > 0, "block size must be greater than 0");
|
||||
Enforce(ut > 0, "utime must be greater than 0");
|
||||
|
||||
const auto bmix = args::get(param::bmix);
|
||||
const auto bmiy = args::get(param::bmiy);
|
||||
const auto bmsw = args::get(param::bmsw);
|
||||
const auto bmsh = args::get(param::bmsh);
|
||||
Enforce(bmix > 0 && bmiy > 0, "block matching search interval must be greater than 0");
|
||||
Enforce(bmsw > 0 && bmsh > 0, "block matching search region size must be greater than 0");
|
||||
|
||||
// open video stream
|
||||
const auto vpath = args::get(param::vpath);
|
||||
std::ifstream vst {vpath.c_str(), std::ifstream::binary | std::ifstream::ate};
|
||||
Enforce(!!vst, "video stream is invalid");
|
||||
const auto vsz = vst.tellg();
|
||||
|
||||
// init decoder
|
||||
ISVCDecoder* dec;
|
||||
Enforce(0 == WelsCreateDecoder(&dec), "decoder creation failure");
|
||||
|
||||
SDecodingParam decp = {};
|
||||
decp.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
|
||||
decp.eEcActiveIdc = ERROR_CON_SLICE_COPY;
|
||||
Enforce(0 == dec->Initialize(&decp), "decoder init failure");
|
||||
|
||||
int declv = WELS_LOG_DEBUG;
|
||||
dec->SetOption(DECODER_OPTION_TRACE_LEVEL, &declv);
|
||||
|
||||
uint8_t* yuv[3] = {0};
|
||||
SBufferInfo frame = {};
|
||||
|
||||
// demux
|
||||
MP4D_demux_t dem = {};
|
||||
MP4D_open(&dem, [](int64_t off, void* buf, size_t sz, void* ptr) {
|
||||
auto& vst = *reinterpret_cast<std::ifstream*>(ptr);
|
||||
vst.seekg(off);
|
||||
Enforce(!!vst, "seek failure");
|
||||
vst.read(reinterpret_cast<char*>(buf), sz);
|
||||
Enforce(!!vst, "read failure");
|
||||
return 0;
|
||||
}, &vst, vsz);
|
||||
|
||||
// find video track
|
||||
size_t ti;
|
||||
for (ti = 0; ti < dem.track_count; ++ti) {
|
||||
const auto& tra = dem.track[ti];
|
||||
if (tra.handler_type == MP4D_HANDLER_TYPE_VIDE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Enforce(ti < dem.track_count, "no video track");
|
||||
const auto& tra = dem.track[ti];
|
||||
|
||||
// consume SPS
|
||||
std::vector<uint8_t> nal;
|
||||
for (size_t si = 0;; ++si) {
|
||||
int sz;
|
||||
auto sps = reinterpret_cast<const uint8_t*>(MP4D_read_sps(&dem, ti, si, &sz));
|
||||
if (!sps) break;
|
||||
CopyNal(nal, sps, sz);
|
||||
|
||||
const auto ret = dec->DecodeFrameNoDelay(nal.data(), nal.size(), yuv, &frame);
|
||||
Enforce(ret == 0, "SPS decode failure");
|
||||
}
|
||||
|
||||
// consume PPS
|
||||
for (size_t si = 0;; ++si) {
|
||||
int sz;
|
||||
auto pps = reinterpret_cast<const uint8_t*>(MP4D_read_pps(&dem, ti, si, &sz));
|
||||
if (!pps) break;
|
||||
CopyNal(nal, pps, sz);
|
||||
|
||||
const auto ret = dec->DecodeFrameNoDelay(nal.data(), nal.size(), yuv, &frame);
|
||||
Enforce(ret == 0, "PPS decode failure");
|
||||
}
|
||||
|
||||
// decode frame
|
||||
Frame pf = {};
|
||||
int32_t fidx = 0;
|
||||
for (size_t si = 0; si < tra.sample_count; ++si) {
|
||||
unsigned fsz, ftime, fdur;
|
||||
const auto off = MP4D_frame_offset(&dem, ti, si, &fsz, &ftime, &fdur);
|
||||
|
||||
vst.seekg(off);
|
||||
Enforce(!!vst, "NAL seek failure");
|
||||
|
||||
nal.resize(fsz);
|
||||
vst.read(reinterpret_cast<char*>(nal.data()), fsz);
|
||||
Enforce(!!vst, "NAL read failure");
|
||||
|
||||
for (size_t i = 0; i < nal.size();) {
|
||||
uint32_t sz =
|
||||
(nal[i] << 24) | (nal[i+1] << 16) | (nal[i+2] << 8) | nal[i+3];
|
||||
|
||||
nal[i+0] = 0;
|
||||
nal[i+1] = 0;
|
||||
nal[i+2] = 0;
|
||||
nal[i+3] = 1;
|
||||
sz += 4;
|
||||
|
||||
const auto ret = dec->DecodeFrameNoDelay(&nal[i], sz, yuv, &frame);
|
||||
Enforce(ret == 0, "frame decode failure");
|
||||
i += sz;
|
||||
|
||||
if (offset <= fidx && (dur == 0 || fidx-offset < dur*ut)) {
|
||||
Frame cf = {yuv, frame};
|
||||
if (cf.w == 0 || cf.h == 0) continue;
|
||||
|
||||
const auto tf = (fidx-offset)%ut;
|
||||
if (tf > 0) {
|
||||
EachFrame(tf, cf, pf);
|
||||
}
|
||||
pf = std::move(cf);
|
||||
}
|
||||
++fidx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
2
gen/CMakeLists.txt
Normal file
2
gen/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
||||
add_executable(smap smap.cc)
|
||||
target_link_libraries(smap PRIVATE args)
|
93
gen/smap.cc
Normal file
93
gen/smap.cc
Normal file
@ -0,0 +1,93 @@
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <args.hxx>
|
||||
|
||||
|
||||
namespace param {
|
||||
using namespace ::args;
|
||||
|
||||
ArgumentParser parser {
|
||||
"converter: feature indices + host -> stego"
|
||||
};
|
||||
HelpFlag help {
|
||||
parser, "help", "display this menu", {'h', "help"},
|
||||
};
|
||||
|
||||
ValueFlag<size_t> dur {
|
||||
parser, "100", "duration", {"dur"}, 100,
|
||||
};
|
||||
ValueFlag<size_t> fnum {
|
||||
parser, "50", "number of feature code alphabet", {"fnum"}, 50,
|
||||
};
|
||||
ValueFlag<size_t> branch {
|
||||
parser, "2", "number of branch", {"branch"}, 2,
|
||||
};
|
||||
|
||||
enum Algo {
|
||||
kIncrement,
|
||||
};
|
||||
const std::unordered_map<std::string, Algo> kAlgo = {
|
||||
{"inc", kIncrement},
|
||||
};
|
||||
MapFlag<std::string, Algo> algo {
|
||||
parser, "inc", "generator algorithm", {"algo", "algorithm"}, kAlgo,
|
||||
};
|
||||
|
||||
Group inc {
|
||||
parser, "increment algorithm parameters"
|
||||
};
|
||||
ValueFlag<size_t> inc_min {
|
||||
inc, "0", "min stride of each move", {"inc-min"}, 0,
|
||||
};
|
||||
Flag inc_time {
|
||||
inc, "inc-time", "add current time value", {"inc-time"},
|
||||
};
|
||||
|
||||
} // namespace param
|
||||
|
||||
|
||||
size_t Step(size_t t, size_t c, size_t b) {
|
||||
const auto fnum = args::get(param::fnum);
|
||||
|
||||
size_t ret;
|
||||
switch (args::get(param::algo)) {
|
||||
case param::kIncrement:
|
||||
ret = c+b+args::get(param::inc_min);
|
||||
if (param::inc_time) {
|
||||
ret += t;
|
||||
}
|
||||
return ret%fnum;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
void Exec() {
|
||||
for (size_t t = 0; t < args::get(param::dur); ++t) {
|
||||
for (size_t c = 0; c < args::get(param::fnum); ++c) {
|
||||
for (size_t b = 0; b < args::get(param::branch); ++b) {
|
||||
std::cout << Step(t, c, b) << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
try {
|
||||
param::parser.ParseCLI(argc, argv);
|
||||
Exec();
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const args::Help&) {
|
||||
std::cout << param::parser << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
36
thirdparty/CMakeLists.txt
vendored
Normal file
36
thirdparty/CMakeLists.txt
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
include(FetchContent)
|
||||
|
||||
|
||||
# ---- args ----
|
||||
# repository: https://github.com/Taywee/args
|
||||
# license : MIT
|
||||
|
||||
FetchContent_Declare(
|
||||
args
|
||||
URL "https://github.com/Taywee/args/archive/refs/tags/6.3.0.zip"
|
||||
)
|
||||
|
||||
set(ARGS_BUILD_EXAMPLE OFF)
|
||||
set(ARGS_BUILD_UNITTESTS OFF)
|
||||
FetchContent_MakeAvailable(args)
|
||||
|
||||
|
||||
# ---- minimp4 ----
|
||||
# repository: https://github.com/lieff/minimp4
|
||||
# license : CC0
|
||||
|
||||
FetchContent_Declare(
|
||||
minimp4
|
||||
URL "https://github.com/lieff/minimp4/archive/4575afb4f69ace25a1a048e25cc86bf8c8d14f2b.zip"
|
||||
)
|
||||
FetchContent_Populate(minimp4)
|
||||
|
||||
add_library(minimp4)
|
||||
target_include_directories(minimp4 PUBLIC SYSTEM ${minimp4_SOURCE_DIR})
|
||||
target_sources(minimp4
|
||||
PUBLIC
|
||||
"${minimp4_SOURCE_DIR}/minimp4.h"
|
||||
PRIVATE
|
||||
minimp4.c
|
||||
)
|
2
thirdparty/minimp4.c
vendored
Normal file
2
thirdparty/minimp4.c
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#define MINIMP4_IMPLEMENTATION
|
||||
#include <minimp4.h>
|
Loading…
x
Reference in New Issue
Block a user