mirror of
https://github.com/outbackdingo/UltraGrid.git
synced 2026-03-21 06:40:15 +00:00
Gamma: allow depth conversion
This commit is contained in:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user