问题
I am trying to modify an image using Qt with the scanLine() method. This methods return a pointer to the data of a given row. I founded how to read rows here. Right now, I am able to read the value of all pixels like this:
QRgb ** pixels;
pixels = (QRgb **) (malloc(sizeof (QRgb*) * img->width() * img->height()));
#pragma omp parallel for
for (int y = 0; y < img->height(); ++y) {
pixels[y] = (QRgb*) img->scanLine(y);
}
for (int x = 0; x < img->width(); ++x) {
for (int y = 0; y < img->height(); ++y) {
int color = qRed(pixels[y][x]);
std::cout << "Pixel at " << x << ", " << y << " is " << color << std::endl;
}
}
Then, I process each pixel and try to add those in a new QRgb **
variable, but this is where the program fails while executing.
QRgb ** finalPixels;
finalPixels = (QRgb **) (malloc(sizeof (QRgb*) * img->width() * img->height()));
for (int x = 0; x < img->width(); ++x) {
for (int y = 0; y < img->height(); ++y) {
// Process pixels[y][x]
QColor final(/* some new value */);
QRgb finalrgb = final.rgb();
finalPixels[y][x] = finalrgb; // What I realy want to do - Make the program fail
finalPixels[y][x] = &finalrgb; // Don't build : invalid conversion from ‘QRgb* {aka unsigned int*}’ to ‘QRgb {aka unsigned int}’
finalPixels[y][x] = pixels[y][x]; // Make the program fail
}
}
I don't understand why I can't change the reference of finalPixels[y][x]
to a new reference. Is the type of the variable incorrect ? Or this is not how to do ? I readed some stuff about 2 dimensional arrays and pointers but still I can't figure out what is the problem here.
EDIT
@rames answered to this question by proposing to use pixel()
and setPixel()
methods. These methods are much easier to use, but this is not what I'm looking for. My first implementation was using these methods but as the setPixel() method's documentation says:
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 ofscanLine()
to access pixel data directly.
And since my goal is to apply filters to an image like blur, edge detection, I need performance, so this is why I'm trying to use scanLine()
.
I tried to change my variable type. And then simply change the pixels' color like this:
QRgb * pixels[img->height()];
#pragma omp parallel
for (int y = 0; y < img->height(); ++y)
pixels[y] = (QRgb*) img->scanLine();
}
for (int x = 0; x < img->width(); ++x) {
for (int y = 0; y < img->height(); ++y) {
QColor c(0,0,0);
QRgb cr = c.rgb();
pixels[y][x] = cr;
}
}
But even this is failing when the program runs pixels[y][x] = cr;
and I don't understand why. The output of QtCreator is he program has unexpectedly finished.
.
Ok so I know how to modify the pixels of an image with the scanLine()
method thanks to @user3528438 and @Rames. But still I can't find a way to get all the pixels in a variable. My goal is to have a temp variable, this way, I can compute a modification on the image with original pixels. Here is the last thing I tried:
QRgb * pixelsCopy[img->height()][img->width()];
QRgb * pColor;
for (int y = 0; y < img->height(); ++y) {
for (int x = 0; x < img->width(); ++x) {
pColor = new QRgb( (QRgb)img->scanLine(y)[x] );
pixelsCopy[y][x] = pColor;
}
}
for (int x = 0; x < img->width(); ++x) {
for (int y = 0; y < img->height(); ++y) {
int color = qRed(*pixelsCopy[y][x]); // Return 0
std::cout << "Pixel at " << x << ", " << y << " is " << color << std::endl;
}
}
This compile and run well, but all values are 0.. If I compare to the original pixels, this is not the case. Can you explain to me why my values are not the original onces and are all set to 0 in my *pixelsCopy
variable ? And also, isn't is too heavy to call scanLine()
method for each pixel ? I also tried to change *pixelsCopy
to pixelsCopy
, but I still get 0 values..
EDIT 2
Thanks to @user3528438 and @Rames, I finally found a way to copy pixels into a new variable, but I am getting weird results. I wrote a code that copy the pixels and reapply those to the image and I am not getting the same image.
QRgb pixelsCopy[img->height()][img->width()];
for (int y = 0; y < img->height(); ++y) {
QRgb * line = reinterpret_cast<QRgb *>(img->scanLine(y));
for (int x = 0; x < img->width(); ++x) {
pixelsCopy[y][x] = line[x];
}
}
for (int x = 0; x < img->width(); ++x) {
for (int y = 0; y < img->height(); ++y) {
int r = qRed(pixelsCopy[y][x]);
QColor final(r, r, r);
img->scanLine(y)[x] = final.rgb();
}
}
And this is the before and after:
It looks like a coordinate mistake but I checked multiple times and saw nothing.. If you have a faster and/or cleaner way to copy the original pixels, It will be nice to advice me !
回答1:
Do not use an array. The QImage class is designed for pixel manipulation and you really should use this one. Assuming that img
variable is of type QImage *
:
QImage finalImage(*img);
for (int y = 0; y < finalImage.height(); ++y) {
QRgb *line = reinterpret_cast<QRgb*>(finalImage.scanLine(y));
for (int x = 0; x < finalImage.width(); ++x) {
QRgb pixelRgb = line[x];
// Process pixelRgb
QColor final(/* some new value */);
line[x] = final.rgb();
}
}
回答2:
Since my question was a bit too wide, I created a topic on the Qt Forum and Chris Kawa answered precisely to my problem.
Copy the pixels into another variable
int size = img->height() * img->width();
QRgb* data = new QRgb[size]; //don't forget to delete it somewhere
memmove(data, img.bits(), img.height() * img.width() * sizeof(QRgb));
// We don't need to copy each pixel, that's slow
Process each pixel
We can read each pixel by incrementing x and y.
for (int y = 0; y < img->height(); ++y) {
for (int x = 0; x < img->width(); ++x) {
But this is going to be slow, it will be much faster to use pointers.
QRgb* ptr = data;
QRgb* end = ptr + img.width() * img.height();
for (; ptr < end; ++ptr)
*ptr = qRgb(qRed(*ptr), qRed(*ptr), qRed(*ptr));
STD Style : Copy the pixels into another variable
//copy
std::vector<QRgb> pixels;
pixels.resize(img.height() * img.width());
memmove(pixels.data(), img.bits(), img.height() * img.width() * sizeof(QRgb));
STD Style : Process each pixel
std::for_each(pixels.begin(), pixels.end(), [](QRgb& c) { c = qRgb(qRed(c), qRed(c), qRed(c)); });
In c++17 you'll even be able to parallelize and vertorize it easily like this:
std::for_each(std::execution::parallel_unsequenced_policy,
pixels.begin(), pixels.end(), [](QRgb& c) { c = qRgb(qRed(c), qRed(c), qRed(c)); });
Thank Chris Kawa for the help, all the code above was given by him on the topic I created on the Qt Forum.
来源:https://stackoverflow.com/questions/40598338/store-all-qimages-pixels-with-scanline-method-in-c