Shifting the hue of a QImage / QPixmap - qt

I suppose this is more of a graphics manipulation question in general, but I'd like to accomplish this in Qt (c++). If I have an image - let's just say a circle on a transparent background - of a light gray color, is there any built-in functionality in Qt to shift the hue / saturation to color it?
I suppose I could go pixel by pixel, and modifying the rgb mathematically - add x to r, g, and b, to get a desired color, but there must be a better way than modifying every single pixel.
Nothing in the Qt Docs goes this far into image manipulation, just altering alpha and colors. Should I look into an open source library (the end result will likely be an independently sold software)? If so, are there any recommendations? Or is there a secret function hidden in the Qt docs that can accomplish without the need of outside libraries / crazy algorithms?

A possible course of action:
Load your image as a QImage
Do a QImage
QImage::convertToFormat(QImage::Format_Indexed8) to get a indexed
image with a color table
Get color table values with QRgb
QImage::color ( int i ) const
Manipulate the colors with QColor
( QRgb color ) and the other QColor methods
Alter the color
table with void QImage::setColor ( int index, QRgb colorValue )

You've got a few options, none of which are built-in Qt solutions, unfortunately.
Use OpenMP or some other concurrency library to take advantage of
SSE/SSE2.
Use the GPU via OpenGL, DirectX or various GPGPU programming techniques.
(the solution I chose) Use OpenCL to take advantage of both CPU and GPU concurency without all the "fun" of shader programming.
Spawn off a pool of thread workers and have each of them handle a chunk of the image.
My application does lots of image filtering and I was honestly shocked at the performance gain that was to be had after I ported filters to OpenCL.
At 1936×2592 a brightness modification filter was running in 175ms in native C++ code iterating through each pixel within a QImage.
After porting to OpenCL that went down to 15ms. Of course, the data had to be pulled out of the QImage and reinserted but that overhead was nothing compared to the OpenCL performance gains.
Best of luck on your coding adventures!

In this case it sounds like you might be able to get away with using a QGraphicsColorizeEffect.
Instead of shifting the hue, this method will set it. Although it seems QGraphicsEffects can only be applied to QWidgets and QGraphicsItems, which can be inconvenient.
Another option - in this case - would be to use a QPainter's CompositionMode.

Just use QImage::pixelColor and QImage::setPixelColor (available since Qt 5.6).

Related

Texture taken from Item: can I make its filtering be gamma-correct?

If this was a texture that I created, I'd simply make its internalFormat be GL_SRGB. But I'm passing a Qt Quick Item foo into my custom QQuickFramebufferObject GL code, where I take foo->textureProvider()->texture() and use that texture to render.
So can I make the filtering of the texture (when bilinearly sampling it) be gamma-correct?
Note: I'm aware I could implement manual bilinear filtering with 4 texture taps and lerping, but that would hurt performance somewhat, so I'm looking for a better way.
Or I could blit from the Qt Quick texture into a GL_SRGB texture of my own, then use that texture, but that's more complex and would need to happen every time the source texture is updated, hurting performance (and RAM usage).
I've searched google for hooks Qt may provide to configure this, but found nothing except QQuickTextureFactory which, however, does not solve my problem, at least AFAICS.
I need to support OpenGL ES 2.0.
Note: I'm aware I could implement manual bilinear filtering with 4 texture taps and lerping, but that would hurt performance somewhat, so I'm looking for a better way.
Well, from the filtered result color, there is simply no way to get back the original colors used as input, even if you know the interpolation factors.
Or I could blit from the Qt Quick texture into a GL_SRGB texture of my own, then use that texture, but that's more complex and would need to happen every time the source texture is updated, hurting performance (and RAM usage).
A more efficient variation of this strategy would be creating a second view onto the texture data, with an SRGB format (see GL_ARB_texture_view extension, core since GL 4.3), which completely avoids the copy and additional RAM usage.

Render YUV in JavaFX

I need to render a yuyv422 stream in JavaFX with minimum latency. If I convert it to RGB, I can use an ImageView with a WritableImage with a PixelFormat instance, and it works, but the RGB conversion consumes a lot of CPU, specially with high resolutions. I saw this exact feature request
https://bugs.openjdk.java.net/browse/JDK-8091933
but seems it will not be implemented in Java 9. And if it does, I wonder if it won't introduce latency or demand too much CPU. Is there another way using JavaFX?
In General:
Image processing is always expensive, this is why Vectorization or Hardware Acceleration is used for these tasks. Simple looping through an Image with just one thread is already really slow, especially in java. On top of that people tend to use Color objects for color modifications which is tremendously slow.
Pure Java:
If you want to keep your code in pure Java. You should check which internal format is used for the WriteableImage by calling:
myImage.getPixelWriter().getPixelFormat().getType()
If the internal format isn't RGB adapt your color conversion to the given format to avoid double conversion.
Additionally make sure that your code is optimized as much as possible:
-Don't use any objects except arrays
-Minimize the use of local variables
You can also try to multithread the conversion process via parallel loops.
JNI:
Moving away from Java opens up a lot of possibilities. There are several platform independent libraries for converting YUV to RGB and back:
OpenCV:
Easy to use and coming already with an java API:
byte[] myYuvImage = null; //your image here
byte[] myRgbImage = new byte[width * height * 3]; //the output image
Mat yuvMat = new Mat(height, width, CvType.CV_8UC2); //YUV422 should be 2 channel
Mat rgbMat = new Mat(height, width, CvType.CV_8UC3);
yuvMat.put(0,0, myYuvImage);
Imgproc.cvtColor(yuvMat, rgbMat, Imgproc.COLOR_YUV2RGB_Y422);
rgbMat.get(0, 0, myRgbImage);
Intel IPP:
Only available via JNI. You would use ippiRGBToYUV422_8u_C3C2R see RGBToYUV422 for more information.
SwScale as part of FFmpeg:
Only available via JNI. See this answer and adapt the example.
My personal experience is that IPP offers by far the best performance even on AMD machines. However the license it comes with may be free but it prohibits decompiling which might be an not compatible with LGPL libraries.

