Gamma: allow depth conversion

This commit is contained in:
Martin Pulec
2020-06-24 13:26:38 +02:00
parent 649f615dfb
commit 843d7bebe1

View File

@@ -66,68 +66,86 @@ using std::thread;
struct state_capture_filter_gamma {
public:
explicit state_capture_filter_gamma(double gamma) {
for (int i = 0; i <= numeric_limits<uint8_t>::max(); ++i) {
int out_depth; ///< 0, 8 or 16 (0 menas keep)
explicit state_capture_filter_gamma(double gamma, int out_depth) : out_depth(out_depth) {
for (int i = 0; i <= numeric_limits<uint8_t>::max(); ++i) { // 8->8
lut8.push_back(pow(static_cast<double>(i)
/ numeric_limits<uint8_t>::max(), gamma)
* numeric_limits<uint8_t>::max());
}
for (int i = 0; i < numeric_limits<uint16_t>::max(); ++i) {
for (int i = 0; i <= numeric_limits<uint16_t>::max(); ++i) { // 8->16
lut16.push_back(pow(static_cast<double>(i)
/ numeric_limits<uint16_t>::max(), gamma)
* numeric_limits<uint16_t>::max());
}
for (int i = 0; i <= numeric_limits<uint8_t>::max(); ++i) { // 8->16
lut8_16.push_back(pow(static_cast<double>(i)
/ numeric_limits<uint8_t>::max(), gamma)
* numeric_limits<uint16_t>::max());
}
for (int i = 0; i <= numeric_limits<uint16_t>::max(); ++i) { // 16->8
lut16_8.push_back(pow(static_cast<double>(i)
/ numeric_limits<uint16_t>::max(), gamma)
* numeric_limits<uint8_t>::max());
}
}
void apply_gamma(int depth, size_t len, void const * __restrict in, void * __restrict out) {
if (depth == CHAR_BIT) {
apply_lut<uint8_t>(len, lut8, in, out);
} else if (depth == 2 * CHAR_BIT) {
apply_lut<uint16_t>(len, lut16, in, out);
void apply_gamma(int in_depth, int out_depth, size_t in_len, void const * __restrict in, void * __restrict out) {
if (in_depth == CHAR_BIT && out_depth == CHAR_BIT) {
apply_lut<uint8_t, uint8_t>(in_len, lut8, in, out);
} else if (in_depth == 2 * CHAR_BIT && out_depth == 2 * CHAR_BIT) {
apply_lut<uint16_t, uint16_t>(in_len, lut16, in, out);
} else if (in_depth == CHAR_BIT && out_depth == 2 * CHAR_BIT) {
apply_lut<uint8_t, uint16_t>(in_len, lut8_16, in, out);
} else if (in_depth == 2 * CHAR_BIT && out_depth == CHAR_BIT) {
apply_lut<uint16_t, uint8_t>(in_len, lut16_8, in, out);
} else {
throw exception();
}
}
private:
template<typename T>
template<typename inT, typename outT>
struct data {
size_t len;
const vector<T> &lut;
const T *in;
T *out;
const vector<outT> &lut;
const inT *in;
outT *out;
};
template<typename T>
template<typename inT, typename outT>
static void *compute(void *arg) {
auto *d = static_cast<struct data<T> *>(arg);
auto *d = static_cast<struct data<inT, outT> *>(arg);
for (size_t i = 0; i < d->len; ++i) {
d->out[i] = d->lut[d->in[i]];
}
return nullptr;
}
template<typename T> void apply_lut(size_t len, const vector<T> &lut, void const *in, void *out)
template<typename inT, typename outT> void apply_lut(size_t in_len, const vector<outT> &lut, void const *in, void *out)
{
auto *in_data = static_cast<const T*>(in);
auto *out_data = static_cast<T*>(out);
auto *in_data = static_cast<const inT*>(in);
auto *out_data = static_cast<outT*>(out);
unsigned int cpus = thread::hardware_concurrency();
len /= sizeof(T);
vector<data<T>> d;
in_len /= sizeof(inT);
vector<data<inT, outT>> d;
vector<task_result_handle_t> handles(cpus);
for (unsigned int i = 0; i < cpus; i++) {
d.push_back({len / cpus, lut, in_data + i * (len / cpus), out_data + i * (len / cpus)});
d.push_back({in_len / cpus, lut, in_data + i * (in_len / cpus), out_data + i * (in_len / cpus)});
}
for (unsigned int i = 0; i < cpus; i++) {
handles[i] = task_run_async(state_capture_filter_gamma::compute<T>, static_cast<void *>(&(d[i])));
handles[i] = task_run_async(state_capture_filter_gamma::compute<inT, outT>, static_cast<void *>(&(d[i])));
}
for (unsigned int i = 0; i < cpus; i++) {
wait_task(handles[i]);
wait_task(handles[i]);
}
}
vector<uint8_t> lut8;
vector<uint16_t> lut16;
vector<uint8_t> lut16_8;
vector<uint16_t> lut8_16;
};
static auto init(struct module *parent, const char *cfg, void **state)
@@ -137,18 +155,31 @@ static auto init(struct module *parent, const char *cfg, void **state)
if (cfg == nullptr || strcmp(cfg, "help") == 0) {
cout << "Performs gamma transformation.\n\n"
"usage:\n";
cout << style::bold << "\t--capture-filter gamma:value\n" << style::reset;
cout << style::bold << "\t--capture-filter gamma:value[:8|:16]\n" << style::reset;
cout << "where:\n";
cout << style::bold << "\t8|16" << style::reset << " - force output to 8 (16) bits regardless the input\n";
return 1;
}
char *endptr = nullptr;
errno = 0;
double gamma = strtod(cfg, &endptr);
if (gamma <= 0.0 || errno != 0 || *endptr != '\0') {
if (gamma <= 0.0 || errno != 0 || (*endptr != '\0' && *endptr != ':')) {
LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Using gamma value " << gamma << "\n";
}
auto *s = new state_capture_filter_gamma(gamma);
long int bits = 0;
if (*endptr != '\0') {
endptr += 1;
errno = 0;
bits = strtol(endptr, &endptr, 0);
if ((bits != 8 && bits != 16) || *endptr != '\0') {
LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Wrong number of bits (only 8 or 16)!\n";
return -1;
}
}
auto *s = new state_capture_filter_gamma(gamma, bits);
*state = s;
return 0;
@@ -159,15 +190,24 @@ static void done(void *state)
delete static_cast<state_capture_filter_gamma *>(state);
}
static auto filter(void *state, struct video_frame *in)
static auto filter(void *state, struct video_frame *in) -> video_frame *
{
if (in->color_spec != RGB && in->color_spec != RG48) {
LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unable to apply lut on: " << get_codec_name(in->color_spec) << "\n";
VIDEO_FRAME_DISPOSE(in);
return nullptr;
}
auto *s = static_cast<state_capture_filter_gamma *>(state);
struct video_desc desc = video_desc_from_frame(in);
struct video_frame *out = vf_alloc_desc_data(desc);
struct video_desc out_desc = video_desc_from_frame(in);
if (s->out_depth != 0) {
out_desc.color_spec = s->out_depth == 8 ? RGB : RG48;
}
struct video_frame *out = vf_alloc_desc_data(out_desc);
out->callbacks.dispose = vf_free;
try {
s->apply_gamma(get_bits_per_component(desc.color_spec), in->tiles[0].data_len, in->tiles[0].data, out->tiles[0].data);
s->apply_gamma(get_bits_per_component(in->color_spec), get_bits_per_component(out_desc.color_spec), in->tiles[0].data_len, in->tiles[0].data, out->tiles[0].data);
} catch(...) {
LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Only 8-bit and 16-bit codecs are currently supported!\n";
vf_free(out);