UltraGrid
UltraGrid Low-Latency High-Resolution Transmission System
Loading...
Searching...
No Matches
gamma.cpp
Go to the documentation of this file.
1
5/*
6 * Copyright (c) 2020 CESNET, z. s. p. o.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, is permitted provided that the following conditions
11 * are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 *
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * 3. Neither the name of CESNET nor the names of its contributors may be
21 * used to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
26 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
27 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
28 * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
34 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
35 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 */
37
38#ifdef HAVE_CONFIG_H
39#include "config.h"
40#include "config_unix.h"
41#include "config_win32.h"
42#endif /* HAVE_CONFIG_H */
43
44#include <iostream>
45#include <stdexcept>
46#include <thread>
47#include <vector>
48
49#include "capture_filter.h"
50#include "debug.h"
51#include "lib_common.h"
52#include "utils/color_out.h"
53#include "utils/worker.h"
54#include "video.h"
55#include "video_codec.h"
57
58constexpr const char *MOD_NAME = "[gamma cap. f.] ";
59
60using std::cout;
61using std::exception;
62using std::numeric_limits;
63using std::vector;
64using std::thread;
65
67public:
70
72 for (int i = 0; i <= numeric_limits<uint8_t>::max(); ++i) { // 8->8
73 lut8.push_back(pow(static_cast<double>(i)
74 / numeric_limits<uint8_t>::max(), gamma)
75 * numeric_limits<uint8_t>::max());
76 }
77 for (int i = 0; i <= numeric_limits<uint16_t>::max(); ++i) { // 8->16
78 lut16.push_back(pow(static_cast<double>(i)
79 / numeric_limits<uint16_t>::max(), gamma)
80 * numeric_limits<uint16_t>::max());
81 }
82 for (int i = 0; i <= numeric_limits<uint8_t>::max(); ++i) { // 8->16
83 lut8_16.push_back(pow(static_cast<double>(i)
84 / numeric_limits<uint8_t>::max(), gamma)
85 * numeric_limits<uint16_t>::max());
86 }
87 for (int i = 0; i <= numeric_limits<uint16_t>::max(); ++i) { // 16->8
88 lut16_8.push_back(pow(static_cast<double>(i)
89 / numeric_limits<uint16_t>::max(), gamma)
90 * numeric_limits<uint8_t>::max());
91 }
92 }
93
94 void apply_gamma(int in_depth, int out_depth, size_t in_len, void const * __restrict in, void * __restrict out) {
95 if (in_depth == CHAR_BIT && out_depth == CHAR_BIT) {
96 apply_lut<uint8_t, uint8_t>(in_len, lut8, in, out);
97 } else if (in_depth == 2 * CHAR_BIT && out_depth == 2 * CHAR_BIT) {
98 apply_lut<uint16_t, uint16_t>(in_len, lut16, in, out);
99 } else if (in_depth == CHAR_BIT && out_depth == 2 * CHAR_BIT) {
100 apply_lut<uint8_t, uint16_t>(in_len, lut8_16, in, out);
101 } else if (in_depth == 2 * CHAR_BIT && out_depth == CHAR_BIT) {
102 apply_lut<uint16_t, uint8_t>(in_len, lut16_8, in, out);
103 } else {
104 throw exception();
105 }
106 }
107
108private:
109 template<typename inT, typename outT>
110 struct data {
111 size_t len;
112 const vector<outT> &lut;
113 const inT *in;
114 outT *out;
115 };
116
117 template<typename inT, typename outT>
118 static void *compute(void *arg) {
119 auto *d = static_cast<struct data<inT, outT> *>(arg);
120 for (size_t i = 0; i < d->len; ++i) {
121 d->out[i] = d->lut[d->in[i]];
122 }
123 return nullptr;
124 }
125
126 template<typename inT, typename outT> void apply_lut(size_t in_len, const vector<outT> &lut, void const *in, void *out)
127 {
128 auto *in_data = static_cast<const inT*>(in);
129 auto *out_data = static_cast<outT*>(out);
130 unsigned int cpus = thread::hardware_concurrency();
131 in_len /= sizeof(inT);
132 vector<data<inT, outT>> d;
133 vector<task_result_handle_t> handles(cpus);
134 for (unsigned int i = 0; i < cpus; i++) {
135 d.push_back({in_len / cpus, lut, in_data + i * (in_len / cpus), out_data + i * (in_len / cpus)});
136 }
137 for (unsigned int i = 0; i < cpus; i++) {
138 handles[i] = task_run_async(state_capture_filter_gamma::compute<inT, outT>, static_cast<void *>(&(d[i])));
139 }
140 for (unsigned int i = 0; i < cpus; i++) {
141 wait_task(handles[i]);
142 }
143 }
144
145 vector<uint8_t> lut8;
146 vector<uint16_t> lut16;
147 vector<uint8_t> lut16_8;
148 vector<uint16_t> lut8_16;
149};
150
151static auto init(struct module *parent, const char *cfg, void **state)
152{
153 UNUSED(parent);
154
155 if (strlen(cfg) == 0 || strcmp(cfg, "help") == 0) {
156 col() << "Performs gamma transformation.\n\n"
157 "usage:\n";
158 col() << "\t" << SBOLD("-F gamma:value[:8|:16]") << "\n";
159 col() << "where:\n";
160 col() << SBOLD("8|16")
161 << " - force output to 8 (16) bits regardless the input\n";
162 return 1;
163 }
164 char *endptr = nullptr;
165 errno = 0;
166 double gamma = strtod(cfg, &endptr);
167
168 if (gamma <= 0.0 || errno != 0 || (*endptr != '\0' && *endptr != ':')) {
169 LOG(LOG_LEVEL_WARNING) << MOD_NAME << "Using gamma value " << gamma << "\n";
170 }
171
172 long int bits = 0;
173 if (*endptr != '\0') {
174 endptr += 1;
175 errno = 0;
176 bits = strtol(endptr, &endptr, 0);
177 if ((bits != 8 && bits != 16) || *endptr != '\0') {
178 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Wrong number of bits (only 8 or 16)!\n";
179 return -1;
180 }
181 }
182
183 auto *s = new state_capture_filter_gamma(gamma, bits);
184
185 *state = s;
186 return 0;
187}
188
189static void done(void *state)
190{
191 delete static_cast<state_capture_filter_gamma *>(state);
192}
193
194static auto filter(void *state, struct video_frame *in) -> video_frame *
195{
196 if (in->color_spec != RGB && in->color_spec != RG48) {
197 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Unable to apply lut on: " << get_codec_name(in->color_spec) << "\n";
199 return nullptr;
200 }
201
202 auto *s = static_cast<state_capture_filter_gamma *>(state);
203 struct video_desc out_desc = video_desc_from_frame(in);
204 if (s->out_depth != 0) {
205 out_desc.color_spec = s->out_depth == 8 ? RGB : RG48;
206 }
207 struct video_frame *out = vf_alloc_desc(out_desc);
208 if (s->vo_pp_out_buffer != nullptr) {
209 out->tiles[0].data = (char *) s->vo_pp_out_buffer;
210 } else {
211 out->tiles[0].data = (char *) malloc(out->tiles[0].data_len);
213 }
215
216 try {
217 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);
218 } catch(...) {
219 LOG(LOG_LEVEL_ERROR) << MOD_NAME << "Only 8-bit and 16-bit codecs are currently supported!\n";
220 vf_free(out);
221 out = nullptr;
222 }
223
225
226 return out;
227}
228
229static void vo_pp_set_out_buffer(void *state, char *buffer)
230{
231 auto *s = (state_capture_filter_gamma *) state;
232 s->vo_pp_out_buffer = buffer;
233}
234
236 .init = init,
237 .done = done,
238 .filter = filter,
239};
240
242// coverity[leaked_storage:SUPPRESS]
244
245/* vim: set expandtab sw=8: */
#define UNUSED(x)
void ** state
void vf_free(struct video_frame *buf)
Frees video_frame structure.
struct video_desc video_desc_from_frame(const struct video_frame *frame)
Returns struct video_desc from video frame.
void vf_data_deleter(struct video_frame *buf)
struct video_frame * vf_alloc_desc(struct video_desc desc)
Allocates video frame accordig given desc.
const char * get_codec_name(codec_t codec)
#define CAPTURE_FILTER_ABI_VERSION
#define ADD_VO_PP_CAPTURE_FILTER_WRAPPER(name, init, filter, done, set_out_buf, get_prop_callb)
#define SBOLD(x)
Definition color_out.h:80
#define LOG_LEVEL_ERROR
general errors
Definition debug.h:59
#define LOG(level)
Definition debug.h:313
#define LOG_LEVEL_WARNING
less severe errors
Definition debug.h:60
static auto filter(void *state, struct video_frame *in) -> video_frame *
Definition gamma.cpp:194
static void vo_pp_set_out_buffer(void *state, char *buffer)
Definition gamma.cpp:229
static const struct capture_filter_info capture_filter_gamma
Definition gamma.cpp:235
constexpr const char * MOD_NAME
Definition gamma.cpp:58
static void done(void *state)
Definition gamma.cpp:189
static auto init(struct module *parent, const char *cfg, void **state)
Definition gamma.cpp:151
@ LIBRARY_CLASS_CAPTURE_FILTER
Definition lib_common.h:75
#define REGISTER_MODULE(name, info, lclass, abi)
Registers module to global modules' registry.
Definition lib_common.h:144
const char * parent
Definition settings.cpp:198
const vector< outT > & lut
Definition gamma.cpp:112
vector< uint8_t > lut8
Definition gamma.cpp:145
void apply_gamma(int in_depth, int out_depth, size_t in_len, void const *__restrict in, void *__restrict out)
Definition gamma.cpp:94
static void * compute(void *arg)
Definition gamma.cpp:118
state_capture_filter_gamma(double gamma, int out_depth)
Definition gamma.cpp:71
int out_depth
0, 8 or 16 (0 menas keep)
Definition gamma.cpp:68
void * vo_pp_out_buffer
buffer to write to if we use vo_pp wrapper (otherwise unused)
Definition gamma.cpp:69
vector< uint16_t > lut16
Definition gamma.cpp:146
vector< uint8_t > lut16_8
Definition gamma.cpp:147
vector< uint16_t > lut8_16
Definition gamma.cpp:148
void apply_lut(size_t in_len, const vector< outT > &lut, void const *in, void *out)
Definition gamma.cpp:126
char * data
Raw tile data. Pointer must be at least 4B aligned.
Definition types.h:287
unsigned int data_len
length of the data
Definition types.h:288
codec_t color_spec
Definition types.h:182
void(* data_deleter)(struct video_frame *)
Definition types.h:256
void(* dispose)(struct video_frame *)
Definition types.h:245
Struct video_frame represents a video frame and contains video description.
Definition types.h:305
struct tile tiles[FLEXIBLE_ARRAY_MEMBER]
Definition types.h:345
struct video_frame_callbacks callbacks
Definition types.h:324
@ RGB
RGB 8-bit (packed into 24-bit word)
Definition types.h:80
@ RG48
16-bit RGB little-endian (RlRhGl...)
Definition types.h:95
This is an umbrella header for video functions.
int get_bits_per_component(codec_t codec)
#define VIDEO_FRAME_DISPOSE(frame)
Definition video_frame.h:76
void * wait_task(task_result_handle_t handle)
Definition worker.cpp:280
task_result_handle_t task_run_async(runnable_t task, void *data)
Runs task asynchronously.
Definition worker.cpp:262