Here’s the idea although I haven’t ever actually tried it.
Using the jpeg encoding standard as an example, the image file consists of a certain number of bits that represent a pixel. If you have a 24-bit resolution file, every 24 bits represents a pixel. In general the low order bit, the last of the 24, if changed will have little impact on the overall image.
Now take the data you want to embed as a bit stream and make the low order bit of every pixel representation match the corresponding bit of your data file. You have not changed how the image looks much, and you have embedded data that can later be extracted from the image.
Note that this is a very simplified answer. This can get complicated when you start to look at the particulars of the image encoding and if you start to include error correction in your hidden data.