Line data Source code
1 : // Snap Websites Servers -- matrix class
2 : // Copyright (c) 2018-2019 Made to Order Software Corp. All Rights Reserved
3 : //
4 : // This program is free software; you can redistribute it and/or modify
5 : // it under the terms of the GNU General Public License as published by
6 : // the Free Software Foundation; either version 2 of the License, or
7 : // (at your option) any later version.
8 : //
9 : // This program is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : // GNU General Public License for more details.
13 : //
14 : // You should have received a copy of the GNU General Public License
15 : // along with this program; if not, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 : #pragma once
18 :
19 : // C++ lib
20 : //
21 : #include <cctype>
22 : #include <iostream>
23 : #include <stdexcept>
24 : #include <vector>
25 :
26 : #include <sstream>
27 :
28 :
29 : namespace snap
30 : {
31 :
32 : // Matrix additions, subtractions, and multiplications can be verified
33 : // using this web page:
34 : // http://www.calcul.com/show/calculator/matrix-multiplication
35 :
36 :
37 :
38 : template<typename T, typename SIZE = std::size_t>
39 143362 : class matrix
40 : {
41 : public:
42 : typedef T value_type;
43 : typedef SIZE size_type;
44 :
45 : // The color weights are used to convert RGB to Luma.
46 : //
47 : // With a factor, it's possible to change the color toward or
48 : // away from the perfect Luma if the color is not already
49 : // a gray color (see the saturation() function.)
50 : //
51 : // Note that these are often referenced to as Luminance Weights.
52 : // The Luminance is what you get on your monitor itself, not with
53 : // linear RGB as we manage in software.
54 : //
55 : // The AVERAGE luma factors are present because they may be useful
56 : // in some situation, but they aree definitely wrong and should
57 : // very rarely be used.
58 : //
59 : // see https://en.wikipedia.org/wiki/Luma_%28video%29
60 : // see https://www.opengl.org/archives/resources/code/samples/advanced/advanced97/notes/node140.html
61 : //
62 : static value_type constexpr HDTV_LUMA_RED = 0.2126;
63 : static value_type constexpr HDTV_LUMA_GREEN = 0.7152;
64 : static value_type constexpr HDTV_LUMA_BLUE = 0.0722;
65 :
66 : static value_type constexpr LED_LUMA_RED = 0.212;
67 : static value_type constexpr LED_LUMA_GREEN = 0.701;
68 : static value_type constexpr LED_LUMA_BLUE = 0.087;
69 :
70 : static value_type constexpr CRT_LUMA_RED = 0.3086;
71 : static value_type constexpr CRT_LUMA_GREEN = 0.6094;
72 : static value_type constexpr CRT_LUMA_BLUE = 0.0820;
73 :
74 : static value_type constexpr NTSC_LUMA_RED = 0.299;
75 : static value_type constexpr NTSC_LUMA_GREEN = 0.587;
76 : static value_type constexpr NTSC_LUMA_BLUE = 0.114;
77 :
78 : static value_type constexpr AVERAGE_LUMA_RED = 1.0 / 3.0;
79 : static value_type constexpr AVERAGE_LUMA_GREEN = 1.0 / 3.0;
80 : static value_type constexpr AVERAGE_LUMA_BLUE = 1.0 / 3.0;
81 :
82 : class element_ref
83 : {
84 : public:
85 555378 : element_ref(matrix<T, SIZE> & m, size_type j, size_type i)
86 : : f_matrix(m)
87 : //, f_j(j)
88 : //, f_i(i)
89 555378 : , f_offset(i + j * m.columns())
90 : {
91 555378 : if(i >= m.f_columns)
92 : {
93 0 : throw std::out_of_range("used [] operator with too large a row number");
94 : }
95 555378 : }
96 :
97 221701 : element_ref & operator = (value_type const v)
98 : {
99 221701 : f_matrix.f_vector[f_offset] = v;
100 :
101 221701 : return *this;
102 : }
103 :
104 1588 : bool operator == (value_type const v) const
105 : {
106 1588 : return f_matrix.f_vector[f_offset] == v;
107 : }
108 :
109 : bool operator != (value_type const v) const
110 : {
111 : return f_matrix.f_vector[f_offset] != v;
112 : }
113 :
114 : bool operator < (value_type const v) const
115 : {
116 : return f_matrix.f_vector[f_offset] < v;
117 : }
118 :
119 : bool operator <= (value_type const v) const
120 : {
121 : return f_matrix.f_vector[f_offset] <= v;
122 : }
123 :
124 : bool operator > (value_type const v) const
125 : {
126 : return f_matrix.f_vector[f_offset] > v;
127 : }
128 :
129 : bool operator >= (value_type const v) const
130 : {
131 : return f_matrix.f_vector[f_offset] >= v;
132 : }
133 :
134 332089 : operator T () const
135 : {
136 332089 : return f_matrix.f_vector[f_offset];
137 : }
138 :
139 : private:
140 : matrix<T, SIZE> & f_matrix;
141 : size_type f_offset;
142 : };
143 :
144 : class row_ref
145 : {
146 : public:
147 555378 : row_ref(matrix<T, SIZE> & m, size_type j)
148 : : f_matrix(m)
149 555378 : , f_j(j)
150 : {
151 555378 : if(j >= m.f_rows)
152 : {
153 0 : throw std::out_of_range("used [] operator with too large a row number");
154 : }
155 555378 : }
156 :
157 555378 : element_ref operator [] (size_type i)
158 : {
159 555378 : element_ref r(f_matrix, f_j, i);
160 555378 : return r;
161 : }
162 :
163 : value_type operator [] (size_type i) const
164 : {
165 : return f_matrix.f_vector[i + f_j * f_matrix.columns()];
166 : }
167 :
168 : private:
169 : matrix<T, SIZE> & f_matrix;
170 : size_type f_j; // row
171 : };
172 :
173 : class const_row_ref
174 : {
175 : public:
176 : const_row_ref(matrix<T, SIZE> const & m, size_type j)
177 : : f_matrix(m)
178 : , f_j(j)
179 : {
180 : if(j >= m.f_rows)
181 : {
182 : throw std::out_of_range("used [] operator with too large a row number");
183 : }
184 : }
185 :
186 : value_type operator [] (size_type i) const
187 : {
188 : if(i >= f_matrix.f_columns)
189 : {
190 : throw std::out_of_range("used [] operator with too large a row number");
191 : }
192 : return f_matrix.f_vector[i + f_j * f_matrix.columns()];
193 : }
194 :
195 : private:
196 : matrix<T, SIZE> const & f_matrix;
197 : size_type f_j; // row
198 : };
199 :
200 4 : matrix<T, SIZE>()
201 4 : {
202 4 : }
203 :
204 95299 : matrix<T, SIZE>(size_type rows, size_type columns)
205 : : f_rows(rows)
206 : , f_columns(columns)
207 95299 : , f_vector(rows * columns)
208 : {
209 95299 : initialize();
210 95299 : }
211 :
212 : template<typename V, typename SZ>
213 : matrix<T, SIZE>(matrix<V, SZ> const & rhs)
214 : : f_rows(rhs.f_rows)
215 : , f_columns(rhs.f_columns)
216 : , f_vector(rhs.f_vector.size())
217 : {
218 : initialize();
219 : }
220 :
221 : template<typename V, typename SZ>
222 : matrix<T, SIZE> & operator = (matrix<V, SZ> const & rhs) //= default;
223 : {
224 : f_rows = rhs.f_rows;
225 : f_columns = rhs.f_columns;
226 : f_vector = rhs.f_vector;
227 : f_luma_red = rhs.f_luma_red;
228 : f_luma_green = rhs.f_luma_green;
229 : f_luma_blue = rhs.f_luma_blue;
230 : #ifdef _DEBUG
231 : f_last_hue_matrix = rhs.f_last_hue_matrix;
232 : #endif
233 : }
234 :
235 : template<typename V, typename SZ>
236 : matrix<T, SIZE> & operator = (matrix<V, SZ> const && rhs) //= default;
237 : {
238 : f_rows = std::move(rhs.f_rows);
239 : f_columns = std::move(rhs.f_columns);
240 : f_vector = std::move(rhs.f_vector);
241 : f_luma_red = std::move(rhs.f_luma_red);
242 : f_luma_green = std::move(rhs.f_luma_green);
243 : f_luma_blue = std::move(rhs.f_luma_blue);
244 : #ifdef _DEBUG
245 : f_last_hue_matrix = std::move(rhs.f_last_hue_matrix);
246 : #endif
247 : }
248 :
249 : bool empty() const
250 : {
251 : return f_rows == 0 || f_columns == 0;
252 : }
253 :
254 : bool is_diagonal() const
255 : {
256 : for(size_type j(0); j < f_rows; ++j)
257 : {
258 : size_type const joffset(j * f_columns);
259 : for(size_type i(0); i < f_columns; ++i)
260 : {
261 : if(i != j
262 : && f_vector[i + joffset] != static_cast<value_type>(0)) // TODO: use a compare function with epsilon instead
263 : {
264 : return false;
265 : }
266 : }
267 : }
268 : return true;
269 : }
270 :
271 72 : size_type rows() const
272 : {
273 72 : return f_rows;
274 : }
275 :
276 555450 : size_type columns() const
277 : {
278 555450 : return f_columns;
279 : }
280 :
281 3 : void swap(matrix<T, SIZE> & rhs)
282 : {
283 3 : std::swap(f_rows, rhs.f_rows);
284 3 : std::swap(f_columns, rhs.f_columns);
285 3 : f_vector.swap(rhs.f_vector);
286 :
287 : // color related parameters
288 : //
289 3 : std::swap(f_luma_red, rhs.f_luma_red);
290 3 : std::swap(f_luma_green, rhs.f_luma_green);
291 3 : std::swap(f_luma_blue, rhs.f_luma_blue);
292 : #ifdef _DEBUG
293 3 : f_last_hue_matrix.swap(rhs.f_last_hue_matrix);
294 : #endif
295 3 : }
296 :
297 95299 : void initialize()
298 : {
299 95299 : if(f_rows == f_columns)
300 : {
301 81297 : identity();
302 : }
303 : else
304 : {
305 14002 : clear();
306 : }
307 95299 : }
308 :
309 14005 : void clear()
310 : {
311 14005 : std::fill(f_vector.begin(), f_vector.end(), value_type());
312 14005 : }
313 :
314 81297 : void identity()
315 : {
316 406061 : for(size_type j(0); j < f_rows; ++j)
317 : {
318 324764 : size_type const joffset(j * f_columns);
319 1622924 : for(size_type i(0); i < f_columns; ++i)
320 : {
321 1298160 : f_vector[i + joffset] =
322 : i == j
323 : ? static_cast<value_type>(1)
324 : : value_type();
325 : }
326 : }
327 81297 : }
328 :
329 555378 : row_ref operator [] (size_type row)
330 : {
331 555378 : row_ref r(*this, row);
332 555378 : return r;
333 : }
334 :
335 : const_row_ref operator [] (size_type row) const
336 : {
337 : const_row_ref r(*this, row);
338 : return r;
339 : }
340 :
341 : template<class S>
342 3 : matrix<T, SIZE> operator * (S const & scalar) const
343 : {
344 6 : matrix<T, SIZE> t(*this);
345 6 : return t *= scalar;
346 : }
347 :
348 : // At this point I don't know how to make that work...
349 : // template<class S, typename V, typename SZ> friend
350 : // matrix<V, SZ> operator * (S const & scalar, matrix<V, SZ> const & m)
351 : // {
352 : // return m * scalar;
353 : // }
354 :
355 : template<class S>
356 4 : matrix<T, SIZE> & operator *= (S const & scalar)
357 : {
358 20 : for(size_type j(0); j < f_rows; ++j)
359 : {
360 16 : size_type const joffset(j * f_columns);
361 80 : for(size_type i(0); i < f_columns; ++i)
362 : {
363 64 : f_vector[i + joffset] *= scalar;
364 : }
365 : }
366 :
367 4 : return *this;
368 : }
369 :
370 : template<typename V, typename SZ>
371 48007 : matrix<T, SIZE> operator * (matrix<V, SZ> const & m) const
372 : {
373 48007 : if(f_columns != m.f_rows)
374 : {
375 : // enhance that support with time
376 : // (i.e. the number of columns of one matrix has to match the
377 : // number of rows of the other, they don't need to be square
378 : // matrices of the same size)
379 : //
380 0 : std::cerr << "this " << f_rows << "x" << f_columns
381 0 : << " and rhs " << m.f_rows << "x" << m.f_columns << "\n";
382 0 : throw std::runtime_error("matrices of incompatible sizes for a multiplication");
383 : }
384 :
385 : // temporary buffer
386 : //
387 48007 : matrix<T, SIZE> t(f_rows, m.f_columns);
388 :
389 240035 : for(size_type j(0); j < f_rows; ++j)
390 : {
391 192028 : size_type const joffset(j * f_columns);
392 876140 : for(size_type i(0); i < m.f_columns; ++i)
393 : {
394 684112 : value_type sum = value_type();
395 :
396 : // k goes from 0 to (f_columns == m.f_rows)
397 : //
398 3420560 : for(size_type k(0); k < m.f_rows; ++k) // sometimes it's X and sometimes it's Y
399 : {
400 5472896 : sum += f_vector[k + joffset]
401 2736448 : * m.f_vector[i + k * m.f_columns];
402 : }
403 684112 : t.f_vector[i + j * t.f_columns] = sum;
404 : }
405 : }
406 :
407 48007 : return t;
408 : }
409 :
410 : template<class V>
411 7002 : matrix<T, SIZE> & operator *= (matrix<V> const & m)
412 : {
413 7002 : return *this = *this * m;
414 : }
415 :
416 : template<class S>
417 1 : matrix<T, SIZE> operator / (S const & scalar) const
418 : {
419 2 : matrix<T, SIZE> t(*this);
420 2 : return t /= scalar;
421 : }
422 :
423 : template<class S>
424 2 : matrix<T, SIZE> & operator /= (S const & scalar)
425 : {
426 10 : for(size_type y(0); y < f_rows; ++y)
427 : {
428 40 : for(size_type x(0); x < f_columns; ++x)
429 : {
430 32 : size_type const idx(x + y * f_columns);
431 32 : f_vector[idx] /= scalar;
432 : }
433 : }
434 :
435 2 : return *this;
436 : }
437 :
438 : template<typename V, typename SZ>
439 7001 : matrix<T, SIZE> operator / (matrix<V, SZ> const & m) const
440 : {
441 : // temporary buffer
442 : //
443 14002 : matrix<T, SIZE> t(m);
444 7001 : t.inverse();
445 14002 : return *this * t;
446 : }
447 :
448 : template<typename V, typename SZ>
449 1 : matrix<T, SIZE> & operator /= (matrix<V, SZ> const & m)
450 : {
451 2 : matrix<T, SIZE> t(m);
452 1 : t.inverse();
453 2 : return *this *= t;
454 : }
455 :
456 : /** \brief Compute the inverse of `this` matrix if possible.
457 : *
458 : * This function computes the matrix determinant to see whether
459 : * it can be inverted. If so, it computes the inverse and becomes
460 : * that inverse.
461 : *
462 : * The function returns false if the inverse cannot be calculated
463 : * and the matrix remains unchanged.
464 : *
465 : * $$A^{-1} = {1 \over det(A)} adj(A)$$
466 : *
467 : * \return true if the inverse computation was successful.
468 : */
469 7003 : bool inverse()
470 : {
471 14006 : if(f_rows != 4
472 7003 : || f_columns != 4)
473 : {
474 0 : double const det(determinant());
475 : #pragma GCC diagnostic push
476 : #pragma GCC diagnostic ignored "-Wfloat-equal"
477 0 : if(det == static_cast<value_type>(0))
478 : {
479 0 : return false;
480 : }
481 : #pragma GCC diagnostic pop
482 0 : snap::matrix<double> adj(adjugate());
483 :
484 0 : *this = adj * (static_cast<value_type>(1) / det);
485 0 : return true;
486 : }
487 :
488 : // the following is very specific to a 4x4 matrix...
489 : //
490 : value_type temp[4][8], *r[4], m[5];
491 :
492 7003 : r[0] = temp[0];
493 7003 : r[1] = temp[1];
494 7003 : r[2] = temp[2];
495 7003 : r[3] = temp[3];
496 :
497 7003 : temp[0][0] = f_vector[0 + 0 * 4];
498 7003 : temp[0][1] = f_vector[0 + 1 * 4];
499 7003 : temp[0][2] = f_vector[0 + 2 * 4];
500 7003 : temp[0][3] = f_vector[0 + 3 * 4];
501 :
502 7003 : temp[1][0] = f_vector[1 + 0 * 4];
503 7003 : temp[1][1] = f_vector[1 + 1 * 4];
504 7003 : temp[1][2] = f_vector[1 + 2 * 4];
505 7003 : temp[1][3] = f_vector[1 + 3 * 4];
506 :
507 7003 : temp[2][0] = f_vector[2 + 0 * 4];
508 7003 : temp[2][1] = f_vector[2 + 1 * 4];
509 7003 : temp[2][2] = f_vector[2 + 2 * 4];
510 7003 : temp[2][3] = f_vector[2 + 3 * 4];
511 :
512 7003 : temp[3][0] = f_vector[3 + 0 * 4];
513 7003 : temp[3][1] = f_vector[3 + 1 * 4];
514 7003 : temp[3][2] = f_vector[3 + 2 * 4];
515 7003 : temp[3][3] = f_vector[3 + 3 * 4];
516 :
517 : // can we do it?!
518 7003 : value_type abs_a = fabsf(r[3][0]);
519 7003 : value_type abs_b = fabsf(r[2][0]);
520 7003 : if(abs_a > abs_b) {
521 3 : value_type *swap = r[3];
522 3 : r[3] = r[2];
523 3 : r[2] = swap;
524 3 : abs_b = abs_a;
525 : }
526 :
527 7003 : abs_a = fabsf(r[1][0]);
528 7003 : if(abs_b > abs_a)
529 : {
530 7003 : value_type *swap = r[2];
531 7003 : r[2] = r[1];
532 7003 : r[1] = swap;
533 7003 : abs_a = abs_b;
534 : }
535 :
536 7003 : abs_b = fabsf(r[0][0]);
537 7003 : if(abs_a > abs_b)
538 : {
539 1003 : value_type *swap = r[1];
540 1003 : r[1] = r[0];
541 1003 : r[0] = swap;
542 1003 : abs_b = abs_a;
543 : }
544 :
545 : #pragma GCC diagnostic push
546 : #pragma GCC diagnostic ignored "-Wfloat-equal"
547 7003 : if(abs_b == 0.0f)
548 : {
549 0 : return false;
550 : }
551 :
552 7003 : temp[0][4] = temp[1][5] = temp[2][6] = temp[3][7] = 1.0f;
553 84036 : temp[0][5] =
554 7003 : temp[0][6] =
555 7003 : temp[0][7] =
556 7003 : temp[1][4] =
557 7003 : temp[1][6] =
558 7003 : temp[1][7] =
559 7003 : temp[2][4] =
560 7003 : temp[2][5] =
561 7003 : temp[2][7] =
562 7003 : temp[3][4] =
563 7003 : temp[3][5] =
564 7003 : temp[3][6] = 0.0f;
565 :
566 : // first elimination
567 7003 : m[4] = 1.0f / r[0][0];
568 7003 : m[1] = r[1][0] * m[4];
569 7003 : m[2] = r[2][0] * m[4];
570 7003 : m[3] = r[3][0] * m[4];
571 :
572 7003 : m[4] = r[0][1];
573 7003 : if(m[4] != 0.0)
574 : {
575 7003 : r[1][1] -= m[1] * m[4];
576 7003 : r[2][1] -= m[2] * m[4];
577 7003 : r[3][1] -= m[3] * m[4];
578 : }
579 :
580 7003 : m[4] = r[0][2];
581 7003 : if(m[4] != 0.0)
582 : {
583 7003 : r[1][2] -= m[1] * m[4];
584 7003 : r[2][2] -= m[2] * m[4];
585 7003 : r[3][2] -= m[3] * m[4];
586 : }
587 :
588 7003 : m[4] = r[0][3];
589 7003 : if(m[4] != 0.0)
590 : {
591 3 : r[1][3] -= m[1] * m[4];
592 3 : r[2][3] -= m[2] * m[4];
593 3 : r[3][3] -= m[3] * m[4];
594 : }
595 :
596 7003 : m[4] = r[0][4];
597 7003 : if(m[4] != 0.0)
598 : {
599 6000 : r[1][4] -= m[1] * m[4];
600 6000 : r[2][4] -= m[2] * m[4];
601 6000 : r[3][4] -= m[3] * m[4];
602 : }
603 :
604 7003 : m[4] = r[0][5];
605 7003 : if(m[4] != 0.0)
606 : {
607 0 : r[1][5] -= m[1] * m[4];
608 0 : r[2][5] -= m[2] * m[4];
609 0 : r[3][5] -= m[3] * m[4];
610 : }
611 :
612 7003 : m[4] = r[0][6];
613 7003 : if(m[4] != 0.0)
614 : {
615 1000 : r[1][6] -= m[1] * m[4];
616 1000 : r[2][6] -= m[2] * m[4];
617 1000 : r[3][6] -= m[3] * m[4];
618 : }
619 :
620 7003 : m[4] = r[0][7];
621 7003 : if(m[4] != 0.0)
622 : {
623 3 : r[1][7] -= m[1] * m[4];
624 3 : r[2][7] -= m[2] * m[4];
625 3 : r[3][7] -= m[3] * m[4];
626 : }
627 :
628 : // can we do it?!
629 7003 : abs_a = fabsf(r[3][1]);
630 7003 : abs_b = fabsf(r[2][1]);
631 7003 : if(abs_a > abs_b)
632 : {
633 0 : value_type *swap = r[3];
634 0 : r[3] = r[2];
635 0 : r[2] = swap;
636 0 : abs_b = abs_a;
637 : }
638 7003 : abs_a = fabsf(r[1][1]);
639 7003 : if(abs_b > abs_a)
640 : {
641 7003 : value_type *swap = r[2];
642 7003 : r[2] = r[1];
643 7003 : r[1] = swap;
644 7003 : abs_a = abs_b;
645 : }
646 :
647 7003 : if(abs_a == 0.0f)
648 : {
649 0 : return false;
650 : }
651 :
652 : // second elimination
653 7003 : m[4] = 1.0f / r[1][1];
654 :
655 7003 : m[2] = r[2][1] * m[4];
656 7003 : m[3] = r[3][1] * m[4];
657 :
658 7003 : if(m[2] != 0.0f)
659 : {
660 7003 : r[2][2] -= m[2] * r[1][2];
661 7003 : r[2][3] -= m[2] * r[1][3];
662 : }
663 :
664 7003 : if(m[3] != 0.0f)
665 : {
666 3 : r[3][2] -= m[3] * r[1][2];
667 3 : r[3][3] -= m[3] * r[1][3];
668 : }
669 :
670 7003 : m[4] = r[1][4];
671 7003 : if(m[4] != 0.0f)
672 : {
673 0 : r[2][4] -= m[2] * m[4];
674 0 : r[3][4] -= m[3] * m[4];
675 : }
676 :
677 7003 : m[4] = r[1][5];
678 7003 : if(m[4] != 0.0f)
679 : {
680 7003 : r[2][5] -= m[2] * m[4];
681 7003 : r[3][5] -= m[3] * m[4];
682 : }
683 :
684 7003 : m[4] = r[1][6];
685 7003 : if(m[4] != 0.0f)
686 : {
687 0 : r[2][6] -= m[2] * m[4];
688 0 : r[3][6] -= m[3] * m[4];
689 : }
690 :
691 7003 : m[4] = r[1][7];
692 7003 : if(m[4] != 0.0f)
693 : {
694 3 : r[2][7] -= m[2] * m[4];
695 3 : r[3][7] -= m[3] * m[4];
696 : }
697 :
698 : // can we do it?!
699 7003 : abs_a = fabsf(r[3][2]);
700 7003 : abs_b = fabsf(r[2][2]);
701 7003 : if(abs_a > abs_b)
702 : {
703 2 : value_type *swap = r[3];
704 2 : r[3] = r[2];
705 2 : r[2] = swap;
706 2 : abs_b = abs_a;
707 : }
708 :
709 7003 : if(abs_b == 0.0f)
710 : {
711 0 : return false;
712 : }
713 :
714 : // third elimination
715 7003 : m[3] = r[3][2] / r[2][2];
716 :
717 7003 : r[3][3] -= m[3] * r[2][3];
718 7003 : r[3][4] -= m[3] * r[2][4];
719 7003 : r[3][5] -= m[3] * r[2][5];
720 7003 : r[3][6] -= m[3] * r[2][6];
721 7003 : r[3][7] -= m[3] * r[2][7];
722 :
723 : // can we do it?!
724 7003 : if(r[3][3] == 0.0f) {
725 0 : return false;
726 : }
727 : #pragma GCC diagnostic pop
728 :
729 : // back substitute
730 : /* 3 */
731 7003 : m[4] = 1.0f / r[3][3];
732 7003 : r[3][4] *= m[4];
733 7003 : r[3][5] *= m[4];
734 7003 : r[3][6] *= m[4];
735 7003 : r[3][7] *= m[4];
736 :
737 : /* 2 */
738 7003 : m[2] = r[2][3];
739 7003 : m[4] = 1.0f / r[2][2];
740 7003 : r[2][4] = m[4] * (r[2][4] - r[3][4] * m[2]);
741 7003 : r[2][5] = m[4] * (r[2][5] - r[3][5] * m[2]);
742 7003 : r[2][6] = m[4] * (r[2][6] - r[3][6] * m[2]);
743 7003 : r[2][7] = m[4] * (r[2][7] - r[3][7] * m[2]);
744 :
745 7003 : m[1] = r[1][3];
746 7003 : r[1][4] -= r[3][4] * m[1];
747 7003 : r[1][5] -= r[3][5] * m[1];
748 7003 : r[1][6] -= r[3][6] * m[1];
749 7003 : r[1][7] -= r[3][7] * m[1];
750 :
751 7003 : m[0] = r[0][3];
752 7003 : r[0][4] -= r[3][4] * m[0];
753 7003 : r[0][5] -= r[3][5] * m[0];
754 7003 : r[0][6] -= r[3][6] * m[0];
755 7003 : r[0][7] -= r[3][7] * m[0];
756 :
757 : /* 1 */
758 7003 : m[1] = r[1][2];
759 7003 : m[4] = 1.0f / r[1][1];
760 7003 : r[1][4] = m[4] * (r[1][4] - r[2][4] * m[1]);
761 7003 : r[1][5] = m[4] * (r[1][5] - r[2][5] * m[1]);
762 7003 : r[1][6] = m[4] * (r[1][6] - r[2][6] * m[1]);
763 7003 : r[1][7] = m[4] * (r[1][7] - r[2][7] * m[1]);
764 :
765 7003 : m[0] = r[0][2];
766 7003 : r[0][4] -= r[2][4] * m[0];
767 7003 : r[0][5] -= r[2][5] * m[0];
768 7003 : r[0][6] -= r[2][6] * m[0];
769 7003 : r[0][7] -= r[2][7] * m[0];
770 :
771 : /* 0 */
772 7003 : m[0] = r[0][1];
773 7003 : m[4] = 1.0f / r[0][0];
774 7003 : r[0][4] = m[4] * (r[0][4] - r[1][4] * m[0]);
775 7003 : r[0][5] = m[4] * (r[0][5] - r[1][5] * m[0]);
776 7003 : r[0][6] = m[4] * (r[0][6] - r[1][6] * m[0]);
777 7003 : r[0][7] = m[4] * (r[0][7] - r[1][7] * m[0]);
778 :
779 : // save in destination (we need to put that in the back substitution)
780 7003 : f_vector[0 + 0 * 4] = r[0][4];
781 7003 : f_vector[0 + 1 * 4] = r[0][5];
782 7003 : f_vector[0 + 2 * 4] = r[0][6];
783 7003 : f_vector[0 + 3 * 4] = r[0][7];
784 :
785 7003 : f_vector[1 + 0 * 4] = r[1][4];
786 7003 : f_vector[1 + 1 * 4] = r[1][5];
787 7003 : f_vector[1 + 2 * 4] = r[1][6];
788 7003 : f_vector[1 + 3 * 4] = r[1][7];
789 :
790 7003 : f_vector[2 + 0 * 4] = r[2][4];
791 7003 : f_vector[2 + 1 * 4] = r[2][5];
792 7003 : f_vector[2 + 2 * 4] = r[2][6];
793 7003 : f_vector[2 + 3 * 4] = r[2][7];
794 :
795 7003 : f_vector[3 + 0 * 4] = r[3][4];
796 7003 : f_vector[3 + 1 * 4] = r[3][5];
797 7003 : f_vector[3 + 2 * 4] = r[3][6];
798 7003 : f_vector[3 + 3 * 4] = r[3][7];
799 :
800 7003 : return true;
801 : }
802 :
803 : /** \brief Reduce a matrix by removing one row and one column.
804 : *
805 : * This function creates a minor duplicate of this matrix with column i
806 : * and row j removed.
807 : *
808 : * The minor is denoted:
809 : *
810 : * $$M_{ij}$$
811 : *
812 : * It is a matrix built from $A$ without column `i` and row `j`.
813 : *
814 : * \note
815 : * The matrix must at least be a 2x2 matrix.
816 : *
817 : * \note
818 : * There is a "minor" macro so I named this function minor_matrix().
819 : *
820 : * \param[in] row The row to remove.
821 : * \param[in] column The column to remove.
822 : *
823 : * \return The requested minor matrix.
824 : */
825 220 : matrix<T, SIZE> minor_matrix(size_type row, size_type column) const
826 : {
827 440 : if(f_rows < 2
828 220 : || f_columns < 2)
829 : {
830 0 : throw std::runtime_error("minor_matrix() must be called with a matrix which is at least 2x2, although it does not need to be a square matrix");
831 : }
832 :
833 220 : matrix<T, SIZE> p(f_rows - 1, f_columns - 1);
834 :
835 : // we loop using p sizes
836 : // the code below ensures the correct input is retrieved
837 : //
838 : // di -- destination column
839 : // dj -- destination row
840 : // si -- source column
841 : // sj -- source row
842 : //
843 701 : for(size_type dj(0); dj < p.f_rows; ++dj)
844 : {
845 1574 : for(size_type di(0); di < p.f_columns; ++di)
846 : {
847 : // here we have 4 cases:
848 : //
849 : // a11 a12 | a13 | a14 a15
850 : // a21 a22 | a23 | a24 a25
851 : // --------+-----+--------
852 : // a31 a32 | a33 | a34 a35
853 : // --------+-----+--------
854 : // a41 a42 | a43 | a44 a45
855 : // a51 a52 | a53 | a54 a55
856 : //
857 : // assuming 'row' and 'column' are set to 3 and 3, then
858 : // the graph shows the 4 cases as the 4 corners, the
859 : // center lines will be removed so they are ignored in
860 : // the source
861 : //
862 1093 : size_type const si(di < column ? di : di + 1);
863 1093 : size_type const sj(dj < row ? dj : dj + 1);
864 :
865 1093 : p.f_vector[di + dj * p.f_columns] = f_vector[si + sj * f_columns];
866 : }
867 : }
868 :
869 220 : return p;
870 : }
871 :
872 : /** \brief Calculate the determinant of this matrix.
873 : *
874 : * This function calculates the determinant of this matrix:
875 : *
876 : * $$det(A) = \sum_{\sigma \in S_n} \Big( sgn(\sigma) \prod_{i=1}^{n} a_{i,\sigma_i} \Big)$$
877 : *
878 : * The function is implemented using a recursive set of calls. It
879 : * knows how to calculate a 2x2 matrix. All the ohers use recursive
880 : * calls to calculate the final result.
881 : *
882 : * Let's say you have a 3x3 matrix like this:
883 : *
884 : * \code
885 : * | a11 a12 a13 |
886 : * | a21 a22 a23 |
887 : * | a31 a32 a33 |
888 : * \endcode
889 : *
890 : * If first calculates the determinant of the 2x2 matrix:
891 : *
892 : * \code
893 : * | a22 a23 | = a22 x a33 - a23 x a32
894 : * | a32 a33 |
895 : * \endcode
896 : *
897 : * Then multiply that determinant by `a11`.
898 : *
899 : * Next it calculates the determinant of the 2x2 matrix:
900 : *
901 : * \code
902 : * | a21 a23 | = a21 x a33 - a23 x a31
903 : * | a31 a33 |
904 : * \endcode
905 : *
906 : * Then multiply that determinant by `a12` and multiply by -1.
907 : *
908 : * Finally, it calculates the last 2x2 matrix at the bottom left corner.
909 : *
910 : * \code
911 : * | a21 a22 | = a21 x a32 - a22 x a31
912 : * | a31 a32 |
913 : * \endcode
914 : *
915 : * Then multiply that last determinant by `a13`.
916 : *
917 : * Finally we sum all three results and that's our determinant for a
918 : * 3x3 matrix.
919 : *
920 : * The determinant of a 4x4 matrix will be calculated in a similar
921 : * way by also calculating the determin of all the 3x3 matrices
922 : * defined under the first row.
923 : *
924 : * Source: https://en.wikipedia.org/wiki/Determinant
925 : *
926 : * \exception runtime_error
927 : * If the matrix is not a square matrix, raise a runtime_error exception.
928 : *
929 : * \return The determinant value.
930 : */
931 229 : value_type determinant() const
932 : {
933 229 : if(f_rows != f_columns)
934 : {
935 0 : throw std::runtime_error("determinant can only be calculated for square matrices");
936 : }
937 :
938 229 : if(f_columns == 1)
939 : {
940 4 : return f_vector[0];
941 : }
942 :
943 225 : if(f_columns == 2)
944 : {
945 344 : return f_vector[0 + 0 * 2] * f_vector[1 + 1 * 2]
946 344 : - f_vector[1 + 0 * 2] * f_vector[0 + 1 * 2];
947 : }
948 :
949 53 : value_type determinant = value_type();
950 :
951 53 : value_type sign = static_cast<value_type>(1);
952 214 : for(size_type c(0); c < f_columns; ++c)
953 : {
954 : // create a minor matrix
955 : //
956 322 : matrix<T, SIZE> p(minor_matrix(0, c));
957 :
958 : // add to the determinant for that column
959 : // (number of row 0 column 'c')
960 : //
961 322 : determinant += sign
962 161 : * f_vector[c + 0 * f_columns]
963 161 : * p.determinant();
964 :
965 : // swap the sign
966 : //
967 161 : sign *= static_cast<value_type>(-1);
968 : }
969 :
970 53 : return determinant;
971 : }
972 :
973 : /** \brief Swap the rows and columns of a matrix.
974 : *
975 : * This function returns the transpose of this matrix.
976 : *
977 : * Generally noted as:
978 : *
979 : * $$A^T$$
980 : *
981 : * The definition of the transpose is:
982 : *
983 : * $$[A^T]_{ij} = [A]_{ji}$$
984 : *
985 : * The resulting matrix has a number of columns equal to 'this' matrix
986 : * rows and vice versa.
987 : *
988 : * \return A new matrix representing the transpose of 'this' matrix.
989 : */
990 6 : matrix<T, SIZE> transpose() const
991 : {
992 : // 'm' has its number of rows and columns swapped compared
993 : // to 'this'
994 6 : matrix<T, SIZE> m(f_columns, f_rows);
995 :
996 29 : for(size_type j(0); j < f_rows; ++j)
997 : {
998 96 : for(size_type i(0); i < f_columns; ++i)
999 : {
1000 : // we could also have used "j + i * f_rows" on the left
1001 : // but I think it's more confusing
1002 : //
1003 73 : m.f_vector[j + i * m.f_columns] = f_vector[i + j * f_columns];
1004 : }
1005 : }
1006 :
1007 6 : return m;
1008 : }
1009 :
1010 : /** \brief This function calculates the adjugate of this matrix.
1011 : *
1012 : * \return The adjugate of this matrix.
1013 : */
1014 4 : matrix<T, SIZE> adjugate() const
1015 : {
1016 4 : if(f_rows != f_columns)
1017 : {
1018 : // is that true?
1019 : //
1020 0 : throw std::runtime_error("adjugate can only be calculated for square matrices");
1021 : }
1022 :
1023 8 : matrix<T, SIZE> r(f_rows, f_columns);
1024 :
1025 : // det(A) when A is 1x1 equals | 1 |
1026 : // which is the default 'r'
1027 : //
1028 4 : if(f_columns != 1)
1029 : {
1030 : //if(f_columns == 2)
1031 : //{
1032 : // // the 2x2 matrix is handled as a special case just so it goes
1033 : // // faster but not so much more than that
1034 : // //
1035 : // // adj | a b | = | d -b |
1036 : // // | c d | | -c a |
1037 : // //
1038 : // r.f_vector[0 + 0 * 2] = f_vector[1 + 1 * 2];
1039 : // r.f_vector[1 + 0 * 2] = -f_vector[1 + 0 * 2];
1040 : // r.f_vector[0 + 1 * 2] = -f_vector[0 + 1 * 2];
1041 : // r.f_vector[1 + 1 * 2] = f_vector[0 + 0 * 2];
1042 : //}
1043 : //else
1044 : {
1045 : // for larger matrices we use a loop and calculate the determinant
1046 : // for each new value with the "rest" of the matrix at that point
1047 : //
1048 17 : for(size_type j(0); j < f_rows; ++j)
1049 : {
1050 58 : for(size_type i(0); i < f_columns; ++i)
1051 : {
1052 90 : matrix<T, SIZE> p(minor_matrix(j, i));
1053 45 : r.f_vector[i + j * r.f_columns] = static_cast<value_type>(((i + j) & 1) == 0 ? 1 : -1) * p.determinant();
1054 : }
1055 : }
1056 :
1057 4 : return r.transpose();
1058 : }
1059 : }
1060 :
1061 0 : return r;
1062 : }
1063 :
1064 : /** \brief Add a scale to 'this' matrix.
1065 : *
1066 : * This function adds the specified scalar to the matrix. This adds
1067 : * the specified amount to all the elements of the matrix.
1068 : *
1069 : * $$[A]_{ij} \leftarrow [A]_{ij} + scalar$$
1070 : *
1071 : * \param[in] scalar The scalar to add to this matrix.
1072 : */
1073 : template<class S>
1074 1 : matrix<T, SIZE> operator + (S const & scalar) const
1075 : {
1076 2 : matrix<T, SIZE> t(*this);
1077 2 : return t += scalar;
1078 : }
1079 :
1080 : template<class S>
1081 2 : matrix<T, SIZE> & operator += (S const & scalar)
1082 : {
1083 10 : for(size_type y(0); y < f_rows; ++y)
1084 : {
1085 40 : for(size_type x(0); x < f_columns; ++x)
1086 : {
1087 32 : size_type const idx(x + y * f_columns);
1088 32 : f_vector[idx] += scalar;
1089 : }
1090 : }
1091 :
1092 2 : return *this;
1093 : }
1094 :
1095 : template<typename V, typename SZ>
1096 1 : matrix<T, SIZE> operator + (matrix<V, SZ> const & m) const
1097 : {
1098 2 : matrix<T, SIZE> t(*this);
1099 2 : return t += m;
1100 : }
1101 :
1102 : template<typename V, typename SZ>
1103 2 : matrix<T, SIZE> & operator += (matrix<V, SZ> const & m)
1104 : {
1105 4 : if(f_rows != m.f_rows
1106 2 : || f_columns != m.f_columns)
1107 : {
1108 0 : throw std::runtime_error("matrices of incompatible sizes for an addition");
1109 : }
1110 :
1111 10 : for(size_type y(0); y < f_rows; ++y)
1112 : {
1113 40 : for(size_type x(0); x < f_columns; ++x)
1114 : {
1115 32 : size_type const idx(x + y * f_columns);
1116 32 : f_vector[idx] += m.f_vector[idx];
1117 : }
1118 : }
1119 :
1120 2 : return *this;
1121 : }
1122 :
1123 : template<class S>
1124 1 : matrix<T, SIZE> operator - (S const & scalar) const
1125 : {
1126 2 : matrix<T, SIZE> t(*this);
1127 2 : return t -= scalar;
1128 : }
1129 :
1130 : template<class S>
1131 2 : matrix<T, SIZE> & operator -= (S const & scalar)
1132 : {
1133 4 : matrix<T, SIZE> t(f_rows, f_columns);
1134 :
1135 10 : for(size_type y(0); y < f_rows; ++y)
1136 : {
1137 40 : for(size_type x(0); x < f_columns; ++x)
1138 : {
1139 32 : size_type const idx(x + y * f_columns);
1140 32 : f_vector[idx] -= scalar;
1141 : }
1142 : }
1143 :
1144 4 : return *this;
1145 : }
1146 :
1147 : template<typename V, typename SZ>
1148 1 : matrix<T, SIZE> operator - (matrix<V, SZ> const & m) const
1149 : {
1150 2 : matrix<T, SIZE> t(*this);
1151 2 : return t -= m;
1152 : }
1153 :
1154 : template<class V, typename SZ>
1155 2 : matrix<T, SIZE> & operator -= (matrix<V, SZ> const & m)
1156 : {
1157 4 : if(f_rows != m.f_rows
1158 2 : || f_columns != m.f_columns)
1159 : {
1160 0 : throw std::runtime_error("matrices of incompatible sizes for a subtraction");
1161 : }
1162 :
1163 10 : for(size_type y(0); y < f_rows; ++y)
1164 : {
1165 40 : for(size_type x(0); x < f_columns; ++x)
1166 : {
1167 32 : size_type const idx(x + y * f_columns);
1168 32 : f_vector[idx] -= m.f_vector[idx];
1169 : }
1170 : }
1171 :
1172 2 : return *this;
1173 : }
1174 :
1175 :
1176 : /** \brief Apply a scaling factor to this matrix.
1177 : *
1178 : * This function multiplies 'this' matrix by a scaling factor and
1179 : * returns the result. 'this' does not get changed.
1180 : *
1181 : * The scale matrix looks like:
1182 : *
1183 : * $$
1184 : * \begin{bmatrix}
1185 : * brightness_r & 0 & 0 & 0
1186 : * \\ 0 & brightness_g & 0 & 0
1187 : * \\ 0 & 0 & brightness_b & 0
1188 : * \\ 0 & 0 & 0 & 1
1189 : * \end{bmatrix}
1190 : * $$
1191 : *
1192 : * The 'r', 'g', 'b' indices represent the RGB colors if one wants to
1193 : * scale just one color at a time although this function only offers
1194 : * to set all of the fields to the same value \p s.
1195 : *
1196 : * See:
1197 : * http://www.graficaobscura.com/matrix/index.html
1198 : *
1199 : * \param[in] b The scaling factor to apply to this matrix.
1200 : *
1201 : * \return 'this' x 'brightness'
1202 : */
1203 : template<typename S>
1204 1000 : matrix<T, SIZE> brightness(S const b) const
1205 : {
1206 2000 : if(f_rows != 4
1207 1000 : || f_columns != 4)
1208 : {
1209 0 : throw std::runtime_error("scale() is only for 4x4 matrices at this time");
1210 : }
1211 :
1212 2000 : matrix<T, SIZE> m(4, 4);
1213 1000 : m[0][0] = b;
1214 1000 : m[1][1] = b;
1215 1000 : m[2][2] = b;
1216 :
1217 2000 : return *this * m;
1218 : }
1219 :
1220 :
1221 : /** \brief Apply an RGB color saturation to this matrix.
1222 : *
1223 : * This function applies the saturation matrix defined below to
1224 : * 'this' matrix. When the saturation parameter is set to zero (0)
1225 : * the function transforms all the colors to gray. When the saturation
1226 : * is set to one (1), the color does not get changed.
1227 : *
1228 : * A saturation parameter outside of the [0 .. 1] range will have
1229 : * unexpected results.
1230 : *
1231 : * The saturation matrix:
1232 : *
1233 : * $$
1234 : * \begin{bmatrix}
1235 : * L_r \times ( 1 - saturation) + saturation & L_r \times ( 1 - saturation) & L_r \times ( 1 - saturation) & 0
1236 : * \\ L_g \times ( 1 - saturation) & L_g \times ( 1 - saturation) + saturation & L_g \times ( 1 - saturation) & 0
1237 : * \\ L_b \times ( 1 - saturation) & L_b \times ( 1 - saturation) & L_b \times ( 1 - saturation) + saturation & 0
1238 : * \\ 0 & 0 & 0 & 1
1239 : * \end{bmatrix}
1240 : * $$
1241 : *
1242 : * The weights used here come from the luma matrix. See the
1243 : * get_luma_vector() function.
1244 : *
1245 : * See:
1246 : * http://www.graficaobscura.com/matrix/index.html
1247 : *
1248 : * \param[in] s How close or far to correct the color toward saturation.
1249 : *
1250 : * \return 'this' x 'scale'
1251 : */
1252 : template<typename S>
1253 6000 : matrix<T, SIZE> saturation(S const s) const
1254 : {
1255 12000 : if(f_rows != 4
1256 6000 : || f_columns != 4)
1257 : {
1258 0 : throw std::runtime_error("saturation() is only for 4x4 matrices at this time");
1259 : }
1260 :
1261 12000 : matrix<T, SIZE> m(4, 4);
1262 :
1263 6000 : value_type const ns(static_cast<value_type>(s));
1264 6000 : value_type const os(static_cast<value_type>(1) - static_cast<value_type>(s));
1265 :
1266 6000 : m[0][0] = f_luma_red * os + ns;
1267 6000 : m[0][1] = f_luma_red * os;
1268 6000 : m[0][2] = f_luma_red * os;
1269 :
1270 6000 : m[1][0] = f_luma_green * os;
1271 6000 : m[1][1] = f_luma_green * os + ns;
1272 6000 : m[1][2] = f_luma_green * os;
1273 :
1274 6000 : m[2][0] = f_luma_blue * os;
1275 6000 : m[2][1] = f_luma_blue * os;
1276 6000 : m[2][2] = f_luma_blue * os + ns;
1277 :
1278 12000 : return *this * m;
1279 : }
1280 :
1281 :
1282 : /** \brief Apply a hue correction.
1283 : *
1284 : * This function applies a hue correction to 'this' matrix.
1285 : *
1286 : * The hue correction makes use of the rotation around the red
1287 : * and green axis, then a skew to take luminace in account.
1288 : * At that point it rotates the color. Finally, the function
1289 : * reverses the skew and rotate back around the green and red
1290 : * axis.
1291 : *
1292 : * The following is the list of matrices used to rotate the hue:
1293 : *
1294 : * Rotation around the Red axis (Rr):
1295 : *
1296 : * $$
1297 : * R_r =
1298 : * \begin{bmatrix}
1299 : * 1 & 0 & 0 & 0
1300 : * \\ 0 & {1 \over \sqrt 2 } & {1 \over \sqrt 2 } & 0
1301 : * \\ 0 & -{1 \over \sqrt 2 } & {1 \over \sqrt 2 } & 0
1302 : * \\ 0 & 0 & 0 & 1
1303 : * \end{bmatrix}
1304 : * $$
1305 : *
1306 : * \note
1307 : * The $1 \over \sqrt 2$ is $sin ( \pi \over 4 )$ and
1308 : * $cos ( \pi \over 4 )$.
1309 : *
1310 : * Rotation around the Green axis (Rg):
1311 : *
1312 : * $$
1313 : * R_g =
1314 : * \begin{bmatrix}
1315 : * {\sqrt 2 \over \sqrt 3} & 0 & {1 \over \sqrt 3} & 0
1316 : * \\ 0 & 1 & 0 & 0
1317 : * \\ -{1 \over \sqrt 3} & 0 & {\sqrt 2 \over \sqrt 3} & 0
1318 : * \\ 0 & 0 & 0 & 1
1319 : * \end{bmatrix}
1320 : * $$
1321 : *
1322 : * \note
1323 : * The $\sqrt 2 \over \sqrt 3$ and $1 \over \sqrt 3$ are sin and
1324 : * cos as well. These take the first rotation in account (i.e.
1325 : * so it is again a 45° angle but applied after the 45° angle
1326 : * applied to the red axis.)
1327 : *
1328 : * Combine both rotations:
1329 : *
1330 : * $$
1331 : * R_{rg} = R_r R_g
1332 : * $$
1333 : *
1334 : * Since colors are linear but not at a similar angle, we want to
1335 : * unskew them for which we need to use the luminance. So here we
1336 : * compute the luminance of the color.
1337 : *
1338 : * $$
1339 : * \begin{bmatrix}
1340 : * L_r
1341 : * \\ L_g
1342 : * \\ L_b
1343 : * \\ 0
1344 : * \end{bmatrix}
1345 : * =
1346 : * R_{rg}
1347 : * \begin{bmatrix}
1348 : * W_r
1349 : * \\ W_g
1350 : * \\ W_b
1351 : * \\ 0
1352 : * \end{bmatrix}
1353 : * $$
1354 : *
1355 : * Now we define a rotation matrix for the blue axis. This one uses
1356 : * a variable as the angle. This is the actual rotation angle which
1357 : * can go from 0 to $2 \pi$:
1358 : *
1359 : * $$
1360 : * R_b =
1361 : * \begin{bmatrix}
1362 : * cos( \theta ) & sin( \theta ) & 0 & 0
1363 : * \\ -sin( \theta ) & cos( \theta ) & 0 & 0
1364 : * \\ 0 & 0 & 1 & 0
1365 : * \\ 0 & 0 & 0 & 1
1366 : * \end{bmatrix}
1367 : * $$
1368 : *
1369 : * Now we have all the matrices to caculate the hue rotation
1370 : * of all the components of an image:
1371 : *
1372 : * $$
1373 : * H = R_{rg} S R_b S^{-1} R_{rg}^{-1}
1374 : * $$
1375 : *
1376 : * $H$ can then be used as in:
1377 : *
1378 : * $$
1379 : * \begin{bmatrix}
1380 : * R'
1381 : * \\ G'
1382 : * \\ B'
1383 : * \end{bmatrix}
1384 : * =
1385 : * H
1386 : * \begin{bmatrix}
1387 : * R
1388 : * \\ G
1389 : * \\ B
1390 : * \end{bmatrix}
1391 : * $$
1392 : *
1393 : * See:
1394 : * http://www.graficaobscura.com/matrix/index.html
1395 : *
1396 : * We can also rewrite the hue matrix as follow:
1397 : *
1398 : * $$
1399 : * H = cos ( \theta ) C + sin ( \theta ) S + T
1400 : * $$
1401 : *
1402 : * Where C is the cosine matrix, S is the sine matrix and T is an
1403 : * additonal translation.
1404 : *
1405 : * I found an example on Microsoft (see link below) which I suspect
1406 : * does not include the color skewing. In other word, it completely
1407 : * ignores the color lumence weights which gives invalid results
1408 : * (albeit in most cases relatively good.)
1409 : *
1410 : * $$
1411 : * H =
1412 : * cos ( \theta )
1413 : * \begin{bmatrix}
1414 : * 0.787 & -0.213 & -0.213
1415 : * \\ -0.715 & 0.285 & -0.715
1416 : * \\ -0.072 & -0.072 & 0.928
1417 : * \end{bmatrix}
1418 : * +
1419 : * sin ( \theta )
1420 : * \begin{bmatrix}
1421 : * -0.213 & 0.143 & -0.787
1422 : * \\ -0.715 & 0.140 & 0.715
1423 : * \\ 0.928 & -0.283 & 0.072
1424 : * \end{bmatrix}
1425 : * +
1426 : * \begin{bmatrix}
1427 : * 0.213 & 0.213 & 0.213
1428 : * \\ 0.715 & 0.715 & 0.715
1429 : * \\ 0.072 & 0.072 & 0.072
1430 : * \end{bmatrix}
1431 : * $$
1432 : *
1433 : * The correct version is the one we actually use and it goes like this
1434 : * with high precision (double floating points) and 4x4 matrices:
1435 : *
1436 : * IMPORTANT: the weights change depending on the selected luma.
1437 : * If the user makes use of a luma which is not one supported by
1438 : * defaut, the algorithm falls back to the fully dynamic version
1439 : * of the hue computation.
1440 : *
1441 : * $$
1442 : * H =
1443 : * cos ( \theta )
1444 : * \begin{bmatrix}
1445 : * 0.943571345820976240 & -0.056428654178995265 & -0.056428654178995265 & 0
1446 : * \\ -0.189552583569840000 & 0.810447416430107000 & -0.189552583569853000 & 0
1447 : * \\ -0.754018762251120000 & -0.754018762251113000 & 0.245981237748847000 & 0
1448 : * \\ 0 & 0 & 0 & 0
1449 : * \end{bmatrix}
1450 : * +
1451 : * sin ( \theta )
1452 : * \begin{bmatrix}
1453 : * 0.32589470021007605 & 0.90324496939967125 & -0.25145556897954369 & 0
1454 : * \\ -0.98010410586906000 & -0.40275383667945400 & 0.17459643251014600 & 0
1455 : * \\ 0.65420940565900000 & -0.50049113272020600 & 0.07685913646939400 & 0
1456 : * \\ 0 & 0 & 0 & 0
1457 : * \end{bmatrix}
1458 : * +
1459 : * \begin{bmatrix}
1460 : * 0.056428654178995 & 0.056428654178995 & 0.056428654178995 & 0
1461 : * \\ 0.189552583569860 & 0.189552583569860 & 0.189552583569860 & 0
1462 : * \\ 0.754018762251160 & 0.754018762251160 & 0.754018762251160 & 0
1463 : * \\ 0 & 0 & 0 & 1
1464 : * \end{bmatrix}
1465 : * $$
1466 : *
1467 : * This is exactly the matrix we use below, although we directly apply the
1468 : * matrix additions and multiplications to generate the matrix as quickly
1469 : * as possible (we calculte just the necessary component and avoid many
1470 : * copies so it ought to be much faster.)
1471 : *
1472 : * \note
1473 : * It looks like the Microsoft matrices are reversed, so affecting BGR
1474 : * instead of RGB. Our matrices expects RGBX, but we adjust the
1475 : * multiplication as required to handle BGR, XBGR, XRGB, etc. We will
1476 : * change our matrix if required by OpenGL but as far as I know it's
1477 : * already defined in the correct direction.
1478 : *
1479 : * See:
1480 : * https://msdn.microsoft.com/en-us/library/windows/desktop/hh706342(v=vs.85).aspx
1481 : *
1482 : * \note
1483 : * To verify that the angle is correct, one can use this Wikipedia
1484 : * page. As we can see, from Red, you add 30° to get yellow, 120°
1485 : * to get green, etc. Obviously you can go also backward with
1486 : * negative angles.
1487 : * https://en.wikipedia.org/wiki/Hue
1488 : *
1489 : * \note
1490 : * For test purposes, which version of the matrix is used is defined
1491 : * by a variable that can be retrieved using the get_last_hue_matrix().
1492 : * The function is only available in debug mode.
1493 : *
1494 : * \param[in] h The amount of rotation, an angle in radian.
1495 : *
1496 : * \return 'this' rotated around the hue axis by \p h
1497 : */
1498 : template<typename S>
1499 6000 : matrix<T, SIZE> hue(S const h) const
1500 : {
1501 12000 : if(f_rows != 4
1502 6000 : || f_columns != 4)
1503 : {
1504 0 : throw std::runtime_error("scale() is only for 4x4 matrices at this time");
1505 : }
1506 :
1507 6000 : value_type const rot_cos(cos(static_cast<value_type>(h)));
1508 6000 : value_type const rot_sin(sin(static_cast<value_type>(h)));
1509 :
1510 8000 : if(fabs(f_luma_red - HDTV_LUMA_RED ) < 0.0001
1511 1000 : && fabs(f_luma_green - HDTV_LUMA_GREEN) < 0.0001
1512 1000 : && fabs(f_luma_blue - HDTV_LUMA_BLUE ) < 0.0001)
1513 : {
1514 : // this matrice make use of the HDTV luma
1515 : //
1516 2000 : matrix<T, SIZE> hue_matrix(4, 4);
1517 :
1518 1000 : hue_matrix[0][0] = 0.85089741314769186 * rot_cos + 0.39419567713872435 * rot_sin + 0.14910258685230815;
1519 1000 : hue_matrix[0][1] = -0.14910258685230816 * rot_cos + 0.97154594632835023 * rot_sin + 0.14910258685230815;
1520 1000 : hue_matrix[0][2] = -0.14910258685230816 * rot_cos - 0.18315459205090151 * rot_sin + 0.14910258685230815;
1521 :
1522 1000 : hue_matrix[1][0] = -0.08406523610970199 * rot_cos - 0.93399661436972614 * rot_sin + 0.08406523610970204;
1523 1000 : hue_matrix[1][1] = 0.91593476389029794 * rot_cos - 0.35664634518010058 * rot_sin + 0.08406523610970204;
1524 1000 : hue_matrix[1][2] = -0.08406523610970201 * rot_cos + 0.22070392400952521 * rot_sin + 0.08406523610970204;
1525 :
1526 1000 : hue_matrix[2][0] = -0.76683217703798975 * rot_cos + 0.53980093723100184 * rot_sin + 0.76683217703798978;
1527 1000 : hue_matrix[2][1] = -0.76683217703798980 * rot_cos - 0.61489960114824952 * rot_sin + 0.76683217703798978;
1528 1000 : hue_matrix[2][2] = 0.23316782296201015 * rot_cos - 0.03754933195862373 * rot_sin + 0.76683217703798978;
1529 :
1530 : #ifdef _DEBUG
1531 1000 : f_last_hue_matrix = "hdtv";
1532 : #endif
1533 :
1534 1000 : return *this * hue_matrix;
1535 : }
1536 :
1537 7000 : if(fabs(f_luma_red - LED_LUMA_RED ) < 0.0001
1538 1000 : && fabs(f_luma_green - LED_LUMA_GREEN) < 0.0001
1539 1000 : && fabs(f_luma_blue - LED_LUMA_BLUE ) < 0.0001)
1540 : {
1541 : // this matrice make use of the LED luma
1542 : //
1543 2000 : matrix<T, SIZE> hue_matrix(4, 4);
1544 :
1545 1000 : hue_matrix[0][0] = 0.86455583487454547 * rot_cos + 0.40703991394281032 * rot_sin + 0.13544416512545457;
1546 1000 : hue_matrix[0][1] = -0.13544416512545459 * rot_cos + 0.98439018313243625 * rot_sin + 0.13544416512545460;
1547 1000 : hue_matrix[0][2] = -0.13544416512545459 * rot_cos - 0.17031035524681553 * rot_sin + 0.13544416512545460;
1548 :
1549 1000 : hue_matrix[1][0] = -0.07977101160856729 * rot_cos - 0.95224727296282565 * rot_sin + 0.07977101160856727;
1550 1000 : hue_matrix[1][1] = 0.92022898839143270 * rot_cos - 0.37489700377320009 * rot_sin + 0.07977101160856730;
1551 1000 : hue_matrix[1][2] = -0.07977101160856729 * rot_cos + 0.20245326541642572 * rot_sin + 0.07977101160856730;
1552 :
1553 1000 : hue_matrix[2][0] = -0.78478482326597805 * rot_cos + 0.54520735902001539 * rot_sin + 0.78478482326597820;
1554 1000 : hue_matrix[2][1] = -0.78478482326597812 * rot_cos - 0.60949317935923603 * rot_sin + 0.78478482326597832;
1555 1000 : hue_matrix[2][2] = 0.21521517673402187 * rot_cos - 0.03214291016961022 * rot_sin + 0.78478482326597832;
1556 :
1557 : #ifdef _DEBUG
1558 1000 : f_last_hue_matrix = "led";
1559 : #endif
1560 :
1561 1000 : return *this * hue_matrix;
1562 : }
1563 :
1564 6000 : if(fabs(f_luma_red - CRT_LUMA_RED ) < 0.0001
1565 1000 : && fabs(f_luma_green - CRT_LUMA_GREEN) < 0.0001
1566 1000 : && fabs(f_luma_blue - CRT_LUMA_BLUE ) < 0.0001)
1567 : {
1568 : // this matrice make use of the CRT luma
1569 : //
1570 2000 : matrix<T, SIZE> hue_matrix(4, 4);
1571 :
1572 1000 : hue_matrix[0][0] = 0.943571345820976240 * rot_cos + 0.32589470021007605 * rot_sin + 0.056428654178995;
1573 1000 : hue_matrix[0][1] = -0.056428654178995265 * rot_cos + 0.90324496939967125 * rot_sin + 0.056428654178995;
1574 1000 : hue_matrix[0][2] = -0.056428654178995265 * rot_cos - 0.25145556897954369 * rot_sin + 0.056428654178995;
1575 :
1576 1000 : hue_matrix[1][0] = -0.189552583569840000 * rot_cos - 0.98010410586906000 * rot_sin + 0.189552583569860;
1577 1000 : hue_matrix[1][1] = 0.810447416430107000 * rot_cos - 0.40275383667945400 * rot_sin + 0.189552583569860;
1578 1000 : hue_matrix[1][2] = -0.189552583569853000 * rot_cos + 0.17459643251014600 * rot_sin + 0.189552583569860;
1579 :
1580 1000 : hue_matrix[2][0] = -0.754018762251120000 * rot_cos + 0.65420940565900000 * rot_sin + 0.754018762251160;
1581 1000 : hue_matrix[2][1] = -0.754018762251113000 * rot_cos - 0.50049113272020600 * rot_sin + 0.754018762251160;
1582 1000 : hue_matrix[2][2] = 0.245981237748847000 * rot_cos + 0.07685913646939400 * rot_sin + 0.754018762251160;
1583 :
1584 : #ifdef _DEBUG
1585 1000 : f_last_hue_matrix = "crt";
1586 : #endif
1587 :
1588 1000 : return *this * hue_matrix;
1589 : }
1590 :
1591 5000 : if(fabs(f_luma_red - NTSC_LUMA_RED ) < 0.0001
1592 1000 : && fabs(f_luma_green - NTSC_LUMA_GREEN) < 0.0001
1593 1000 : && fabs(f_luma_blue - NTSC_LUMA_BLUE ) < 0.0001)
1594 : {
1595 : // this matrice make use of the NTSC luma
1596 : //
1597 2000 : matrix<T, SIZE> hue_matrix(4, 4);
1598 :
1599 1000 : value_type const c(cos(h));
1600 1000 : value_type const s(sin(h));
1601 :
1602 1000 : hue_matrix[0][0] = 0.97667266520552899 * rot_cos + 0.35888772800180165 * rot_sin + 0.02332733479447109;
1603 1000 : hue_matrix[0][1] = -0.02332733479447109 * rot_cos + 0.93623799719142759 * rot_sin + 0.02332733479447109;
1604 1000 : hue_matrix[0][2] = -0.02332733479447108 * rot_cos - 0.21846254118782418 * rot_sin + 0.02332733479447109;
1605 :
1606 1000 : hue_matrix[1][0] = -0.17753044304672443 * rot_cos - 1.02526720325074270 * rot_sin + 0.17753044304672438;
1607 1000 : hue_matrix[1][1] = 0.82246955695327556 * rot_cos - 0.44791693406111712 * rot_sin + 0.17753044304672441;
1608 1000 : hue_matrix[1][2] = -0.17753044304672441 * rot_cos + 0.12943333512850867 * rot_sin + 0.17753044304672441;
1609 :
1610 1000 : hue_matrix[2][0] = -0.79914222215880441 * rot_cos + 0.66637947524894110 * rot_sin + 0.79914222215880448;
1611 1000 : hue_matrix[2][1] = -0.79914222215880448 * rot_cos - 0.48832106313031034 * rot_sin + 0.79914222215880459;
1612 1000 : hue_matrix[2][2] = 0.20085777784119549 * rot_cos + 0.08902920605931547 * rot_sin + 0.79914222215880459;
1613 :
1614 : #ifdef _DEBUG
1615 1000 : f_last_hue_matrix = "ntsc";
1616 : #endif
1617 :
1618 1000 : return *this * hue_matrix;
1619 : }
1620 :
1621 4000 : if(fabs(f_luma_red - AVERAGE_LUMA_RED ) < 0.0001
1622 1000 : && fabs(f_luma_green - AVERAGE_LUMA_GREEN) < 0.0001
1623 1000 : && fabs(f_luma_blue - AVERAGE_LUMA_BLUE ) < 0.0001)
1624 : {
1625 : // this matrice make use of the average luma
1626 : // (in other words, it makes no luma correction at all)
1627 : //
1628 2000 : matrix<T, SIZE> hue_matrix(4, 4);
1629 :
1630 1000 : value_type const c(cos(h));
1631 1000 : value_type const s(sin(h));
1632 :
1633 1000 : hue_matrix[0][0] = 1.88796748671567113 * rot_cos + 0.76774179094706859 * rot_sin - 0.88796748671567094;
1634 1000 : hue_matrix[0][1] = 0.88796748671567144 * rot_cos + 1.34509206013669466 * rot_sin - 0.88796748671567149;
1635 1000 : hue_matrix[0][2] = 0.88796748671567144 * rot_cos + 0.19039152175744295 * rot_sin - 0.88796748671567149;
1636 :
1637 1000 : hue_matrix[1][0] = -0.27909984885071244 * rot_cos - 2.01889870048836475 * rot_sin + 0.27909984885071226;
1638 1000 : hue_matrix[1][1] = 0.72090015114928749 * rot_cos - 1.44154843129873957 * rot_sin + 0.27909984885071243;
1639 1000 : hue_matrix[1][2] = -0.27909984885071243 * rot_cos - 0.86419816210911379 * rot_sin + 0.27909984885071243;
1640 :
1641 1000 : hue_matrix[2][0] = -1.60886763786495844 * rot_cos + 1.25115690954129627 * rot_sin + 1.60886763786495757;
1642 1000 : hue_matrix[2][1] = -1.60886763786495881 * rot_cos + 0.09645637116204509 * rot_sin + 1.60886763786495868;
1643 1000 : hue_matrix[2][2] = -0.60886763786495889 * rot_cos + 0.67380664035167087 * rot_sin + 1.60886763786495868;
1644 :
1645 : #ifdef _DEBUG
1646 1000 : f_last_hue_matrix = "average";
1647 : #endif
1648 :
1649 1000 : return *this * hue_matrix;
1650 : }
1651 :
1652 : #ifdef _DEBUG
1653 1000 : f_last_hue_matrix = "dynamic";
1654 : #endif
1655 :
1656 : {
1657 :
1658 : // the full computation, it works, it's just slower than using
1659 : // a pre-calculated matrix
1660 :
1661 : // $R_r$ -- rotation around red axis (inverse rotation around X axis)
1662 : //
1663 2000 : matrix<T, SIZE> r_r(4, 4);
1664 1000 : value_type const inv_sqrt_2 = static_cast<value_type>(1) / sqrt(static_cast<value_type>(2));
1665 1000 : r_r[1][1] = inv_sqrt_2;
1666 1000 : r_r[1][2] = inv_sqrt_2;
1667 1000 : r_r[2][1] = -inv_sqrt_2;
1668 1000 : r_r[2][2] = inv_sqrt_2;
1669 :
1670 : //std::cerr << "R_r = " << r_r << "\n";
1671 :
1672 : // $R_g$ -- rotation around green axis (inverse rotation around Y axis)
1673 : //
1674 2000 : matrix<T, SIZE> r_g(4, 4);
1675 1000 : value_type const inv_sqrt_3 = static_cast<value_type>(1) / sqrt(static_cast<value_type>(3));
1676 1000 : value_type const sqrt_2_over_sqrt_3 = sqrt(static_cast<value_type>(2)) / sqrt(static_cast<value_type>(3));
1677 1000 : r_g[0][0] = sqrt_2_over_sqrt_3;
1678 1000 : r_g[0][2] = inv_sqrt_3;
1679 1000 : r_g[2][0] = -inv_sqrt_3;
1680 1000 : r_g[2][2] = sqrt_2_over_sqrt_3;
1681 :
1682 : //std::cerr << "R_g = " << r_g << "\n";
1683 :
1684 : // $R_{rg}$ -- the product or $R_r$ and $R_g$
1685 : //
1686 2000 : matrix<T, SIZE> r_rg(r_r * r_g);
1687 :
1688 : //std::cerr << "R_rg = " << r_rg << "\n";
1689 :
1690 : // Luminance Vector
1691 : //
1692 2000 : matrix<T, SIZE> w(get_luma_vector());
1693 : //w[0][0] = RED_WEIGHT;
1694 : //w[1][0] = GREEN_WEIGHT;
1695 : //w[2][0] = BLUE_WEIGHT;
1696 : //w[3][0] = 0;
1697 :
1698 : //std::cerr << "w = " << w << "\n";
1699 :
1700 2000 : matrix<T, SIZE> l(r_rg * w);
1701 :
1702 : //std::cerr << "l = " << l << "\n";
1703 :
1704 2000 : matrix<T, SIZE> s(4, 4);
1705 1000 : s[0][2] = l[0][0] / l[2][0];
1706 1000 : s[1][2] = l[1][0] / l[2][0];
1707 :
1708 : //std::cerr << "s = " << s << "\n";
1709 :
1710 : //std::cerr << "s / s = " << (s/s) << "\n";
1711 :
1712 : //std::cerr << "M_rg * s = " << (r_rg * s) << "\n";
1713 :
1714 2000 : matrix<T, SIZE> p(r_rg);
1715 1000 : p *= s;
1716 :
1717 : // Rotate blue (rotation around Z axis)
1718 : //
1719 2000 : matrix<T, SIZE> r_b(4, 4);
1720 1000 : r_b[0][0] = rot_cos;
1721 1000 : r_b[0][1] = rot_sin;
1722 1000 : r_b[1][0] = -rot_sin;
1723 1000 : r_b[1][1] = rot_cos;
1724 :
1725 : //std::cerr << "r_b = " << r_b << "\n";
1726 :
1727 : //matrix<T, SIZE> ident(4,4);
1728 : //std::cerr
1729 : // << "---------------------------------------\n"
1730 : // << "r_rg = " << r_rg
1731 : // << "\nr_g * r_r = " << (r_g * r_r)
1732 : // << "\nr_rg^-1 = " << (ident/r_rg)
1733 : // << "\n(1/r_g/r_r) = " << (ident/r_g/r_r) << "\n"
1734 : // << "\np = " << p << "\n"
1735 : // << "\np^-1 = " << (ident/p) << "\n"
1736 : // << "---------------------------------------\n";
1737 :
1738 : // $H = R_r R_g S R_b S^{-1} R_g^{-1} R_r^{-1}$
1739 : //
1740 1000 : return *this * p * r_b / p;
1741 :
1742 : // others to test further
1743 : //return r_rg * s * r_b / s / r_rg;
1744 : //return r_rg * s * r_b / s / r_g / r_r;
1745 : //return r_r * r_g * r_b / r_g / r_r;
1746 : }
1747 : }
1748 :
1749 : #ifdef _DEBUG
1750 6000 : std::string get_last_hue_matrix()
1751 : {
1752 6000 : return f_last_hue_matrix;
1753 : }
1754 : #endif
1755 :
1756 : /** \brief Retrieve the current luma vector.
1757 : *
1758 : * By default the luma vector is set to the HDTV weights.
1759 : * This may not be desirable, though. If you would prefer to
1760 : * use NTSC (?!) or some other weights, call the
1761 : * set_luma_vector() function with the correct weights.
1762 : *
1763 : * \note
1764 : * This is often referenced as the luminance, which is not quite
1765 : * correct. What we offer here are luma weights. See:
1766 : * https://en.wikipedia.org/wiki/Luma_%28video%29 for additional
1767 : * information.
1768 : *
1769 : * You can access to the red, green, and blue weights as:
1770 : *
1771 : * \code
1772 : * matrix<T, SIZE> luma = m.get_luma_vector();
1773 : * matrix::value_type red = luma[0][0];
1774 : * matrix::value_type green = luma[1][0];
1775 : * matrix::value_type blue = luma[2][0];
1776 : * \endcode
1777 : *
1778 : * This matrix can directly be used against a 4x4 matrix.
1779 : *
1780 : * The 4th parameter is set to zero.
1781 : *
1782 : * \return A 4x1 matrix with the luma weights.
1783 : *
1784 : * \sa set_luma_vector()
1785 : */
1786 7000 : matrix<T, SIZE> get_luma_vector() const
1787 : {
1788 7000 : matrix<T, SIZE> luma(4, 1);
1789 7000 : luma[0][0] = f_luma_red;
1790 7000 : luma[1][0] = f_luma_green;
1791 7000 : luma[2][0] = f_luma_blue;
1792 7000 : return luma;
1793 : }
1794 :
1795 : /** \brief Change the luma vector.
1796 : *
1797 : * This function seets the luminance vector to the three weights
1798 : * you are passing here.
1799 : *
1800 : * The current luminance vector can be retrieved as a 4x1 matrix using
1801 : * the get_luma_vector().
1802 : *
1803 : * The recommended values are the following predefined weights:
1804 : *
1805 : * \li HDTV_RED_WEIGHT, HDTV_GREEN_WEIGHT, HDTV_BLUE_WEIGHT
1806 : * \li LED_RED_WEIGHT, LED_GREEN_WEIGHT, LED_BLUE_WEIGHT
1807 : * \li CRT_RED_WEIGHT, CRT_GREEN_WEIGHT, CRT_BLUE_WEIGHT
1808 : * \li NTSC_RED_WEIGHT, NTSC_GREEN_WEIGHT, NTSC_BLUE_WEIGHT
1809 : * \li AVERAGE_RED_WEIGHT, AVERAGE_GREEN_WEIGHT, AVERAGE_BLUE_WEIGHT
1810 : *
1811 : * The HDTV weights are those used by default if you do not call the
1812 : * set_luminance_vector() with different or even your own weights.
1813 : *
1814 : * \param[in] red_weight The luma red weight.
1815 : * \param[in] green_weight The luma green weight.
1816 : * \param[in] blue_weight The luma blue weight.
1817 : */
1818 12 : void set_luma_vector(value_type red_weight, value_type green_weight, value_type blue_weight)
1819 : {
1820 : // save the user defined weights
1821 : //
1822 12 : f_luma_red = red_weight;
1823 12 : f_luma_green = green_weight;
1824 12 : f_luma_blue = blue_weight;
1825 12 : }
1826 :
1827 : std::string to_string() const
1828 : {
1829 : std::stringstream s;
1830 :
1831 : s.precision(17);
1832 :
1833 : s << "[";
1834 : for(size_type j(0); j < f_rows; ++j)
1835 : {
1836 : s << std::endl << " [";
1837 : if(f_columns > 0)
1838 : {
1839 : s << f_vector[0 + j * f_columns];
1840 : for(size_type i(1); i < f_columns; ++i)
1841 : {
1842 : s << ", " << f_vector[i + j * f_columns];
1843 : }
1844 : }
1845 : s << "]";
1846 : }
1847 : s << std::endl << "]" << std::endl;
1848 :
1849 : return s.str();
1850 : }
1851 :
1852 : private:
1853 : friend element_ref;
1854 : friend row_ref;
1855 : friend const_row_ref;
1856 :
1857 : size_type f_rows = 0;
1858 : size_type f_columns = 0;
1859 : std::vector<T> f_vector = std::vector<T>();
1860 : value_type f_luma_red = HDTV_LUMA_RED;
1861 : value_type f_luma_green = HDTV_LUMA_GREEN;
1862 : value_type f_luma_blue = HDTV_LUMA_BLUE;
1863 : #ifdef _DEBUG
1864 : mutable std::string f_last_hue_matrix = std::string();
1865 : #endif
1866 : };
1867 :
1868 :
1869 : } // namespace snap
1870 :
1871 :
1872 :
1873 : /** \brief Output a matrix to a basic_ostream.
1874 : *
1875 : * This function allows one to print out a matrix. The function attempts
1876 : * to properly format the matrix inorder o mak readable.
1877 : *
1878 : * \param[in] out The output stream where the matrix gets written.
1879 : * \param[in] matrix The actual matrix that is to be printed.
1880 : *
1881 : * \return A reference to the basic_ostream object.
1882 : */
1883 : template<class E, class S, class T, class SIZE>
1884 : std::basic_ostream<E, S> & operator << (std::basic_ostream<E, S> & out, snap::matrix<T, SIZE> const & m)
1885 : {
1886 : // write to a string buffer first
1887 : //
1888 : std::basic_ostringstream<E, S, std::allocator<E> > s;
1889 :
1890 : // setup the string output like the out stream
1891 : //
1892 : s.flags(out.flags());
1893 : s.imbue(out.getloc());
1894 : s.precision(out.precision());
1895 :
1896 : // output the matrix
1897 : //
1898 : s << "[";
1899 : for(typename snap::matrix<T, SIZE>::size_type j(0); j < m.rows(); ++j)
1900 : {
1901 : s << std::endl << " [";
1902 : if(m.columns() > 0)
1903 : {
1904 : s << static_cast<T>(m[j][0]);
1905 : for(typename snap::matrix<T, SIZE>::size_type i(1); i < m.columns(); ++i)
1906 : {
1907 : s << ", " << static_cast<T>(m[j][i]);
1908 : }
1909 : }
1910 : s << "]";
1911 : }
1912 : s << std::endl << "]" << std::endl;
1913 :
1914 : // buffer is ready, display in output in one go
1915 : //
1916 : return out << s.str();
1917 : }
1918 :
1919 :
1920 : // file in ve folder (matrix.c)
1921 : // http://www.graficaobscura.com/matrix/index.html
1922 : // https://ncalculators.com/matrix/3x3-matrix-multiplication-calculator.htm
1923 : // /home/alexis/m2osw/sources/freeware/libx3d/utilities/src/fast_matrix.c++
1924 :
1925 : // vim: ts=4 sw=4 et
|