1 /***************************************************************************
2 * Copyright (C) 2010 by Simon Andreas Eugster (simon.eu@gmail.com) *
3 * This file is part of kdenlive. See www.kdenlive.org. *
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 ***************************************************************************/
15 The basis matrix for converting RGB to YUV is
16 (in this example for calculating the YUV value of red):
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
24 The resulting YUV value is then drawn on the circle
25 using U and V as coordinate values.
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.
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
38 The basis matrix for converting RGB to YPbPr is:
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
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
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.
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
66 http://www.poynton.com/ColorFAQ.html
67 http://de.wikipedia.org/wiki/Vektorskop
68 http://www.elektroniktutor.de/techno/vektskop.html
74 #include "vectorscopegenerator.h"
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;
80 const float VectorscopeGenerator::scaling = 1/.7;
83 Input point is on [-1,1]², 0 being at the center,
84 and positive directions are →top/→right.
86 Maps to the coordinates used in QImages with the 0 point
87 at the top left corner.
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.
112 QPoint VectorscopeGenerator::mapToCircle(const QSize &targetSize, const QPointF &point) const
114 return QPoint( (targetSize.width() -1) * (point.x()+1)/2,
115 (targetSize.height()-1) * (1 - (point.y()+1)/2) );
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
123 if (vectorscopeSize.width() <= 0 || vectorscopeSize.height() <= 0 || image.width() <= 0 || image.height() <= 0) {
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));
133 const uchar *bits = image.bits();
136 double dy, dr, dg, db, dmax;
140 int bpp = image.depth() / 8;
142 const int stepsize = bpp * accelFactor;
144 // Just an average for the number of image pixels per scope pixel.
145 // 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
146 double avgPxPerPx = (double) bpp*(image.bytesPerLine()*image.height())/scope.size().width()/scope.size().height()/accelFactor;
148 for (int i = 0; i < (image.bytesPerLine()*image.height()); i+= stepsize) {
149 QRgb *col = (QRgb *) bits;
155 switch (colorSpace) {
156 case VectorscopeGenerator::ColorSpace_YUV:
157 // y = (double) 0.001173 * r +0.002302 * g +0.0004471* b;
158 u = (double) -0.0005781* r -0.001135 * g +0.001713 * b;
159 v = (double) 0.002411 * r -0.002019 * g -0.0003921* b;
161 case VectorscopeGenerator::ColorSpace_YPbPr:
163 // y = (double) 0.001173 * r +0.002302 * g +0.0004471* b;
164 u = (double) -0.0006671* r -0.001299 * g +0.0019608* b;
165 v = (double) 0.001961 * r -0.001642 * g -0.0003189* b;
170 pt = mapToCircle(vectorscopeSize, QPointF(SCALING*gain*u, SCALING*gain*v));
172 if (pt.x() >= scope.width() || pt.x() < 0
173 || pt.y() >= scope.height() || pt.y() < 0) {
174 // Point lies outside (because of scaling), don't plot it
178 // Draw the pixel using the chosen draw mode.
182 dy = 128; // Default Y value. Lower = darker.
184 // Calculate the RGB values from YUV/YPbPr
185 switch (colorSpace) {
186 case VectorscopeGenerator::ColorSpace_YUV:
188 dg = dy - 100.6*u - 148*v;
191 case VectorscopeGenerator::ColorSpace_YPbPr:
194 dg = dy - 87.75*u - 182*v;
203 if (dr > 255) dr = 255;
204 if (dg > 255) dg = 255;
205 if (db > 255) db = 255;
207 scope.setPixel(pt, qRgba(dr, dg, db, 255));
210 case PaintMode_Chroma:
211 dy = 200; // Default Y value. Lower = darker.
213 // Calculate the RGB values from YUV/YPbPr
214 switch (colorSpace) {
215 case VectorscopeGenerator::ColorSpace_YUV:
217 dg = dy - 100.6*u - 148*v;
220 case VectorscopeGenerator::ColorSpace_YPbPr:
223 dg = dy - 87.75*u - 182*v;
228 // Scale the RGB values back to max 255
230 if (dg > dmax) dmax = dg;
231 if (db > dmax) dmax = db;
238 scope.setPixel(pt, qRgba(dr, dg, db, 255));
240 case PaintMode_Original:
241 scope.setPixel(pt, *col);
243 case PaintMode_Green:
244 px = scope.pixel(pt);
245 scope.setPixel(pt, qRgba(qRed(px)+(255-qRed(px))/(3*avgPxPerPx), qGreen(px)+20*(255-qGreen(px))/(avgPxPerPx),
246 qBlue(px)+(255-qBlue(px))/(avgPxPerPx), qAlpha(px)+(255-qAlpha(px))/(avgPxPerPx)));
248 case PaintMode_Green2:
249 px = scope.pixel(pt);
250 scope.setPixel(pt, qRgba(qRed(px)+ceil((255-(float)qRed(px))/(4*avgPxPerPx)), 255,
251 qBlue(px)+ceil((255-(float)qBlue(px))/(avgPxPerPx)), qAlpha(px)+ceil((255-(float)qAlpha(px))/(avgPxPerPx))));
253 case PaintMode_Black:
254 px = scope.pixel(pt);
255 scope.setPixel(pt, qRgba(0,0,0, qAlpha(px)+(255-qAlpha(px))/20));