Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Pixel differences between setPixel() and after saveToFile()  (Read 4445 times)

0 Members and 1 Guest are viewing this topic.

Untaled

  • Newbie
  • *
  • Posts: 4
    • View Profile
Pixel differences between setPixel() and after saveToFile()
« on: January 06, 2019, 01:32:39 pm »
Hello :),

I'm trying to do a program that should hide a message inside an image. I made it like that :

  // Load the image
inputImg.loadFromFile("input.bmp")

  // Load the pixel
currentPixel = inputImg.getPixel(x,y);

  // Change the pixel
(int)currentPixel.r++;
(int)currentPixel.g++;
(int)currentPixel.a--;

  // set the pixel back in the image
inputImg.setPixel(x,y,currentPixel)

  // Save the image once changes are done
inputImg.saveToFile("output.bmp")
 

All seem to work well and when the program is running the pixel values are correct.
However the saved image does't have the correct pixel values, it's slightly off for bmp and totally different for jpg :(.
For exemple I save the pixel in the top-left corner as {222,30,30,255} and the top-left corner pixel of my saved image is {223,30,29,255}.
The problem is that I need it to be exactly the same because I use the parity of values for binary conversation.

So is it possible to do it with sfml ? If so what am I doing wrong ?

Thanks you :D

eXpl0it3r

  • SFML Team
  • Hero Member
  • *****
  • Posts: 10801
    • View Profile
    • development blog
    • Email
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #1 on: January 06, 2019, 02:38:16 pm »
Obviously JPEG will completely different as JPEG is a lossy format.

Not sure what you mean exactly with top-left and some coordinates. Can you provide a complete example?
Official FAQ: https://www.sfml-dev.org/faq.php
Official Discord Server: https://discord.gg/nr4X7Fh
——————————————————————
Dev Blog: https://duerrenberger.dev/blog/

Untaled

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #2 on: January 06, 2019, 03:15:22 pm »
#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>

int main()
{
    sf::Image input;
    sf::Color currentPix;

        // input.bmp is a 100*100 red square
    input.loadFromFile("input.bmp");

    currentPix = input.getPixel(0,0); // top-left corner red pixel
    std::cout << std::endl << "     R : " << (int)currentPix.r << ",     G : " << (int)currentPix.g << ",     B : " << (int)currentPix.b;
    std::cout << ",     A : " << (int)currentPix.a;

        // Some changes
    currentPix.r++;
    currentPix.g++;
    currentPix.b++;
    currentPix.a--; // I can't use ++ since 255 is the maximum

    input.setPixel(0,0,currentPix);

    std::cout << std::endl << " new R : " << (int)currentPix.r << ", new G : " << (int)currentPix.g << ", new B : " << (int)currentPix.b;
    std::cout << ", new A : " << (int)currentPix.a << std::endl;

    input.saveToFile("output.bmp");
}

 

It shows :       R : 237, G : 28, B : 36, A : 255
               newR : 238, G : 29, B : 37, A : 254

However, when I open the image with gimp and check the color of the pixel I changed, or if I load the file and check the pixel with getPixel(),
the pixel in 0,0 will be {239,28,38,255} instead of the expected {238,29,37,254};.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32504
    • View Profile
    • SFML's website
    • Email
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #3 on: January 06, 2019, 04:28:31 pm »
The first thing to do is to pick a suitable image format. BMP doesn't support alpha values, try PNG instead.
Laurent Gomila - SFML developer

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #4 on: January 06, 2019, 04:45:43 pm »
How could have
std::cout << std::endl << " new R : " << (int)currentPix.r << ", new G : " << (int)currentPix.g << ", new B : " << (int)currentPix.b;
    std::cout << ", new A : " << (int)currentPix.a << std::endl;
printed out "newR : 238, G : 29, B : 37, A : 254" without 'new' before G, B and A and without space between 'new' and R?

Are you sure you're running right code? Can you provide the BMP file to test?
Back to C++ gamedev with SFML in May 2023

Untaled

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #5 on: January 06, 2019, 05:15:49 pm »
How could have
std::cout << std::endl << " new R : " << (int)currentPix.r << ", new G : " << (int)currentPix.g << ", new B : " << (int)currentPix.b;
    std::cout << ", new A : " << (int)currentPix.a << std::endl;
printed out "newR : 238, G : 29, B : 37, A : 254" without 'new' before G, B and A and without space between 'new' and R?

Are you sure you're running right code? Can you provide the BMP file to test?

It did print "new R : 238, new G : 29,  new B : 37,  new A : 254", I just wrote new once in order to align the two lines, sorry if it confused you.
I ran this code before to post, here's the square I'm using : https://puu.sh/CsxIi/320e594419.bmp

The first thing to do is to pick a suitable image format. BMP doesn't support alpha values, try PNG instead.

Thanks you, it seems the problem was here since it works with PNG. I tried again to modify my .bmp without
changing the alpha and it works too :) !

FRex

  • Hero Member
  • *****
  • Posts: 1845
  • Back to C++ gamedev with SFML in May 2023
    • View Profile
    • Email
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #6 on: January 06, 2019, 06:20:33 pm »
This is a real thing in stbi_write code.

So apparently with alpha values in 4 channel (and SFML always does 4 channels) BMP stbi_write does this (just important bits of C code here):
unsigned char bg[3] = { 255, 0, 255 }, px[3];
int k;

//1, 2 channel code, ifs, etc....

    // composite against pink background
    for(k = 0; k < 3; ++k)
      px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255;
 

d[3] is alpha so if it's 255 it's px[k] = d[k] but if it's less than 255 then it's like blending between 'pink' (magenta) and the pixel color.

So 0 alpha makes the pixel fully magenta (0xff00ff).

Some (old?) games do that - they assume magenta pixel is 100% transparency/0 alpha. We have createMaskFromColor in sf::Image for that use case too.

See (or look for 'pink' in stb_image_write.h): https://github.com/SFML/SFML/blob/master/extlibs/headers/stb_image/stb_image_write.h#L321


Quote
I just wrote new once in order to align the two lines, sorry if it confused you.
It's just that we get lots of people on forum who make simple mistakes so it'd be very likely if you forgot to recompile your program or something since output doesn't match the std::cout in the code.

After that was cleared up and I checked your bmp file myself to see it had this problem you described I knew there has to be something in SFML code so I went and looked without risking that I might waste my time and not find anything because it was your compilation mistake or strange file that did it.

There's a very neat function (that got added when I proposed unsigned -> sf::Color constructors ;d) in sf::Color called toInteger BTW. You can neatly print them in a one liner using hex:
std::printf("0x%08x\n", pixel.toInteger());
std::cout << "0x" << std::hex << pixel.toInteger() << std::dec << std::endl;
Back to C++ gamedev with SFML in May 2023

Untaled

  • Newbie
  • *
  • Posts: 4
    • View Profile
Re: Pixel differences between setPixel() and after saveToFile()
« Reply #7 on: January 06, 2019, 07:56:41 pm »
Thanks for the explanation, it makes sens now :D

There's a very neat function (that got added when I proposed unsigned -> sf::Color constructors ;d) in sf::Color called toInteger BTW. You can neatly print them in a one liner using hex:
std::printf("0x%08x\n", pixel.toInteger());
std::cout << "0x" << std::hex << pixel.toInteger() << std::dec << std::endl;

This will be definitly be usefull to keep my code clean

 

anything