QGLWidget is slower than QWidget

The problem mainly is determined in the title. I tried out the Qt's example (2dpainting) and noticed, that the same code consumes more CPU power if I try to draw on QGLWidget and less if I try to draw simply on QWidget. I thought that the QGLWidget should be faster. And one more interesting phenomenon: In QGLWidget the antialiasing hint seems to be ignored.
OpenGL version: 3.3.0
So why is that?
Firstly, note this text at the bottom of the documentation that you link to:
The example shows the same painting operations performed at the same
time in a Widget and a GLWidget. The quality and speed of rendering in
the GLWidget depends on the level of support for multisampling and
hardware acceleration that your system's OpenGL driver provides. If
support for either of these is lacking, the driver may fall back on a
software renderer that may trade quality for speed.
Putting that aside, hardware rendering is not always guaranteed to be faster than software rendering; it all depends upon what the renderer is being asked to do.
An example of where software can exceed hardware is if the goal of the item being rendered is constantly changing. So, if you have a drawing program that draws a line being created by the mouse being constantly moved and it is implemented by adding points to a painter path that is drawn every frame, a hardware renderer will be subject to constant pipeline stalls as new points are added to the painter path. Setting up the graphics pipeline from a stall takes time, which is not something a software renderer has to deal with.
In the 2dPainting example you ask about, the helper class, which performs the paint calls, is doing a lot of unnecessary work; saving the painter state; setting the pen / brush; rotating the painter; restoring the brush. All of this is a bigger overhead in hardware than software. To really see hardware rendering outperform software, pre-calculating the objects' positions outside of the render loop (paint function) and then doing nothing put actually rendering in the paint function is likely to display a noticeable difference here.
Finally, regarding anti-aliasing, the documentation that you linked to states: "the QGLWidget will also use anti-aliasing if the required extensions are supported by your system's OpenGL driver"

If Qt uses double buffer to paint, is there any way to get these buffers on Qgraphicsview?

I have explore the web to find any solution without success. If there is a double buffer painting by default, where are these buffers? And how can I 'capture' it and use at QPixmap?
In case I have to edit one item of 100000 , I think it'd must exist a posibility of having an automatic 'background' buffer copy. But I didn't find anything related. So I'm going to do myself.

Can the QPropertyAnimation update interval be increased for ultra-smooth animation?

The new Animation Framework in Qt 4.6+ is based on QTimeLine which has 'void setUpdateInterval(int interval)' public function. Based on QTimeLine as well, QGraphicsItemAnimation could access this function, but the new animation framework classes (e.g. QPropertyAnimation) can't! Is the animation framework locked to the about 60 updates per second which corresponds to a pixel-by-pixel transition of 60 pixels on screen per second only (for QPropertyAnimation animating position property) or is there a way to increase this without reimplementing everything?
I think there are some hardware limitations and also some limits with how the OS/Qt handles some of the painting. The Qt Main Loop has something to do with it also.
In my experience, double buffering and repainting only the regions that need to be repainted will give you the smoother animations that you are looking for. Also make sure that your graphics are close to the size that you are actually painting them. Increasing the interval of refresh won't help most monitors because they don't refresh faster than 60 Hz.
Here is a link that may be helpful.
Qt works hard to optimize and get the graphics to look nice on a lot of platforms, and I know that as they are preparing for Qt 5, that there are some more changes to how the raster engine works.
I've also seen a demonstration similar to what is discussed here in person where they showed the frames per seconds of refreshes they could get with painting tiles in a game. Here is a link to a video discussing it. It goes into tweaking the performance of Qt for a particular game implementation, and what helps and what doesn't.
Here's my solution for Qt 4.8:
// You need qt source code to access QUnifiedTimer for QAnimationDriver
// Alternatively, copy'n'paste only the declaration for QUnifiedTimer class.
#include <qt/src/corelib/animation/qabstractanimation_p.h>
...
// In the animation thread, set the update timing interval.
// The smaller the interval, the more updates and more CPU consumption.
int animationTimingInterval = update_interval_in_msecs_u_want;
QUnifiedTimer::instance()->setTimingInterval(animationTimingInterval);

Resources