Jump to content

Accidentally flattened an image when saving it and can't undo. How can I reverse-engineer RGBA values from glasses lenses based on the color difference between the area that's under glasses and the area that isn't?


Go to solution Solved by LWChris,

Recommended Posts

I have this image, which I am planning on using as a profile picture, that I had someone make for me on commission some time ago. I accidentally flattened/saved the PSD using the PSD plugin, and the person I paid for the art no longer has the original PSD either. Since I can't undo this, I need to reverse-engineer the color/alpha of the glasses lenses so that I can recreate the glasses separately for an edit I plan on making, but also so I can restore the file to its original layered state.

!!!pfp.png

Insert signature here.

Link to comment
Share on other sites

The glasses are most likely pure white for highlights, #3c93f9 the rest. 255 alpha, multiply blend.

 

I just based it on guesswork: since the whites (pure white #ffffff) of the eyes are unchanged under the highlights but become blue elsewhere, using multiply blend with the blue-on-eye-whites color sounded correct, and after testing with a dummy layer it appears to be at least very close.

Link to comment
Share on other sites

  • 2 weeks later...
  • Solution

I have been thinking about creating a plugin called "Color Unmixer" to answer exactly these questions of reverse engineering. Sadly, I've not yet gotten around to think of a good custom UI. But luckily, having had those plans means I have a good understanding of how it should work, and I can do for you by hand what the plugin should've done if it existed already. So... here we go!

 

The Maths, the Basics, and You

Finding the semi-transparent overlay color boils down to a system of 4 linear equations that you can solve if you have two pixels with distinct known background colors "b1" and "b2" that were overlain by the same unknown semi-transparent foreground color "f". That is, as long as both colors were overlain by the same foreground color at the same opacity, you can mathematically calculate that foreground color and its opacity.


Your image has several of these pairs, so it should be possible to work out the color.

 

But first of all, one thing I noticed right away: the outlines are not affected by the glass, i.e. the oval outline of the eyes stays black, no matter if it should be "under" the blue tint or highlight or not under the glasses at all. So my guess is those outlines were on a separate layer ABOVE the glasses. But that's probably something you remembered anyway.

For the other bits, there's 4 pairs for both the highlight and the tint on the glasses. I've marked them red, yellow, lime and blue here (the 3rd lime marker is in the top right). Any "pair" consists of one of the base colors (outside of the glasses) and then either the corresponding part on the tinted or highlighted part of the glasses, respectively.

 

image.png.f776982728cddaa7248ea9d78f111963.png

Having 4 pairs per unknown overlay color is cool, because we can use two to determine the color and its opacity, and the other two to check if our calculations "add up" (literally) as a sanity check.


But first, here's the actual formula what the visible mixed color "m" becomes when a semi-transparent foreground color "f" is overlayed over the background color "b":

 

mc = bc + (fc - bc) * falpha / 255

 

This formula is applied per channel "c". For example, for the red channel, the formula would be:

 

mred = bred + (fred - bred) * falpha / 255

 

The keen-eyed can probably also see why we need 2 pixel pairs: since we want to know the semi-transparent foreground color, we have to re-arrange that formula to solve for the f's (more on that later). But we have to make a choice: we can either solve for the red channel value fred, or for the alpha channel value falpha. Any m/b pair will produce three equations, one for each channel, and we can use those to solve for fred, fgreen and fblue respectively. But we then need a fourth equation from a different pair to also give us some formula we can instead re-arrange to solve for falpha.

 

With all that being said, there's two snags we need to pay attention to:

 

Snag 1: If the values for a color channel between background color and resulting mixed color match, that means the foreground color used the same color value (or maybe a really close one with very low opacity) for that channel, but we cannot use that equation any longer to solve for falpha. This makes sense; if you overlay "some amount of redness" with "any transparency of same amount of redness", the resulting mix will still have that same redness value to it, no matter the actual transparency. [Side note: this is also visible from the formula. If mred = bred, that means the calculation must've been "mred = bred + [something that equals 0]". This can either mean that falpha was 0, which is not really interesting, because I think we agree the overlay was probably not 100% transparent, or "fred - bred = 0", which means "fred = bred", and that's what we stated when we said that background and foreground use the same red value, or "(fred - bred) * falpha / 255" is so small it gets rounded to 0.]

 

Snag 2: This one is not as obvious from the formula, but becomes clear once you start crunching numbers. RGB colors are discrete values, that is: integers. The formula works perfect for floating values, where mixed red values like 15.7311248 would be a possibility. Alas, in the actual image the red value is probably either 15 or more likely 16. That's something we need to keep in mind, and it is the reason why many online maths solvers might probably say that the system of equations cannot be solved, because with all that rounding, the results between channels or different pairs etc. just not quite "line up" when calculating forwards and backwards again. But we'll deal with that later.

 

The Highlights

But now: to the actual solving! Let's start with the highlight color first, so the pairs are going to be the background colors from outside the glasses plus the respective color from inside the glasses where these have a highlight.

For the blue pair, both m and b are #FFFFFF. As per snag 1, we cannot use that to determine the highlight falpha, but at least it's probably safe to assume it was "some transparency of clean #FFFFFF white". We'll use the yellow pair to find that alpha value:

The background color for the yellow markers (iris) is b = #56C3F6, the mixed result on the glass is m = #89D5F9. Let's use the red channel, since it exhibits the most intense value change from bred = 56hex to mred = 89hex, which equals bred = 86 to mred = 137 in decimal numbers. Now let's re-arrange our formula to solve for falpha:

 

mc = bc + (fc - bc) * falpha / 255

mc - bc = (fc - bc) * falpha / 255

(mc - bc) / (fc - bc) = falpha / 255

(mc - bc) / (fc - bc) * 255 = falpha

 

There we have it, we can use this formula to get falpha:

falpha = (mc - bc) / (fc - bc) * 255

 

Inserting the values for mred = 137, bred = 86 and fred = 255 (which, remember, we deduced from the "clean white"), we get 76.95266272189..., so basically 77. Hence we predict the highlight color to be "#FFFFFF with a transparency of 77".

 

Let's test our theory by applying the original formula "mc = bc + (fc - bc) * falpha / 255" to the lime pair.

 

Here are the known values for the lime pair (where "m" is the one located in the highlight, not the tint):

b = #FBD574 = rgb(251, 213, 116)

m = #FCE29E = rgb(252, 226, 158)

f = rgba(255, 255, 255, 77)

 

Now we plug the values for b and f into the formula, and see if that correctly predicts m:

mred = 251 + (255 - 251) * 77 / 255 = 252.21

mgreen = 213 + (255 - 213) * 77 / 255 = 225.68

mblue = 116 + (255 - 116) * 77 / 255 = 157.97

 

And sure enough, if we round those values to whole numbers, we get m = rgb(252, 226, 158).

Now for the red pair:

b = #FCEDDA = rgb(252, 237, 218)

m = #FDF2E5 = rgb(253, 242, 229)

 

mred = 252 + (255 - 252) * 77 / 255 = 252.91

mgreen = 237 + (255 - 237) * 77 / 255 = 242.44

mblue = 218 + (255 - 218) * 77 / 255 = 229.17

 

Again, rounding those values to whole numbers, we correctly get m = rgb(253, 242, 229). Hooray, the highlight color is indeed "#FFFFFF with a transparency of 77", just like we predicted earlier. Yay for Maths!

 

The Blue Tint

Now let's do the same thing for the blue tint. This time, it's going to require a little bit more effort, since the tint color isn't obvious already. But worry not, it's the same maths like for falpha, but we're re-arranging the original equation to solve for fc instead of falpha, and then use that formula for red, green, and blue:

 

mc = bc + (fc - bc) * falpha / 255

mc - bc = (fc - bc) * falpha / 255

(mc - bc) * 255 = (fc - bc) * falpha

(mc - bc) * 255 / falpha = fc - bc

(mc - bc) * 255 / falpha + bc = fc

 

We can use this formula to get fc:

fc = (mc - bc) * 255 / falpha + bc

 

Let's use it on the yellow pair. Here are the known values:

b = #56C3F6 = rgb(86, 195, 246)

m = #3C93F9 = rgb(60, 147, 249)

 

Plugging in those two colors for all three channels, we get three equations which are only dependent on falpha:

fred = (60 - 86) * 255 / falpha + 86

fgreen = (147 - 195) * 255 / falpha + 195

fblue = (249 - 246) * 255 / falpha + 246

 

From the blue pair, we get these known values:

b = #FFFFFF = rgb(255, 255, 255)

m = #B2BDFF = rgb(178, 189, 255)

 

This gives these equations:

fred = (178 - 255) * 255 / falpha + 255

fgreen = (189 - 255) * 255 / falpha + 255

fblue = (255 - 255) * 255 / falpha + 255

 

Since we know (or rather: require) all equations come from the same f (i.e.: with the same fred, and the same falpha), we can essentially solve for falpha by equating yellow's fred and the blue's fred, and then re-arrange the resulting equation to solve for falpha:

 

fred = (60 - 86) * 255 / falpha + 86

fred = (178 - 255) * 255 / falpha + 255


(60 - 86) * 255 / falpha + 86 = (178 - 255) * 255 / falpha + 255

-26 * 255 / falpha + 86 = -77 * 255 / falpha + 255

-26 * 255 / falpha = -77 * 255 / falpha + 169

51 * 255 / falpha = 169

51 * 255 = 169 * falpha

51 * 255 / 169 = falpha

falpha = 76.95266272189349

 

Hey! That value looks awfully familiar, doesn't it? Yep, we landed on the same result as with the highlight color earlier. In hindsight, it's probably not too surprising that we got the same transparency value of "77" for both parts of the glass. The glasses were probably created on a separate layer using opaque colors and a uniform layer transparency of 77. But now we know for sure (and I could include the steps to calculate falpha for a non-trivial case).

 

Anyway, back to the original "yellow equations". Now that we have confirmed that falpha is indeed 77, we can complete those equations and hopefully get our foreground color. So here goes nothing:

 

fred = (60 - 86) * 255 / 77 + 86 = -0.10

fgreen = (147 - 195) * 255 / 77 + 195 = 36.04

fblue = (249 - 246) * 255 / 77 + 246 = 255.94

 

Here we have a classic example of snag 2. The "known" values were rounded, so reversing the operation has lead to slight deviations from the original colors. We get negative values such as -0.10, or values like 255.94, which would get rounded to 256 and thereby exceed the possible maximum of 255. But for now, let's reasonably assume f to be rgba(0, 36, 255, 77) and see where that assumption takes us. We can check it using the other two pairs - and that's why having such extra pairs for verification is so nice.

 

Let's test red first. Known colors:

b = #FCEDDA = rgb(252, 237, 218)

m = #B0B0E5 = rgb(176, 176, 229)

 

Assuming f = rgba(0, 36, 255, 77), the formula suggests:

mred = 252 + (0 - 252) * 77 / 255 = 175.91

mgreen = 237 + (36 - 237) * 77 / 255 = 176.31

mblue = 218 + (255 - 218) * 77 / 255 = 229.17

 

Great news! These values check out for our observed m.

 

Now, for our final test, we check the lime pair. Known colors:

b = #FBD574 = rgb(251, 213, 116)

m = #B0A09E = rgb(176, 160, 158)

 

And one last time, assuming f = rgba(0, 36, 255, 77), the formula suggests:

mred = 251 + (0 - 251) * 77 / 255 = 175.21

mgreen = 213 + (36 - 213) * 77 / 255 = 159.55

mblue = 116 + (255 - 116) * 77 / 255 = 157.97

 

Spot on! Nice.

 

The Answer

And there we have it. From using nothing but the final result and Maths, we can deduce:
The glasses were made using a highlight color of #FFFFFF and a tint color of #0024FF, on a layer with "77" transparency.

 

Cheers, Chris

Edited by LWChris
  • Like 4
  • Upvote 1
  • You're a Smart Cookie! 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...