]> git.sesse.net Git - kdenlive/blob - src/colorcorrection/vectorscopegenerator.cpp
Fix a couple of compile warnings because of unused and uninitialized variables.
[kdenlive] / src / colorcorrection / vectorscopegenerator.cpp
1 /***************************************************************************
2  *   Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com)      *
3  *   This file is part of kdenlive. See www.kdenlive.org.                  *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  ***************************************************************************/
10
11 /**
12
13   Vectorscope.
14
15   The basis matrix for converting RGB to YUV is
16   (in this example for calculating the YUV value of red):
17
18 mRgb2Yuv =                       r =
19
20    0.29900   0.58700   0.11400     1.00000
21   -0.14741  -0.28939   0.43680  x  0.00000
22    0.61478  -0.51480  -0.09998     0.00000
23
24   The resulting YUV value is then drawn on the circle
25   using U and V as coordinate values.
26
27   The maximum length of such an UV vector is reached
28   for the colors Red and Cyan: 0.632.
29   To make optimal use of space in the circle, this value
30   can be used for scaling.
31
32   As we are dealing with RGB values in a range of {0,...,255}
33   and the conversion values are made for [0,1], we already
34   divide the conversion values by 255 previously, e.g. in
35   GNU octave.
36
37
38   The basis matrix for converting RGB to YPbPr is:
39
40 mRgb2YPbPr =                        r =
41
42    0.299000   0.587000   0.114000     1.0000
43   -0.168736  -0.331264   0.500000  x  0.0000
44    0.500000  -0.418688  -0.081312     0.0000
45
46
47
48    Note that YUV and YPbPr are not the same.
49    * YUV is used for analog transfer of PAL/NTSC
50    * YPbPr is used for analog transfer of YCbCr sources
51      like DVD/DVB.
52    It does _not_ matter for the Vectorscope what color space
53    the input video is; The used color space is just a tool
54    to visualize the hue. The difference is merely that the
55    scope looks slightly different when choosing the respectively
56    other color space; The way the Vectorscope works stays the same.
57
58    YCbCr is basically YPbPr for digital use (it is defined
59    on a range of {16,219} for Y and {16,240} for Cb/Cr
60    components).
61
62
63
64
65   See also:
66     http://www.poynton.com/ColorFAQ.html
67     http://de.wikipedia.org/wiki/Vektorskop
68     http://www.elektroniktutor.de/techno/vektskop.html
69
70  */
71
72 #include <math.h>
73 #include <QImage>
74 #include "vectorscopegenerator.h"
75
76 // The maximum distance from the center for any RGB color is 0.63, so
77 // no need to make the circle bigger than required.
78 const float SCALING = 1/.7;
79
80 const float VectorscopeGenerator::scaling = 1/.7;
81
82 /**
83   Input point is on [-1,1]², 0 being at the center,
84   and positive directions are →top/→right.
85
86   Maps to the coordinates used in QImages with the 0 point
87   at the top left corner.
88
89  -1          +1
90 +1+-----------+
91   |    +      |
92   |  --0++    |
93   |    -      |
94 -1+-----------+
95      vvv
96    mapped to
97       v
98   0      x
99  0+------+
100   |0++   |
101   |-     |
102   |-     |
103  y+------+
104
105   With y:
106   1. Scale from [-1,1] to [0,1] with                    y01 := (y+1)/2
107   2. Invert (Orientation of the y axis changes) with    y10 := 1-y01
108   3. Scale from [1,0] to [height-1,0] with              yy  := (height-1) * y10
109   x does not need to be inverted.
110
111  */
112 QPoint VectorscopeGenerator::mapToCircle(const QSize &targetSize, const QPointF &point) const
113 {
114     return QPoint( (targetSize.width() -1) *      (point.x()+1)/2,
115                    (targetSize.height()-1) * (1 - (point.y()+1)/2) );
116 }
117
118 QImage VectorscopeGenerator::calculateVectorscope(const QSize &vectorscopeSize, const QImage &image, const float &gain,
119                                                   const VectorscopeGenerator::PaintMode &paintMode,
120                                                   const VectorscopeGenerator::ColorSpace &colorSpace,
121                                                   const bool &, const uint &accelFactor) const
122 {
123     if (vectorscopeSize.width() <= 0 || vectorscopeSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
124         // Invalid size
125         return QImage();
126     }
127
128     // Prepare the vectorscope data
129     const int cw = (vectorscopeSize.width() < vectorscopeSize.height()) ? vectorscopeSize.width() : vectorscopeSize.height();
130     QImage scope = QImage(cw, cw, QImage::Format_ARGB32);
131     scope.fill(qRgba(0,0,0,0));
132
133     const uchar *bits = image.bits();
134
135     int r,g,b;
136     double dy, dr, dg, db, dmax;
137     double /*y,*/ u, v;
138     QPoint pt;
139     QRgb px;
140
141     const int stepsize = 4*accelFactor;
142
143     // Just an average for the number of image pixels per scope pixel.
144     // NOTE: byteCount() has to be replaced by (img.bytesPerLine()*img.height()) for Qt 4.5 to compile, see: http://doc.trolltech.org/4.6/qimage.html#bytesPerLine
145     double avgPxPerPx = (double) 4*(image.bytesPerLine()*image.height())/scope.size().width()/scope.size().height()/accelFactor;
146
147     for (int i = 0; i < (image.bytesPerLine()*image.height()); i+= stepsize) {
148         QRgb *col = (QRgb *) bits;
149
150         r = qRed(*col);
151         g = qGreen(*col);
152         b = qBlue(*col);
153
154         switch (colorSpace) {
155         case VectorscopeGenerator::ColorSpace_YUV:
156 //             y = (double)  0.001173 * r +0.002302 * g +0.0004471* b;
157             u = (double) -0.0005781* r -0.001135 * g +0.001713 * b;
158             v = (double)  0.002411 * r -0.002019 * g -0.0003921* b;
159             break;
160         case VectorscopeGenerator::ColorSpace_YPbPr:
161         default:
162 //             y = (double)  0.001173 * r +0.002302 * g +0.0004471* b;
163             u = (double) -0.0006671* r -0.001299 * g +0.0019608* b;
164             v = (double)  0.001961 * r -0.001642 * g -0.0003189* b;
165             break;
166         }
167
168
169         pt = mapToCircle(vectorscopeSize, QPointF(SCALING*gain*u, SCALING*gain*v));
170
171         if (pt.x() >= scope.width() || pt.x() < 0
172             || pt.y() >= scope.height() || pt.y() < 0) {
173             // Point lies outside (because of scaling), don't plot it
174
175         } else {
176
177             // Draw the pixel using the chosen draw mode.
178             switch (paintMode) {
179             case PaintMode_YUV:
180                 // see yuvColorWheel
181                 dy = 128; // Default Y value. Lower = darker.
182
183                 // Calculate the RGB values from YUV/YPbPr
184                 switch (colorSpace) {
185                 case VectorscopeGenerator::ColorSpace_YUV:
186                     dr = dy + 290.8*v;
187                     dg = dy - 100.6*u - 148*v;
188                     db = dy + 517.2*u;
189                     break;
190                 case VectorscopeGenerator::ColorSpace_YPbPr:
191                 default:
192                     dr = dy + 357.5*v;
193                     dg = dy - 87.75*u - 182*v;
194                     db = dy + 451.9*u;
195                     break;
196                 }
197
198
199                 if (dr < 0) dr = 0;
200                 if (dg < 0) dg = 0;
201                 if (db < 0) db = 0;
202                 if (dr > 255) dr = 255;
203                 if (dg > 255) dg = 255;
204                 if (db > 255) db = 255;
205
206                 scope.setPixel(pt, qRgba(dr, dg, db, 255));
207                 break;
208
209             case PaintMode_Chroma:
210                 dy = 200; // Default Y value. Lower = darker.
211
212                 // Calculate the RGB values from YUV/YPbPr
213                 switch (colorSpace) {
214                 case VectorscopeGenerator::ColorSpace_YUV:
215                     dr = dy + 290.8*v;
216                     dg = dy - 100.6*u - 148*v;
217                     db = dy + 517.2*u;
218                     break;
219                 case VectorscopeGenerator::ColorSpace_YPbPr:
220                 default:
221                     dr = dy + 357.5*v;
222                     dg = dy - 87.75*u - 182*v;
223                     db = dy + 451.9*u;
224                     break;
225                 }
226
227                 // Scale the RGB values back to max 255
228                 dmax = dr;
229                 if (dg > dmax) dmax = dg;
230                 if (db > dmax) dmax = db;
231                 dmax = 255/dmax;
232
233                 dr *= dmax;
234                 dg *= dmax;
235                 db *= dmax;
236
237                 scope.setPixel(pt, qRgba(dr, dg, db, 255));
238                 break;
239             case PaintMode_Original:
240                 scope.setPixel(pt, *col);
241                 break;
242             case PaintMode_Green:
243                 px = scope.pixel(pt);
244                 scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/(3*avgPxPerPx), qGreen(px)+20*(255-qGreen(px))/(avgPxPerPx),
245                                          qBlue(px)+(255-qBlue(px))/(avgPxPerPx), qAlpha(px)+(255-qAlpha(px))/(avgPxPerPx)));
246                 break;
247             case PaintMode_Green2:
248                 px = scope.pixel(pt);
249                 scope.setPixel(pt, qRgba(qRed(px)+ceil((255-(float)qRed(px))/(4*avgPxPerPx)), 255,
250                                          qBlue(px)+ceil((255-(float)qBlue(px))/(avgPxPerPx)), qAlpha(px)+ceil((255-(float)qAlpha(px))/(avgPxPerPx))));
251                 break;
252             case PaintMode_Black:
253                 px = scope.pixel(pt);
254                 scope.setPixel(pt, qRgba(0,0,0, qAlpha(px)+(255-qAlpha(px))/20));
255                 break;
256             }
257         }
258
259         bits += stepsize;
260     }
261     return scope;
262 }