Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(288)

Side by Side Diff: media/base/video_color_space.cc

Issue 2088273003: Video Color Managament (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: bugfix Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/base/video_color_space.h"
6
7 #include "media/base/video_frame.h"
8 #include "media/base/video_frame_metadata.h"
9
10 namespace media {
11
12 VideoColorSpace::VideoColorSpace()
13 : primaries(PRI_UNSPECIFIED),
14 transfer(TRC_UNSPECIFIED),
15 matrix(SPC_UNSPECIFIED),
16 full_range(false) {}
17
18 VideoColorSpace::VideoColorSpace(PrimaryID primaries_,
19 TransferID transfer_,
20 MatrixID matrix_,
21 bool full_range_)
22 : primaries(primaries_),
23 transfer(transfer_),
24 matrix(matrix_),
25 full_range(full_range_) {}
26
27 VideoColorSpace::VideoColorSpace(const scoped_refptr<VideoFrame>& frame) {
28 int primaries_tmp;
29 int transfer_tmp;
30 int matrix_tmp;
31 bool full_range_tmp;
32 if (frame->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE_PRIMARIES,
33 &primaries_tmp) &&
34 frame->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE_TRANSFER,
35 &transfer_tmp) &&
36 frame->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE_MATRIX,
37 &matrix_tmp) &&
38 frame->metadata()->GetBoolean(VideoFrameMetadata::COLOR_SPACE_FULL_RANGE,
39 &full_range_tmp)) {
40 primaries = static_cast<PrimaryID>(primaries_tmp);
41 transfer = static_cast<TransferID>(transfer_tmp);
42 matrix = static_cast<MatrixID>(matrix_tmp);
43 full_range = full_range_tmp;
44 }
45 int color_space_tmp;
46 if (frame->metadata()->GetInteger(VideoFrameMetadata::COLOR_SPACE,
47 &color_space_tmp)) {
48 VideoColorSpace tmp(static_cast<ColorSpace>(color_space_tmp));
49 *this = tmp;
50 return;
51 }
52 primaries = PRI_UNSPECIFIED;
53 transfer = TRC_UNSPECIFIED;
54 matrix = SPC_UNSPECIFIED;
55 full_range = false;
56 }
57
58 VideoColorSpace::VideoColorSpace(ColorSpace color_space) {
59 switch (color_space) {
60 case COLOR_SPACE_UNSPECIFIED:
61 primaries = PRI_UNSPECIFIED;
62 transfer = TRC_UNSPECIFIED;
63 matrix = SPC_UNSPECIFIED;
64 full_range = false;
65 break;
66
67 case COLOR_SPACE_JPEG:
68 // TODO(hubbe): Check that these are right.
69 primaries = PRI_BT709;
70 transfer = TRC_IEC61966_2_1;
71 matrix = SPC_BT709;
72 full_range = true;
73 break;
74
75 case COLOR_SPACE_HD_REC709:
76 primaries = PRI_BT709;
77 transfer = TRC_BT709;
78 matrix = SPC_BT709;
79 full_range = false;
80 break;
81
82 case COLOR_SPACE_SD_REC601:
83 primaries = PRI_SMPTE170M;
84 transfer = TRC_SMPTE170M;
85 matrix = SPC_SMPTE170M;
86 full_range = false;
87 break;
88 }
89 }
90
91 void VideoColorSpace::SetVideoFrameColorSpace(
92 const scoped_refptr<VideoFrame>& frame) {
93 frame->metadata()->SetInteger(VideoFrameMetadata::COLOR_SPACE_PRIMARIES,
94 primaries);
95 frame->metadata()->SetInteger(VideoFrameMetadata::COLOR_SPACE_TRANSFER,
96 transfer);
97 frame->metadata()->SetInteger(VideoFrameMetadata::COLOR_SPACE_MATRIX, matrix);
98 frame->metadata()->SetBoolean(VideoFrameMetadata::COLOR_SPACE_FULL_RANGE,
99 full_range);
100 }
101
102 bool VideoColorSpace::operator==(const VideoColorSpace& other) const {
103 return primaries == other.primaries && transfer == other.transfer &&
104 matrix == other.matrix && full_range == other.full_range;
105 }
106
107 bool VideoColorSpace::operator<(const VideoColorSpace& other) const {
108 if (primaries < other.primaries)
109 return true;
110 if (primaries > other.primaries)
111 return false;
112 if (transfer < other.transfer)
113 return true;
114 if (transfer > other.transfer)
115 return false;
116 if (matrix < other.matrix)
117 return true;
118 if (matrix > other.matrix)
119 return false;
120 return full_range < other.full_range;
121 }
122
123 VideoColorSpace VideoColorSpace::XYZ() {
124 VideoColorSpace ret;
125 ret.primaries = PRI_XYZ_D50;
126 ret.transfer = TRC_LINEAR;
127 ret.matrix = SPC_RGB;
128 ret.full_range = true;
129 return ret;
130 }
131
132 VideoColorSpace VideoColorSpace::SRGB() {
133 VideoColorSpace ret;
134 ret.primaries = PRI_BT709;
135 ret.transfer = TRC_IEC61966_2_1;
136 ret.matrix = SPC_RGB;
137 ret.full_range = true;
138 return ret;
139 }
140
141 std::vector<VideoColorSpace::XY> VideoColorSpace::GetPrimaries(PrimaryID id) {
142 std::vector<XY> ret(4);
143 switch (id) {
144 default:
145 // If we don't know, assume BT709
146
147 case PRI_BT709:
148 // Red
149 ret[0].first = 0.640;
150 ret[0].second = 0.330;
151 // Green
152 ret[1].first = 0.300;
153 ret[1].second = 0.600;
154 // Blue
155 ret[2].first = 0.150;
156 ret[2].second = 0.060;
157 // Whitepoint (D65)
158 ret[3].first = 0.3127;
159 ret[3].second = 0.3290;
160 break;
161
162 case PRI_BT470M:
163 // Red
164 ret[0].first = 0.67;
165 ret[0].second = 0.33;
166 // Green
167 ret[1].first = 0.21;
168 ret[1].second = 0.71;
169 // Blue
170 ret[2].first = 0.14;
171 ret[2].second = 0.08;
172 // Whitepoint
173 ret[3].first = 0.31;
174 ret[3].second = 0.316;
175 break;
176
177 case PRI_BT470BG:
178 // Red
179 ret[0].first = 0.64;
180 ret[0].second = 0.33;
181 // Green
182 ret[1].first = 0.29;
183 ret[1].second = 0.60;
184 // Blue
185 ret[2].first = 0.15;
186 ret[2].second = 0.06;
187 // Whitepoint (D65)
188 ret[3].first = 0.3127;
189 ret[3].second = 0.3290;
190 break;
191
192 case PRI_SMPTE170M:
193 case PRI_SMPTE240M:
194 // Red
195 ret[0].first = 0.630;
196 ret[0].second = 0.340;
197 // Green
198 ret[1].first = 0.310;
199 ret[1].second = 0.595;
200 // Blue
201 ret[2].first = 0.155;
202 ret[2].second = 0.070;
203 // Whitepoint (D65)
204 ret[3].first = 0.3127;
205 ret[3].second = 0.3290;
206 break;
207
208 case PRI_FILM:
209 // Red
210 ret[0].first = 0.681;
211 ret[0].second = 0.319;
212 // Green
213 ret[1].first = 0.243;
214 ret[1].second = 0.692;
215 // Blue
216 ret[2].first = 0.145;
217 ret[2].second = 0.049;
218 // Whitepoint (C)
219 ret[3].first = 0.310;
220 ret[3].second = 0.136;
221 break;
222
223 case PRI_BT2020:
224 // Red
225 ret[0].first = 0.708;
226 ret[0].second = 0.292;
227 // Green
228 ret[1].first = 0.170;
229 ret[1].second = 0.797;
230 // Blue
231 ret[2].first = 0.131;
232 ret[2].second = 0.046;
233 // Whitepoint (D65)
234 ret[3].first = 0.3127;
235 ret[3].second = 0.3290;
236 break;
237
238 case PRI_SMPTEST428_1:
239 // X
240 ret[0].first = 1.0;
241 ret[0].second = 0.0;
242 // Y
243 ret[1].first = 0.0;
244 ret[1].second = 1.0;
245 // Z
246 ret[2].first = 0.0;
247 ret[2].second = 0.0;
248 // Whitepoint (E)
249 ret[3].first = 1.0f / 3.0f;
250 ret[3].second = 1.0f / 3.0f;
251 break;
252
253 case PRI_SMPTEST431_2:
254 // Red
255 ret[0].first = 0.680;
256 ret[0].second = 0.320;
257 // Green
258 ret[1].first = 0.265;
259 ret[1].second = 0.690;
260 // Blue
261 ret[2].first = 0.150;
262 ret[2].second = 0.060;
263 // Whitepoint
264 ret[3].first = 0.314;
265 ret[3].second = 0.351;
266 break;
267
268 case PRI_SMPTEST432_1:
269 // Red
270 ret[0].first = 0.680;
271 ret[0].second = 0.320;
272 // Green
273 ret[1].first = 0.265;
274 ret[1].second = 0.690;
275 // Blue
276 ret[2].first = 0.150;
277 ret[2].second = 0.060;
278 // Whitepoint (D65)
279 ret[3].first = 0.3127;
280 ret[3].second = 0.3290;
281 break;
282
283 case PRI_XYZ_D50:
284 // X
285 ret[0].first = 1.0;
286 ret[0].second = 0.0;
287 // Y
288 ret[1].first = 0.0;
289 ret[1].second = 1.0;
290 // Z
291 ret[2].first = 0.0;
292 ret[2].second = 0.0;
293 // D50
294 ret[3].first = 0.34567;
295 ret[3].second = 0.35850;
296 break;
297 }
298 return ret;
299 }
300
301 float VideoColorSpace::fromLinear(TransferID id, float v) {
302 switch (id) {
303 default:
304 case TRC_BT709:
305 case TRC_SMPTE170M:
306 case TRC_BT2020_10:
307 case TRC_BT2020_12: {
308 v = fmax(0.0f, v);
309 float a = 1.099296826809442f;
310 float b = 0.018053968510807;
311 if (v <= b) {
312 return 4.5f * v;
313 } else {
314 return a * pow(v, 0.45f) - (a - 1.0f);
315 }
316 }
317
318 case TRC_GAMMA22:
319 v = fmax(0.0f, v);
320 return pow(v, 1.0f / 2.2f);
321
322 case TRC_GAMMA28:
323 v = fmax(0.0f, v);
324 return pow(v, 1.0f / 2.8f);
325
326 case TRC_SMPTE240M: {
327 v = fmax(0.0f, v);
328 float a = 1.11157219592173128753f;
329 float b = 0.02282158552944503135f;
330 if (v <= b) {
331 return 4.0f * v;
332 } else {
333 return a * pow(v, 0.45f) - (a - 1.0f);
334 }
335 }
336
337 case TRC_LINEAR:
338 return v;
339
340 case TRC_LOG:
341 if (v < 0.01)
342 return 0.0;
343 return 1.0 + log(v) / log(10.0f) / 2.0f;
344
345 case TRC_LOG_SQRT:
346 if (v < sqrt(10.0f) / 1000.0)
347 return 0.0;
348 return 1.0 + log(v) / log(10.0f) / 2.5f;
349
350 case TRC_IEC61966_2_4: {
351 float a = 1.099296826809442f;
352 float b = 0.018053968510807f;
353 if (v < -b) {
354 return -a * pow(-v, 0.45) + (a - 1.0f);
355 } else if (v <= b) {
356 return 4.5 * v;
357 } else {
358 return a * pow(v, 0.45) - (a - 1.0f);
359 }
360 }
361
362 case TRC_BT1361_ECG: {
363 float a = 1.099;
364 float b = 0.018;
365 float l = 0.0045;
366 if (v < -l) {
367 return -(a * pow(-4.0f * v, 0.45) + (a - 1.0f)) / 4.0f;
368 } else if (v <= b) {
369 return 4.5 * v;
370 } else {
371 return a * pow(v, 0.45) - (a - 1.0f);
372 }
373 }
374
375 case TRC_IEC61966_2_1: { // SRGB
376 v = fmax(0.0f, v);
377 float a = 1.055f;
378 float b = 0.0031308f;
379 if (v < b) {
380 return 12.92f * v;
381 } else {
382 return a * pow(v, 1.0f / 2.4f) - (a - 1.0f);
383 }
384 }
385 case TRC_SMPTEST2084: {
386 v = fmax(0.0f, v);
387 float m1 = (2610.0 / 4096.0) / 4.0;
388 float m2 = (2523.0 / 4096.0) * 128.0;
389 float c1 = 3424.0 / 4096.0;
390 float c2 = (2413.0 / 4096.0) * 32.0;
391 float c3 = (2392.0 / 4096.0) * 32.0;
392 return pow((c1 + c2 * pow(v, m1)) / (1.0f + c3 * pow(v, m1)), m2);
393 }
394
395 case TRC_SMPTEST428_1:
396 v = fmax(0.0f, v);
397 return pow(48.0f * v + 52.37f, 1.0f / 2.6f);
398
399 // Chrome-specific values below
400 case TRC_GAMMA24:
401 v = fmax(0.0f, v);
402 return pow(v, 1.0f / 2.4f);
403 }
404 }
405
406 float VideoColorSpace::toLinear(TransferID id, float v) {
407 switch (id) {
408 default:
409 case TRC_BT709:
410 case TRC_SMPTE170M:
411 case TRC_BT2020_10:
412 case TRC_BT2020_12: {
413 v = fmax(0.0f, v);
414 float a = 1.099296826809442f;
415 float b = 0.018053968510807;
416 if (v < fromLinear(TRC_BT709, b)) {
417 return v / 4.5f;
418 } else {
419 return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
420 }
421 }
422
423 case TRC_GAMMA22:
424 v = fmax(0.0f, v);
425 return pow(v, 2.2f);
426
427 case TRC_GAMMA28:
428 v = fmax(0.0f, v);
429 return pow(v, 2.8f);
430
431 case TRC_SMPTE240M: {
432 v = fmax(0.0f, v);
433 float a = 1.11157219592173128753f;
434 float b = 0.02282158552944503135f;
435 if (v <= fromLinear(TRC_SMPTE240M, b)) {
436 return v / 4.0f;
437 } else {
438 return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
439 }
440 }
441
442 case TRC_LINEAR:
443 return v;
444
445 case TRC_LOG:
446 if (v < 0.0)
447 return 0.0;
448 return pow(10.0, (v - 1.0f) * 2.0f);
449
450 case TRC_LOG_SQRT:
451 if (v < 0.0)
452 return 0.0;
453 return pow(10.0, (v - 1.0f) * 2.5f);
454
455 case TRC_IEC61966_2_4: {
456 float a = 1.099296826809442f;
457 float b = 0.018053968510807f;
458 if (v < fromLinear(TRC_IEC61966_2_4, -a)) {
459 return -pow((a - 1.0f - v) / a, 1.0f / 0.45f);
460 } else if (v <= fromLinear(TRC_IEC61966_2_4, b)) {
461 return v / 4.5f;
462 } else {
463 return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
464 }
465 }
466
467 case TRC_BT1361_ECG: {
468 float a = 1.099;
469 float b = 0.018;
470 float l = 0.0045;
471 if (v < fromLinear(TRC_BT1361_ECG, -l)) {
472 return -pow((1.0f - a - v * 4.0) / a, 1.0f / 0.45f) / 4.0f;
473 } else if (v <= fromLinear(TRC_BT1361_ECG, b)) {
474 return v / 4.5f;
475 } else {
476 return pow((v + a - 1.0f) / a, 1.0f / 0.45f);
477 }
478 }
479
480 case TRC_IEC61966_2_1: { // SRGB
481 v = fmax(0.0f, v);
482 float a = 1.055f;
483 float b = 0.0031308f;
484 if (v < fromLinear(TRC_IEC61966_2_1, b)) {
485 return v / 12.92f;
486 } else {
487 return pow((v + a - 1.0f) / a, 2.4f);
488 }
489 }
490
491 case TRC_SMPTEST2084: {
492 v = fmax(0.0f, v);
493 float m1 = (2610.0f / 4096.0f) / 4.0f;
494 float m2 = (2523.0f / 4096.0f) * 128.0f;
495 float c1 = 3424.0f / 4096.0f;
496 float c2 = (2413.0f / 4096.0f) * 32.0f;
497 float c3 = (2392.0f / 4096.0f) * 32.0f;
498 return pow(fmax(pow(v, 1.0 / m2) - c1, 0) / (c2 - c3 * pow(v, 1.0 / m2)),
499 1.0f / m1);
500 }
501
502 case TRC_SMPTEST428_1:
503 return (pow(v, 2.6f) - 52.37f) / 48.0f;
504
505 // Chrome-specific values below
506 case TRC_GAMMA24:
507 v = fmax(0.0f, v);
508 return pow(v, 2.4f);
509 }
510 }
511
512 VideoColorSpace::Matrix4x4 VideoColorSpace::GetTransferMatrix(MatrixID id) {
513 Matrix4x4 ret;
514 for (int i = 0; i < 4; i++)
515 for (int j = 0; j < 4; j++)
516 ret.rows[i][j] = 0.0f;
517
518 float Kr, Kb;
519 switch (id) {
520 case SPC_RGB:
521 ret.rows[0][0] = 1.0;
522 ret.rows[1][1] = 1.0;
523 ret.rows[2][2] = 1.0;
524 ret.rows[3][3] = 1.0;
525 return ret;
526
527 case SPC_BT709:
528 case SPC_UNSPECIFIED:
529 case SPC_RESERVED:
530 Kr = 0.2126f;
531 Kb = 0.0722f;
532 break;
533
534 case SPC_FCC:
535 Kr = 0.30f;
536 Kb = 0.11f;
537 break;
538
539 case SPC_BT470BG:
540 case SPC_SMPTE170M:
541 Kr = 0.299f;
542 Kb = 0.144f;
543 break;
544
545 case SPC_SMPTE240M:
546 Kr = 0.212f;
547 Kb = 0.087f;
548 break;
549
550 case SPC_YCOCG:
551 ret.rows[0][0] = 0.25f;
552 ret.rows[0][1] = 0.5f;
553 ret.rows[0][2] = 0.25f;
554 ret.rows[0][3] = 0.5;
555 ret.rows[1][0] = -0.25f;
556 ret.rows[1][1] = 0.5f;
557 ret.rows[1][2] = -0.25f;
558 ret.rows[1][3] = 0.5;
559 ret.rows[2][0] = 0.5f;
560 ret.rows[2][1] = 0.0f;
561 ret.rows[2][2] = -0.5f;
562 ret.rows[3][3] = 1.0f;
563 return ret;
564
565 // TODO(hubbe): Check if the CL equation is right.
566 case SPC_BT2020_NCL:
567 case SPC_BT2020_CL:
568 Kr = 0.2627f;
569 Kb = 0.0593f;
570 break;
571
572 case SPC_YDZDX:
573 ret.rows[0][1] = 1.0f;
574 ret.rows[1][1] = -0.5f;
575 ret.rows[1][2] = 0.986566f / 2.0f;
576 ret.rows[1][3] = 0.5f;
577 ret.rows[2][0] = 0.5f;
578 ret.rows[2][1] = -0.991902f / 2.0f;
579 ret.rows[2][3] = 0.5f;
580 ret.rows[3][3] = 1.0f;
581 return ret;
582 }
583 ret.rows[0][0] = Kr;
584 ret.rows[0][1] = 1.0f - Kr - Kb;
585 ret.rows[0][2] = Kb;
586
587 float u_mult = 0.5f / (1.0f - Kb);
588 ret.rows[1][0] = u_mult * -Kr;
589 ret.rows[1][1] = u_mult * -(1.0f - Kr - Kb);
590 ret.rows[1][2] = u_mult * (1.0f - Kb);
591 ret.rows[1][3] = 0.5f;
592
593 float v_mult = 0.5f / (1.0f - Kr);
594 ret.rows[2][0] = v_mult * (1.0f - Kr);
595 ret.rows[2][1] = v_mult * -(1.0f - Kr - Kb);
596 ret.rows[2][2] = v_mult * -Kb;
597 ret.rows[2][3] = 0.5f;
598
599 ret.rows[3][3] = 1.0f;
600 return ret;
601 }
602
603 bool VideoColorSpace::GetRangeAdjust(TriStim* offset,
604 TriStim* multiplier) const {
605 if (full_range) {
606 *offset = TriStim(0.0f, 0.0f, 0.0f);
607 *multiplier = TriStim(1.0f, 1.0f, 1.0f);
608 return false;
609 }
610 switch (matrix) {
611 case SPC_RGB:
612 case SPC_YCOCG:
613 *offset = TriStim(16.0f / 255.0f, 16.0f / 255.0f, 16.0f / 255.0f);
614 *multiplier = TriStim(255.0f / 219.0f, 255.0f / 219.0f, 255.0f / 219.0f);
615 return true;
616
617 default:
618 // Note: offset of 15.5 is needed to make sure that 128 is decoded as 0.5.
619 *offset = TriStim(16.0f / 255.0f, 15.5f / 255.0f, 15.5f / 255.0f);
620 *multiplier = TriStim(255.0f / 219.0f, 255.0f / 224.0f, 255.0f / 224.0f);
621 return true;
622 }
623 }
624
625 VideoColorSpace::Matrix4x4 VideoColorSpace::GetRangeAdjustMatrix() const {
626 TriStim offset, multiplier;
627 GetRangeAdjust(&offset, &multiplier);
628 Matrix4x4 ret;
629 for (int y = 0; y < 3; y++) {
630 for (int x = 0; x < 3; x++) {
631 ret.rows[y][x] = x == y ? multiplier.values[y] : 0.0f;
632 }
633 ret.rows[y][3] = -offset.values[y] * multiplier.values[y];
634 ret.rows[3][y] = 0.0f;
635 }
636 ret.rows[3][3] = 1.0f;
637 return ret;
638 }
639
640 VideoColorSpace::Matrix4x4 VideoColorSpace::Multiply(const Matrix4x4& a,
641 const Matrix4x4& b) {
642 Matrix4x4 ret;
643 for (int x = 0; x < 4; x++) {
644 for (int y = 0; y < 4; y++) {
645 float sum = 0.0;
646 for (int z = 0; z < 4; z++) {
647 sum += a.rows[z][x] * b.rows[y][z];
648 }
649 ret.rows[y][x] = sum;
650 }
651 }
652 return ret;
653 }
654
655 VideoColorSpace::TriStim VideoColorSpace::Multiply(const Matrix4x4& a,
656 const TriStim& b) {
657 TriStim ret;
658 ret.values[0] = a.rows[0][0] * b.values[0] + a.rows[0][1] * b.values[1] +
659 a.rows[0][2] * b.values[2] + a.rows[0][3];
660 ret.values[1] = a.rows[1][0] * b.values[0] + a.rows[1][1] * b.values[1] +
661 a.rows[1][2] * b.values[2] + a.rows[1][3];
662 ret.values[2] = a.rows[2][0] * b.values[0] + a.rows[2][1] * b.values[1] +
663 a.rows[2][2] * b.values[2] + a.rows[2][3];
664 return ret;
665 }
666
667 // Simple gaussian elimination.
668 VideoColorSpace::Matrix4x4 VideoColorSpace::Invert(Matrix4x4 m) {
669 Matrix4x4 ret;
670 for (int y = 0; y < 4; y++) {
671 for (int x = 0; x < 4; x++) {
672 ret.rows[y][x] = x == y ? 1.0f : 0.0f;
673 }
674 }
675 for (int x = 0; x < 4; x++) {
676 if (m.rows[x][x] != 1.0) {
677 int best = x;
678 for (int y = x + 1; y < 4; y++) {
679 if (fabs(m.rows[y][x]) > fabs(m.rows[best][x])) {
680 best = y;
681 }
682 }
683 if (best != x) {
684 // Swap rows x, best
685 std::swap(m.rows[x], m.rows[best]);
686 std::swap(ret.rows[x], ret.rows[best]);
687 }
688 if (m.rows[x][x] == 0.0f) {
689 // Failed, shouldn't happen.
690 NOTREACHED();
691 return ret;
692 }
693 float mult = 1.0f / m.rows[x][x];
694 for (int x2 = 0; x2 < 4; x2++) {
695 m.rows[x][x2] *= mult;
696 ret.rows[x][x2] *= mult;
697 }
698 m.rows[x][x] = 1.0f;
699 }
700 for (int y = 0; y < 4; y++) {
701 if (y == x)
702 continue;
703 float mult = m.rows[y][x];
704 if (mult == 0.0f)
705 continue;
706
707 for (int x2 = 0; x2 < 4; x2++) {
708 m.rows[y][x2] -= m.rows[x][x2] * mult;
709 ret.rows[y][x2] -= ret.rows[x][x2] * mult;
710 }
711 m.rows[y][x] = 0.0f;
712 }
713 }
714 return ret;
715 }
716
717 namespace {
718
719 VideoColorSpace::TriStim xy2xyz(const VideoColorSpace::XY& xy) {
720 VideoColorSpace::TriStim ret;
721 ret.values[0] = xy.first;
722 ret.values[1] = xy.second;
723 ret.values[2] = 1.0f - xy.first - xy.second;
724 return ret;
725 }
726
727 } // namespace
728
729 VideoColorSpace::Matrix4x4 VideoColorSpace::GetPrimaryMatrix(
730 const std::vector<XY>& primaries) {
731 TriStim Rxyz = xy2xyz(primaries[0]);
732 TriStim Gxyz = xy2xyz(primaries[1]);
733 TriStim Bxyz = xy2xyz(primaries[2]);
734 TriStim Wxyz = xy2xyz(primaries[3]);
735 TriStim WXYZ;
736
737 WXYZ.values[0] = Wxyz.values[0] / Wxyz.values[1];
738 WXYZ.values[1] = 1.0f;
739 WXYZ.values[2] = Wxyz.values[2] / Wxyz.values[1];
740
741 Matrix4x4 tmp;
742 for (int i = 0; i < 3; i++) {
743 tmp.rows[i][0] = Rxyz.values[i];
744 tmp.rows[i][1] = Gxyz.values[i];
745 tmp.rows[i][2] = Bxyz.values[i];
746 tmp.rows[3][i] = 0.0f;
747 tmp.rows[i][3] = 0.0f;
748 }
749 tmp.rows[3][3] = 1.0;
750 Matrix4x4 tmpinv = Invert(tmp);
751 TriStim conv = Multiply(tmpinv, WXYZ);
752
753 Matrix4x4 ret;
754 for (int y = 0; y < 3; y++) {
755 for (int x = 0; x < 3; x++) {
756 ret.rows[y][x] = conv.values[x] * tmp.rows[y][x];
757 }
758 ret.rows[3][y] = 0.0f;
759 ret.rows[y][3] = 0.0f;
760 }
761 ret.rows[3][3] = 1.0f;
762
763 // Chromatic adaptation.
764 Matrix4x4 bradford;
765 bradford.rows[0][0] = 0.8951000f;
766 bradford.rows[0][1] = 0.2664000f;
767 bradford.rows[0][2] = -0.1614000f;
768 bradford.rows[0][3] = 0.0f;
769
770 bradford.rows[1][0] = -0.7502000f;
771 bradford.rows[1][1] = 1.7135000f;
772 bradford.rows[1][2] = 0.0367000f;
773 bradford.rows[1][3] = 0.0f;
774
775 bradford.rows[2][0] = 0.0389000f;
776 bradford.rows[2][1] = -0.0685000f;
777 bradford.rows[2][2] = 1.0296000f;
778 bradford.rows[2][3] = 0.0f;
779
780 bradford.rows[3][0] = 0.0f;
781 bradford.rows[3][1] = 0.0f;
782 bradford.rows[3][2] = 0.0f;
783 bradford.rows[3][3] = 1.0f;
784
785 Matrix4x4 bradford_inv = Invert(bradford);
786
787 TriStim D50(0.9642, 1.0000, 0.8249);
788 TriStim source_response = Multiply(bradford, WXYZ);
789 TriStim dest_response = Multiply(bradford, D50);
790
791 Matrix4x4 adapter;
792 for (int y = 0; y < 4; y++) {
793 for (int x = 0; x < 4; x++) {
794 adapter.rows[y][x] = 0.0f;
795 }
796 }
797
798 for (int c = 0; c < 3; c++) {
799 adapter.rows[c][c] = dest_response.values[c] / source_response.values[c];
800 }
801 adapter.rows[3][3] = 1.0;
802 ret = Multiply(bradford_inv, ret);
803 ret = Multiply(adapter, ret);
804 ret = Multiply(bradford, ret);
805 return ret;
806 }
807
808 ColorTransform::ColorTransform(const VideoColorSpace& from,
809 const VideoColorSpace& to,
810 Intent intent)
811 : from_(from), to_(to) {
812 if (intent == INTENT_PERCEIVED) {
813 switch (from_.transfer) {
814 case VideoColorSpace::TRC_UNSPECIFIED:
815 case VideoColorSpace::TRC_BT709:
816 case VideoColorSpace::TRC_SMPTE170M:
817 // See SMPTE 1886
818 from_.transfer = VideoColorSpace::TRC_GAMMA24;
819 // from_.transfer = VideoColorSpace::TRC_IEC61966_2_1;
820
821 default: // Do nothing
822 break;
823 }
824
825 // TODO(hubbe): stretch/shrink gamuts here
826 }
827 for (int i = 0; i < 3; i++) {
828 for (int x = 0; x < 4; x++) {
829 for (int y = 0; y < 4; y++) {
830 transforms_[i].rows[y][x] = x == y ? 1.0f : 0.0f;
831 }
832 }
833 }
834 int from_transfer_matrix =
835 from_.matrix == VideoColorSpace::SPC_BT2020_CL ? 1 : 0;
836 int to_transfer_matrix = to_.matrix == VideoColorSpace::SPC_BT2020_CL ? 1 : 2;
837 transforms_[0] = from_.GetRangeAdjustMatrix();
838 transforms_[from_transfer_matrix] = VideoColorSpace::Multiply(
839 transforms_[from_transfer_matrix],
840 VideoColorSpace::Invert(from_.GetTransferMatrix()));
841
842 transforms_[1] =
843 VideoColorSpace::Multiply(transforms_[1], from_.GetPrimaryMatrix());
844 transforms_[1] = VideoColorSpace::Multiply(
845 transforms_[1], VideoColorSpace::Invert(to_.GetPrimaryMatrix()));
846
847 transforms_[to_transfer_matrix] = VideoColorSpace::Multiply(
848 transforms_[to_transfer_matrix], to_.GetTransferMatrix());
849
850 transforms_[2] = VideoColorSpace::Multiply(
851 transforms_[2], VideoColorSpace::Invert(to_.GetRangeAdjustMatrix()));
852
853 for (int i = 0; i < 3; i++) {
854 for (int row = 0; row < 4; row++) {
855 VLOG(2) << i << ":" << transforms_[i].rows[row][0] << ", "
856 << transforms_[i].rows[row][1] << ", "
857 << transforms_[i].rows[row][2] << ", "
858 << transforms_[i].rows[row][3];
859 }
860 }
861 }
862
863 void ColorTransform::transform(VideoColorSpace::TriStim* colors, size_t num) {
864 // Fix input range.
865 for (size_t i = 0; i < num; i++) {
866 VideoColorSpace::TriStim c = colors[i];
867 c = VideoColorSpace::Multiply(transforms_[0], c);
868 for (int j = 0; j < 3; j++) {
869 c.values[j] = from_.toLinear(c.values[j]);
870 }
871 c = VideoColorSpace::Multiply(transforms_[1], c);
872 for (int j = 0; j < 3; j++) {
873 c.values[j] = to_.fromLinear(c.values[j]);
874 }
875 c = VideoColorSpace::Multiply(transforms_[2], c);
876 colors[i] = c;
877 }
878 }
879 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698