Accessing an SFML pixel

Asked

Viewed 258 times

3

I am developing a project where I must access pixel of an image and put them in a new matrix in RGB, for this I am using the SFML library, my difficulty is in logic, I know that a pixel is the smallest point of an image and that library gives me access to it through the code:

const sf::Uint8* pixels = imagem.getPixelsPtr();

For documentation the library stores pixels in a vector.

What I’m doing is this: knowing the height and width of it, I’m walking around with a loop and I try to catch the pixels!

// Descobre o tamanho da imagem
sf::Vector2u tam = imagem.getSize();
int largura = tam.x;
int altura  = tam.y;

for(int i = 0; i < largura ; i++)
{
    for(int j = 0; j < altura; j++)
    {
        ///METODO DA BIBLIOTECA SFML, PARA ACESSAR O PIXEL.
        const sf::Uint8* pixels = imagem.getPixelsPtr();
    }
}

How do I convert this image to RGB and put them into another vector?

3 answers

1

The simplest way to do this is by using the function getPixel(x, y) of sf::Image. Just call her inside your loop:

for(int i = 0; i < largura ; i++)
{
    for(int j = 0; j < altura; j++)
    {
        sf::Color px = imagem.getPixel(i, j);
        sf::Uint8 r = px.r;
        sf::Uint8 g = px.g;
        sf::Uint8 b = px.b;
        sf::Uint8 a = px.a;
    }
}

Alternatively (much better in performance), use the getPixelsPtr() as suggested in the question:

const sf::Uint8* ptr = imagem.getPixelsPtr();
for(int i = 0; i < largura ; i++)
{
    for(int j = 0; j < altura; j++)
    {
        sf::Uint8 r = ptr[4 * (j * largura + i) + 0];
        sf::Uint8 g = ptr[4 * (j * largura + i) + 1];
        sf::Uint8 b = ptr[4 * (j * largura + i) + 2];
        sf::Uint8 a = ptr[4 * (j * largura + i) + 3];
    }
}

This function returns a pointer to a list of pixels. Each composed of 4 components, the RGBA. It is important to note that this pointer is only valid until the image is edited. If anything changes the image, this list can be relocated and moved. Do not store for longer than necessary.

1

A pixel really is the smallest point of an image, but what the library returns, through the pointer const sf::Uint8* pixels are the R, G, B and A components of the pixels (according to the documentation).

Every four Uint8, You have a pixel itself. So, if you want to copy the pixels to another vector, or need to use manual loops, can use the function memcpy of C itself for this.

The vector to which you should copy the image needs to be the size of 4 * largura * altura bytes:

sf::Uint8* novoVetor = new sf::Uint8[4 * largura * altura];

Then just copy the bytes of the pointer returned by the function getPixelsPtr() using the memcpy:

memcpy(novoVetor, imagem.getPixelsPtr(), 4 * largura * altura);

For performance reasons, give preference to functions that return multiple pixels at a time, such as getPixelsPtr(), instead of using several getPixel(x, y).

Now, if you prefer to use a manually created loop instead of using the memcpy, you can do so:

sf::Uint8* novoVetor = new sf::Uint8[4 * largura * altura];
const sf::Uint8* pixels = imagem.getPixelsPtr();
int tamanho = 4 * largura * altura;
for (int i = 0; i < tamanho; i++) {
    novoVetor[i] = pixels[i];
}

Still, if you prefer, you can perform a typecast, and work with integer array. Like every integer of type int, or unsigned int has 4 bytes, you can effectively store 1 pixel per vector element:

unsigned int* novoVetor = new unsigned int[largura * altura];
const unsigned int* pixels = (const unsigned int*)imagem.getPixelsPtr();
int tamanho = largura * altura;
for (int i = 0; i < tamanho; i++) {
    novoVetor[i] = pixels[i];
}
  • Hello all right @carlosrafaelgn? Thanks for the answer, sorry my iguinorancia I’m still studying hehe, but if I put up in a matrix instead of a vector the code would be more or less like this?! int mat[largura][altura];&#xA;memcpy(mat, imagem.getPixelsPtr(), 4 * largura * altura); not wanting to extend myself too much, but another part of the project is to change the pixels listed by characters, the use of this method would not complicate me further?! Att

  • 1

    Wow...! I didn’t really get the idea of changing pixels by characters... :(

  • Hello, this project is for a code that will create an ASCIIART in c++, as this I must in addition to get the pixels convert them to characters before putting them in a new matrix (the new image).

  • Well, in this case, the conversation is different... ;) Take a look at these 3 links here: ASCII Art no SO, ASCII Art in Codeproject and a lib for that AA-lib

1

Another solution is the following:

Check the size of the original image before converting.

sf::Uint32 sz = img.getSize().x * img.getSize().y;

Create a structure to store pixels in RGB. May be a vector of sf::Uint8.
But, I used a std::vector<sf::Uint8>.

// Vetor que armazenará os pixels.
std::vector<sf::Uint8> rgb_pixels;
rgb_pixels.reserve(sz * 3); // reserva o espaço do tamanho da imagem RGB.

Finally, copy the pixels from the original image and store in the RGB vector.

// O ponteiro dos pixel originais em RGBA.
const sf::Uint8* img_pixels = img.getPixelsPtr();

// Armazena os pixels RGBA em um std::vector RGB:
for(sf::Uint32 i = 0u; i < (sz * 4); i += 4)
{
    // Pega o pixel da imagem original em RGBA na mesma posição que será copiada.
    const sf::Uint8* rgba = reinterpret_cast<const sf::Uint8*>(&img_pixels[i]);

    // Armazena no vetor de RGB
    rgb_pixels.push_back(rgba[0]); // r
    rgb_pixels.push_back(rgba[1]); // g
    rgb_pixels.push_back(rgba[2]); // b

    // Não armazena o rgba[3] pois você quer RGB, não RGBA.
    // rgb_pixels.push_back(rgba[3]); // a
}

Finally, if you want to access the pointer on const sf::Uint8* just call:

const sf::Uint8* rgb_ptr = rgb_pixels.data();
  • 1

    I would put a caveat on the comparison of for, i < sz * 4. If the compiler is not very smart, it will do this multiplication every time before comparing. Another detail is the use of Uint64 i as an accountant... in this case, I think Overkill and half memory/processing waste. One last detail, is that in the code comment you say Convertendo para Uint32*, but does not appear Uint32* nowhere in the rest of the code.

  • @carlosrafaelgn Thanks for the warning. This extra information was due to the reuse of an old code of mine that used Uint32*, but I ended up redoing to be simpler.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.