16 #include <QRegularExpression>
20 void ColorMap::load_cube_file()
22 if (lut_path.empty()) {
25 needs_refresh =
false;
30 std::vector<float> parsed_data;
32 #pragma omp critical(load_lut)
34 QFile file(QString::fromStdString(lut_path));
35 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
38 QTextStream in(&file);
40 QRegularExpression ws_re(
"\\s+");
44 line = in.readLine().trimmed();
45 if (line.startsWith(
"LUT_3D_SIZE")) {
46 auto parts = line.split(ws_re);
47 if (parts.size() >= 2) {
48 parsed_size = parts[1].toInt();
55 if (parsed_size > 0) {
56 int total = parsed_size * parsed_size * parsed_size;
57 parsed_data.reserve(
size_t(total * 3));
58 while (!in.atEnd() &&
int(parsed_data.size()) < total * 3) {
59 line = in.readLine().trimmed();
61 line.startsWith(
"#") ||
62 line.startsWith(
"TITLE") ||
63 line.startsWith(
"DOMAIN"))
67 auto vals = line.split(ws_re);
68 if (vals.size() >= 3) {
70 parsed_data.push_back(vals[0].toFloat());
71 parsed_data.push_back(vals[1].toFloat());
72 parsed_data.push_back(vals[2].toFloat());
75 if (
int(parsed_data.size()) != total * 3) {
83 if (parsed_size > 0) {
84 lut_size = parsed_size;
85 lut_data.swap(parsed_data);
90 needs_refresh =
false;
93 void ColorMap::init_effect_details()
98 info.
description =
"Adjust colors using 3D LUT lookup tables (.cube format)";
104 : lut_path(
""), lut_size(0), needs_refresh(true),
105 intensity(1.0), intensity_r(1.0), intensity_g(1.0), intensity_b(1.0)
107 init_effect_details();
124 init_effect_details();
128 std::shared_ptr<openshot::Frame>
134 needs_refresh =
false;
137 if (lut_data.empty())
140 auto image = frame->GetImage();
141 int w = image->width(), h = image->height();
142 unsigned char *pixels = image->bits();
149 int pixel_count = w * h;
150 #pragma omp parallel for
151 for (
int i = 0; i < pixel_count; ++i) {
153 int A = pixels[idx + 3];
154 float alpha = A / 255.0f;
155 if (alpha == 0.0f)
continue;
158 float R = pixels[idx + 0] / alpha;
159 float G = pixels[idx + 1] / alpha;
160 float B = pixels[idx + 2] / alpha;
163 float Rn = R * (1.0f / 255.0f);
164 float Gn = G * (1.0f / 255.0f);
165 float Bn = B * (1.0f / 255.0f);
168 float rf = Rn * (lut_size - 1);
169 float gf = Gn * (lut_size - 1);
170 float bf = Bn * (lut_size - 1);
172 int r0 = int(floor(rf)), r1 = std::min(r0 + 1, lut_size - 1);
173 int g0 = int(floor(gf)), g1 = std::min(g0 + 1, lut_size - 1);
174 int b0 = int(floor(bf)), b1 = std::min(b0 + 1, lut_size - 1);
181 int base000 = ((b0 * lut_size + g0) * lut_size + r0) * 3;
182 int base100 = ((b0 * lut_size + g0) * lut_size + r1) * 3;
183 int base010 = ((b0 * lut_size + g1) * lut_size + r0) * 3;
184 int base110 = ((b0 * lut_size + g1) * lut_size + r1) * 3;
185 int base001 = ((b1 * lut_size + g0) * lut_size + r0) * 3;
186 int base101 = ((b1 * lut_size + g0) * lut_size + r1) * 3;
187 int base011 = ((b1 * lut_size + g1) * lut_size + r0) * 3;
188 int base111 = ((b1 * lut_size + g1) * lut_size + r1) * 3;
192 float c00 = lut_data[base000 + 0] * (1 - dr) + lut_data[base100 + 0] * dr;
193 float c01 = lut_data[base001 + 0] * (1 - dr) + lut_data[base101 + 0] * dr;
194 float c10 = lut_data[base010 + 0] * (1 - dr) + lut_data[base110 + 0] * dr;
195 float c11 = lut_data[base011 + 0] * (1 - dr) + lut_data[base111 + 0] * dr;
196 float c0 = c00 * (1 - dg) + c10 * dg;
197 float c1 = c01 * (1 - dg) + c11 * dg;
198 float lr = c0 * (1 - db) + c1 * db;
201 c00 = lut_data[base000 + 1] * (1 - dr) + lut_data[base100 + 1] * dr;
202 c01 = lut_data[base001 + 1] * (1 - dr) + lut_data[base101 + 1] * dr;
203 c10 = lut_data[base010 + 1] * (1 - dr) + lut_data[base110 + 1] * dr;
204 c11 = lut_data[base011 + 1] * (1 - dr) + lut_data[base111 + 1] * dr;
205 c0 = c00 * (1 - dg) + c10 * dg;
206 c1 = c01 * (1 - dg) + c11 * dg;
207 float lg = c0 * (1 - db) + c1 * db;
210 c00 = lut_data[base000 + 2] * (1 - dr) + lut_data[base100 + 2] * dr;
211 c01 = lut_data[base001 + 2] * (1 - dr) + lut_data[base101 + 2] * dr;
212 c10 = lut_data[base010 + 2] * (1 - dr) + lut_data[base110 + 2] * dr;
213 c11 = lut_data[base011 + 2] * (1 - dr) + lut_data[base111 + 2] * dr;
214 c0 = c00 * (1 - dg) + c10 * dg;
215 c1 = c01 * (1 - dg) + c11 * dg;
216 float lb = c0 * (1 - db) + c1 * db;
219 float outR = (lr * tR + Rn * (1 - tR)) * alpha;
220 float outG = (lg * tG + Gn * (1 - tG)) * alpha;
221 float outB = (lb * tB + Bn * (1 - tB)) * alpha;
223 pixels[idx + 0] =
constrain(outR * 255.0f);
224 pixels[idx + 1] =
constrain(outG * 255.0f);
225 pixels[idx + 2] =
constrain(outB * 255.0f);
242 root[
"lut_path"] = lut_path;
257 throw InvalidJSON(
"Invalid JSON for ColorMap effect");
264 if (!root[
"lut_path"].isNull())
266 lut_path = root[
"lut_path"].asString();
267 needs_refresh =
true;
269 if (!root[
"intensity"].isNull())
271 if (!root[
"intensity_r"].isNull())
273 if (!root[
"intensity_g"].isNull())
275 if (!root[
"intensity_b"].isNull())
284 "LUT File", 0.0,
"string", lut_path,
nullptr, 0, 0,
false, requested_frame);
289 "float",
"", &
intensity, 0.0, 1.0,
false, requested_frame);
294 "float",
"", &
intensity_r, 0.0, 1.0,
false, requested_frame);
299 "float",
"", &
intensity_g, 0.0, 1.0,
false, requested_frame);
304 "float",
"", &
intensity_b, 0.0, 1.0,
false, requested_frame);
306 return root.toStyledString();