MeeGo Camera VideoSurface manipulation
This article explains how to get the Qt camera example to work on MeeGo, and then how to manipulate the displayed video before it is written to the VideoSurface.
Article Metadata
Code Example
Tested with
Compatibility
Platform Security
Article
Contents |
Introduction
The QCamera example (at least in Qt 4.7) does not work on MeeGo, as discussed in the bug QTMOBILITY-1963. This article first explains why the example does not work, how you can modify it to enable support for the (required) UYVY color space, and provides a helper class you can use in your own projects.
The article then shows how you can manipulate the image prior to display. The example is relatively simple, but is sufficient to illustrate other possibilities for augmenting the video view in real-time:
- Switch color components to help color-blind people
- Add clothes on people without going to shop
- We can merge visible reality with games
UYVY color space
The reason why QCamera example is not working on MeeGo is that MeeGo uses the UYVY color space, and this is not supported by QImage. Therefore at first you will need to do following changes to your class that subclasses QAbstractVideoSurface:
- In function supportedPixelFormats you will need to add following line:
-
<< QVideoFrame::Format_UYVY
-
- In start function you will need to add this code snippet:
- Lastly we will need to handle UYVY frames ourselves. For that I have written helper class that you init with UYVY frame and get RGB image data:
-
if (m_frame.pixelFormat() == QVideoFrame::Format_UYVY)
{
uyvy2rgb.initData(m_frame);
image = new QImage(uyvy2rgb.bits(),
uyvy2rgb.width(),
uyvy2rgb.height(),
uyvy2rgb.width() * 4,
m_imageFormat);
}
-
I am supplying source code for this class below but let's analyze UYVY to RGB conversion algorithm. Based on Harmattan documentation and wikipedia article on YUV we can find out that Nokia is referring to Y'UV422. Therefore 4 bytes in UYVY represents 2 pixels and when converted to RGB they will occupy 8 bytes (that's because RGB pixel takes 4 bytes for performance/simplicity reasons). In wikipedia article we can find algorithm for converting from UYVY color space to RGB color space:
C = Y - 16
D = U - 128
E = V - 128
R = clamp((298 * C + 409 * E + 128) >> 8)
G = clamp((298 * C - 100 * D - 208 * E + 128) >> 8)
B = clamp((298 * C + 516 * E + 128) >> 8)
Algorithm looks quite simple and here we can do our first optimization. What we know that we have 256 different C, D and E values - therefore we can pre-calculate multiplications before using them. Multiplications are more expensive than additions and subtractions in CPU and we only need to about 4kb of memory to keep pre-calculated multiplications. If we care about performance even more we can hardcode pre-calculated arrays into the code. Here is code for helper class:
uyvy2rgb.h:
#ifndef UYVY2RGB_H
#define UYVY2RGB_H
#include <QVideoFrame>
class UYVY2RGB : public QObject
{
Q_OBJECT
public:
UYVY2RGB();
~UYVY2RGB();
public:
void initData(const QVideoFrame &source);
unsigned char* bits();
int width();
int height();
private:
int *c298, *e409, *d100, *e208, *d516;
unsigned char *m_bits;
int m_width;
int m_height;
};
#endif // UYVY2RGB_H
uyvy2rgb.cpp:
#include "uyvy2rgb.h"
#include <QtCore/qmath.h>
UYVY2RGB::UYVY2RGB() : m_bits(NULL), m_width(0), m_height(0)
{
int i;
c298 = new int[256];
for (i = 0; i < 256; i++)
c298[i] = (i-16)*298;
e409 = new int[256];
e208 = new int[256];
for (i = 0; i < 256; i++) {
e409[i] = (i-128)*409;
e208[i] = (i-128)*208;
}
d100 = new int[256];
d516 = new int[256];
for (i = 0; i < 256; i++) {
d100[i] = (i-128)*100;
d516[i] = (i-128)*516;
}
}
UYVY2RGB::~UYVY2RGB()
{
delete c298;
delete e409;
delete d100;
delete e208;
delete d516;
}
unsigned char* UYVY2RGB::bits()
{
return m_bits;
}
int UYVY2RGB::width()
{
return m_width;
}
int UYVY2RGB::height()
{
return m_height;
}
void UYVY2RGB::initData(const QVideoFrame &source)
{
if (m_bits == NULL || source.width() != m_width || source.height() != m_height)
{
if (m_bits != NULL)
delete[] m_bits;
m_width = source.width();
m_height = source.height();
m_bits = new unsigned char[m_width * m_height * 4];
}
int steps = (m_width/2)*m_height;
unsigned char *dst = m_bits;
const unsigned char *src = source.bits();
for (int i = 0; i < steps; ++i)
{
int d = *src ; // u
int y1 = *(src+1); // y
int e = *(src+2); // v
int y2 = *(src+3); // y
int rpart = e409[e] + 128;
int gpart = d100[d] + e208[e] + 128;
int bpart = d516[d] + 128;
int r = (c298[y1] + rpart) >> 8;
int g = (c298[y1] - gpart) >> 8;
int b = (c298[y1] + bpart) >> 8;
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
*dst = b;
*(dst+1) = g;
*(dst+2) = r;
*(dst+3) = 0;
dst+=4;
r = (c298[y2] + rpart) >> 8;
g = (c298[y2] - gpart) >> 8;
b = (c298[y2] + bpart) >> 8;
r = r < 0 ? 0 : (r > 255 ? 255 : r);
g = g < 0 ? 0 : (g > 255 ? 255 : g);
b = b < 0 ? 0 : (b > 255 ? 255 : b);
*dst = b;
*(dst+1) = g;
*(dst+2) = r;
*(dst+3) = 0;
dst+=4;
src+=4;
}
}
Download sample application from here: File:Camera uyvy.zip
Here is sample image from camera:
View image manipulation
After this view image manipulation is easy part. E.g. for swapping RGB colors we can get image data using bits() function and swap data in place. QImage actually has rgbSwapped() function we will use in this case. Drawing image on top of view is as simple as calling drawImage() on painter object. The only thing we should be concerned about is that images should support transparency. I have drawn some images using Inkscape and exported them to PNG because PNG supports transparency (we can actually use SVG files if we need them to be resizable but that falls out of scope of this article). Here is sample code I have used to swap colors and draw image on top of view:
Download sample application from here: File:Camera pic.zip
Here is sample image you can get using this application (notice box color - it is the same box as above):
Summary
This article show how to display camera's viewport in Meego Harmattan, and how to manipulate it before displaying it for users.



Contents
Hamishwillee - Split into 2 articles?
Hi daliusd
This looks pretty cool - particularly the live edge detection video.
As a reader I think you've covered a lot of good material here, but that the first and following sections do not match too well. You would be better off splitting the first section into a topic on "Supporting UYVY as a Qt video frame image format", possibly also supporting how you would cover it in QML. This would explain the fact that you're working around the bug in the example as a context, but it would be the image format support that would be the main focus.
The second section would then cover image manipulation and luma edge detection as a separate topic. I think you'd need to expand a bit on how you write your image to the screen (I'm assuming that video just works by writing frames you're fed by Video to some surface in its painter method?).
YOu don't lose anything by splitting them because they all form part of your "competition entry".
I found the explanation at the beginning of "Luma and edge detection" a bit confusing - I can see that you're precalculating square roots, but I though you only needed to do this for Symbian - what's going on?
Remember also that the focus of this competition is Qt with Symbian. While MeeGo is something we want to support too, from a competition point of view, making it as "Symbian" as possible is a good idea.
Regards
Hamishhamishwillee 07:46, 11 May 2012 (EEST)
Daliusd -
Thank you for your notes. I was thinking myself that I should split this into two articles. I will add Symbian to this as well.daliusd 08:20, 11 May 2012 (EEST)
Daliusd - QML
BTW, about QML: I think I don't know enough to write how to do that in QML. I suspect that we have to subclass QDeclarativeItem and override paint() method. However I guess there might be a little bit more to make that efficient.daliusd 23:37, 13 May 2012 (EEST)
Hamishwillee - Thanks for the split!
Makes more sense now thanks. I agree re QML, and I don't think would be easy. Ignore that.
Hamishwillee - Subedited again
Hi
I've subedited the introduction and abstract to make it clear what this article actually provides - a fix for the example and an explanation of how to edit the video surface. Please check it is still accurate.
I know this does remove some of your "friendly approach" - my opinion is that readers are short on time. They want to know what the article offers, and to a lesser extent they are also interested in "options" that this might provide. IMO they don't particularly care that you asked people how to do this and they couldn't help. Does that make sense. Its your article so I hope so!
I am slightly tempted to rename article title to something which better captures that this is 90% about supporting the UYVY color space. What do you think?
Cheerio
Hhamishwillee 09:58, 17 May 2012 (EEST)
Daliusd -
Hi,
In my opinion your changes are good. As developer I usually care about code itself so less text is OK.
I'm OK with renaming but is it possible to keep this link as well (because I have linked to this article in bug report).daliusd 09:02, 18 May 2012 (EEST)
Jaaura - Renaming (i.e. move action on our wiki) does add automatic redirect
So, no worry, the old link will automatically point/redirect to the new renamed article, and no existing links will break. "Special:MovePage: Using this action will rename a page and moves all of its history to the new name. The old title will become a redirect page to the new title."jaaura 16:00, 18 May 2012 (EEST)
Hamishwillee - Thanks - got a preference for the name?
Something like "Supporting the UYVY color space in Qt Camera and manipulating the VideoSurface" or perhaps just
"Supporting the UYVY color space in Qt Camera"hamishwillee 08:21, 25 May 2012 (EEST)