Kevin M Kevin M - 10 days ago 6
C++ Question

Store all QImage's pixels with scanLine() method in C++

I am trying to modify an image using Qt with the

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 of
scanLine()
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:
enter image description here

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 !

Answer

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.

Comments