RoyiAvital / StackExchangeCodes

Codes related to answers on StackExchange Network.
Other
110 stars 43 forks source link

Replicating Results of Photoshop and PhotoPea with the MATLAB Code of Digital Signal Processing Q688 #1

Closed Twice22 closed 5 years ago

Twice22 commented 5 years ago

Hello.

I actually want to achieve the same result as the Black/White adjustment from photopea and I saw your answer on StackExchange. I also have implemented my own version using what ivan Kuckir said but using yours or mine implementation I don't have the same result as Photoshop/Photopea. For example you can try to decrease all the cursors to -200 and let just 1 or 2 to 200: I used [-200; -200; -200; -200; 300; 300; -200]

Using your code I actually do something like:

img = im2double(imread("my_image.jpg"))
coeffs = [-200; -200; -200; 300; 300; -200]
normalized_coeffs = (coeffs - 50) / 50  % or 100 because Ivan Kuckir say to map in [-2.5, 2.5]
o_img = ApplyBlackWhiteFilter(img, normalized_coeffs);
imshow(o_img)

I actually understand the logic behind the code but there is one thing I don't understand: Why the cursors goes from [-200, 300]. So going from [-200, 300] to [-2.5, 2.5] as Ivan Kuckir said makes the transformation affine (y = ax + b) and not linear (y = ax). So it means that negative values will impact less the image than positive value as the range is not centered around 0?

I understand the logic of the code, because, according to the code, each pixel will be impacted by 2 weights and if Dk is x for an activated weight then it will be 1-x for the other activated weight.

I post here because I don't have enough reputation to add a comment on StackExchange

RoyiAvital commented 5 years ago

Hi, I'm not sure what's is the issue you're describing.

Pay attention that my code matches Photoshop. I didn't compare it to PhotoPea.

The range in Photoshop is [-300, 200] hence I use it.

Twice22 commented 5 years ago

Hello. Sorry I've made some mistakes. The range is actually [-200, 300] in both Photoshop and photopea. I tested both and the results between Photoshop and Photopea are similars but the result between your code (or mine) with photoshop is not the same at all.

I uploaded a test example here

I used your code as follow:

img = im2double(imread("my_image.jpg"))
coeffs = [-200; -200; -200; 300; 300; -200]
normalized_coeffs = (coeffs - 50) / 50
o_img = ApplyBlackWhiteFilter(img, normalized_coeffs);
imshow(o_img)
RoyiAvital commented 5 years ago

@Twice22 , I can reproduce the errors (Which I don't have in 11 colors I tested). I think it has to do with the slope of the curve in my code.

I will try playing with it to see if I can figure the correct value.

RoyiAvital commented 5 years ago

I can validate the Radius is correct.

The factor isn't correct. Either the transformation from Photoshop values to my script values or something else which scales the factor is wrong.

Tried some cases on Photoshop and yet to find what's the correct factor. For instance for [255, 255, 0] indeed only yellows slider will have any effect.
It will yield 255 for slider of value 100 which suggests factor of 1 -> 1 + 1 = 2. Hence its L in HSL is (Max + Min) / 2 = 128 -> Factor of 2 should yield white which is indeed.

Yet for [80, 80, 217] which has Hue of 240 -> Only blues slider. Its L from HSL is (217 + 80) / 2 = 148.5. Hence one would expect setting Blues slider to 100 will also yield white yet it won't`.

So I'm missing something with the scaling or the calculation of luminosity (Though setting all sliders at 50 which is factor of 0 indeed yields the mean of the Max and Min values).

RoyiAvital commented 5 years ago

@Twice22 , I find @kuckir answer missing.
According to his answer the output depends only on the H (Hue) and L (Lightness) values of the input.

Yet I take the following in Photoshop / PhotoPea: c1 = [255, 102, 51] and c2 = [240, 110, 66]. They have the same H and L values yet for different Reds value in Photoshop / PhotoPea they yield different result.

So something is missing and probably S (Saturation) related.

Have you had any progress?

photopea commented 5 years ago

Hi guys, I decided not to describe how the Black/White adjustment is implemented, as I want this feature to be "unique" for Photopea (and not present in other software).

RoyiAvital commented 5 years ago

OK, I think for color in the range [0, 60] [DEG] multiplying the weight by the Saturation value works. It doesn't work for colors on [240, 300] [DEG].

I will give it a try when I have another hour for playing.

Update

OK, I think I got most of it.
On some cases (Either by H value or Slider Index, I have to verify) it uses the Saturation for scaling or just multiply by 0.5.
This makes sense as @photopea / @kuckir on his answer (Deleted, unfortunately, though users with enough credit can see it) used scaling of 100 while I use 50.
So maybe he meant those zones where indeed the scaling is half yet on others it is governed by the S value as well.

Twice22 commented 5 years ago

Hi. I totally understand the point of view of the developer of photopea (not diclosing the algorithm). Yet a person found the solution (that doesn't need to convert into hsl). His answer is detailed in my stackoverflow post here

I have also provided an numpy/scipy vectorized implementation of the solution which is very fast. The figure 9 from wikipedia helps to understand the algorithm. Only 2 colors amongst R,G,B,C,M,Y impact a pixel (the colors that are nearest to the aforementioned pixel color). Here the person used min(r,g,b) as the "luminance" but we can adapt the code to use luminance = (min(r,g,b) + max(r,g,b)) / 2 or other measures as it is detailed under the Lightness section of the wikipedia article

RoyiAvital commented 5 years ago

@Twice22 , That's great. I replicated it with MATLAB and indeed it works.

I'm surprised it is so different from @photopea approach. I guess I hit the wall going that path while the answer was on other direction.

I will update my answer on DSP StackExchange.

Remark

On the code on StackOverflow it seems that for each value 3 sliders are affecting result.
While as far as I can see in Photoshop there is at most 2. It means that at least one value is zero.
This is trivial since both are subtracted from the min of the 2.

RoyiAvital commented 5 years ago

@photopea, I think this feature is now solved and we have the correct answer which replicate Photoshop perfectly.

It would be great if you shared your solution so there will be 2 approaches to solve it. Again, since it is solved, the logic of leaving it for Photopea doesn't hold as anyone can copy the answer on StackOverflow.