[SOLVED]Access image pixels from QScript Engine
-
How can I get a pixmap inside my script and then, after modifying it, return it back to my main app so it can repaint the image?
For example a simple case would be making the whole image black. I would have two public slots named something like
getPixels()
that returns ?, andsetPixels(?)
. What would be the best variable type to use? -
If you are using QPixmap, then you can convert your QPixmap to a QImage and iterate over each line using scanLine(). You will get a pointer to the first pixel in the line, you can go to the second one by incrementing your pointer. You can convert your modified QImage back to a QPixmap using QPixmap::fromImage().
About the variable: scanline returns a uchar pointer. Afaik you can cast it into a QRgb pointer or manipulate it directly, whatever you prefer. It could look similar to that(for a Format_RGB32 Image):QImage image = pixmap.toImage(); for(int i = 0; i < image.height(); ++i) // to get each line { QRgb *rgb = (QRgb*)image.scanLine(i); // get line at i for(int x = 0; x < image.width(); ++x) // iterate over each pixel in each line { *rgb = qRgb(qRed(color), qGreen(color), qBlue(color)); // manipulate the image ++rgb; // increment rgb to go to the next pixel } }
I haven't testet the code so i don't know if it works, but the idea behind it is correct.
-
@ealione said:
Hi @onek24,
I do not want to access pixel values of an image. I want to edit those values from inside a script that is run through the script engine.Regards.
Well, thats how you manipulate(edit) pixels from C++ using Qt. I haven't worked with the QScriptEngine yet so i'm not sure if i can help you with that. Maybe you could connect a signal with a slot in c++ which manipulates a pixel or a hole area? Afaik QScriptEngine can't work with QImages directly, but i might be wrong.
-
One possible way could be having to functions, as an example:
QList<int> MainWindow::getPixel(int x, int y) { QColor tempColor; QList<int> pixelValues; tempColor = QColor(m_image2->pixel(x, y)); pixelValues << tempColor.red(); pixelValues << tempColor.green(); pixelValues << tempColor.blue(); return pixelValues; } void MainWindow::setPixel(int x, int y, QList<int> pVal) { m_image2->setPixel(x, y, qRgb(pVal[0], pVal[1], pVal[2])); m_item2->setPixmap(QPixmap::fromImage(*m_image2)); }
added as
public slots:
to your main class, or whatever class you want to make accessible to the ScriptEngine.
This way you do not read all the values at once but it can still be useful.A second way could be encapsulating your data inside a
QObject
, which you later on make available to the ScriptEngine under a certain name.Thus I am marking this as solved, but if someone knows a better way or believes these solutions are not correct do say so.
-
@ealione Thats correct, but thats way too heavy for larger manipulations... From the QtDocs:
QImage::setPixel()
Warning: This function is expensive due to the call of the internal detach() function called within; if performance is a concern, we recommend the use of scanLine() to access pixel data directly.
QImage::pixel()
Warning: This function is expensive when used for massive pixel manipulations.
Also i would replace the QList<int> with a QRgb, if this is a possible variable-type for the ScriptEngine.
-
@onek24 the QRgb seems like a good idea, I will have to try.
What other options are there except from those functions. Scanline cannot be used as its output can't be returned to the ScriptEngine (but maybe I should try again). Unless if you mean I should use something like this:
QRgb *rowData = (QRgb*)m_image1.scanLine(row); QRgb pixelData = rowData[col];
That produces the same result, I have not tested it to see of it is faster though.
-
@ealione said:
@onek24 the QRgb seems like a good idea, I will have to try.
What other options are there except from those functions. Scanline cannot be used as its output can't be returned to the ScriptEngine (but maybe I should try again). Unless if you mean I should use something like this:
QRgb *rowData = (QRgb*)m_image1.scanLine(row); QRgb pixelData = rowData[col];
That produces the same result, I have not tested it to see of it is faster though.
There are several methods, if there is a QRgb given like in my example, then you can use qRed, qGreen, qBlue to get the colors as integers.
QRgb *pointerToAPixel = ...; int redValue = qRed(*pointerToAPixel); int greenValue = qGreen(*pointerToAPixel); int blueValue = qBlue(*pointerToAPixel);
Also you could work with QColor:
// Just pass it the scanLine-value instead of converting the scanLine into a QRgb pointer QColor color = QColor::fromRgb(*scanLine); int redValue = color.red(); int greenValue = color.green(); int blueValue = color.blue();
-
This is what I am doing. The whole issue from the start was that I had difficulties passing multidimensional data from and to my script.
I think that we are practically saying the same thing, its just that as you said, going about it the way I have (reading and writing individual pixels) is quite slow.
-
@ealione I'm just saying that it's better to use scanLine than setPixel() or pixel() for larger manipulations. About the datatypes: QScriptEngine can pass or take QVariants which can be converted into QVariantList or QVariantMap. A QVariantList is just a QList<QVariant>. If you look at this example you will see that you can create custom Objects and register them so your ScriptEngine will know what they are. You could create a struct which contains the x-coordinate, the y-coordinate and a new red, blue and green value to set. Add each struct to a QVariantList and pass it to your slot which manipulates the image. Now iterate over each pixel on the image using scanLine(if you want) and set the new value if your list has a new value for the pixel. For the reading part: Same struct, read the x, y, red, green and blue into the struct, write the struct into a QVariantList for every pixel you want and then return it to your ScriptEngine.
Also you could use a QVector<int> (as mentioned in the example).