0

I have a web app where users can use the filter contrast in order to modify an image : filter: contrast(1.1) that I would like to reproduce in VIPS. (In Ruby)

Though I have not found any information on how to do that in Vips yet. I saw Vips implement CLAHE algorithm https://github.com/libvips/libvips/issues/1576 but it is overkill for me and may not return the same kind of contrast adjustmeent similar to the CSS filter.

Also I checked this thread of adjustments examples yet I can't find anything for contrast adjustment : https://github.com/libvips/libvips/issues/59 ...

Also I ended up on W3 explanation page where they cleanly explain what is done for contrast adjustment : https://www.w3.org/TR/filter-effects/#contrastEquivalent

<filter id="contrast">
  <feComponentTransfer>
      <feFuncR type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5"/>
      <feFuncG type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5"/>
      <feFuncB type="linear" slope="[amount]" intercept="-(0.5 * [amount]) + 0.5"/>
  </feComponentTransfer>
</filter>

Definitely awesome.

Now I know they use a linear fonction (that I know how to leverage in Vips) and they stay in RGB colorspace. I have understood that a slope is just a multiplication being done. Which is easy to do as I just need to pass an array as the first argument of the Vips Linear function. http://libvips.github.io/ruby-vips/Vips/Image.html#linear-instance_method

Though I have basically no clue of what Intercept is and if Vips Linear function offers similar tool.

Any idea?


EDIT EDIT

Ok seems like W3 has been doing something funny.

The solution to exactly match CSS filter contrast with Vips is :

divider = 256
contrast = 1.5
intercept = 0.5-(contrast*0.5)
im = Vips::Image.new_from_file "pic.jpg", access: :sequential
im = ((im * contrast / divider) + intercept)*divider
im.write_to_file "output.jpg"
Maxence
  • 2,029
  • 4
  • 18
  • 37

1 Answers1

1

Yes, just multiply to change contrast. From the CSS docs, linear just computes:

C' = C * slope + intercept

So in ruby-vips you could write:

#!/usr/bin/ruby

require 'vips'

contrast = 1.1

image = Vips::Image.new_from_file ARGV[0], access: :sequential
image = image.colourspace("scrgb")
image = image * contrast - (0.5 * contrast - 0.5)
image = image.colourspace("srgb")
image.write_to_file ARGV[1]

scRGB is sRGB but with linear floats in the range 0 - 1 for black to white.

You could use linear rather than the operator overloads, it'd be slightly quicker.

image = image.linear contrast, -0.5 * contrast - 0.5
jcupitt
  • 10,213
  • 2
  • 23
  • 39
  • Hi John thanks a lot for your answer. Though I can't get it work on my end. Straight when converting my JPEG to "scrgb" I get a black image (without even performing any pixel change). If I stay in RGB then it just performs a brightness increase. (I guess the addition part is too tiny to make a difference and only the slope has an effect then). Still I am curious what the "intercept" is. I have been thinking it is the second argument (additional part) of the Linear function but I am wondering if it is not like a conditional thing .. – Maxence Sep 04 '22 at 22:24
  • Oh dear, sorry, I've updated my answer. I should not post untested code heh! Yes, intercept is just the 0 offset. – jcupitt Sep 05 '22 at 09:45
  • Oh sorry I didn't read the W3 specs in full .. Now I can indeed see a contrast improvement. It is a bit stronger than CSS for the same amount of contrast and saturation seems a bit stronger but I will investigate now I know what intercept is. I am wondering if it doesn't have something to do with a :sequential application of the linear function.I will read more about the different colourspace and play a bit with Vips. Thank you – Maxence Sep 05 '22 at 10:11
  • I tried something: changing contrast in CSS to 1.5 and screenshotting my image then picking the color in GIMP. Then I pick the color and use a converter https://ajalt.github.io/colormath/converter/ to get the first channel in SRGB 0-1. Original image : 0.70588, dot in the same area on CSS screenshotted image : 0.80784, Vips image: 0.68627. With simple math,, from the original image I get 0.70588 * 1.5 - (1.5 * 0.5) + 0.5 = 0.80882 which is off from the VIPS image but quite consistent to the CSS screenshot. Although the scrgb colourspace seems pretty wide .. I don't get where the diff arise – Maxence Sep 05 '22 at 11:32
  • Actually W3 might be using a Linear sRGB colorspace (this is really a guess) which involves the gamma https://github.com/ocornut/imgui/issues/578#issuecomment-379467586 https://github.com/ocornut/imgui/issues/578#issuecomment-379467586 I don't think Vips has such a colourspace but I will try to make the computation and see if I get something consistent. I get back to you in case I have some success – Maxence Sep 05 '22 at 11:49
  • The vips conversion to scrgb makes linear 0-1, so it's using linear space. If you remove those colourspace conversions it's do the adjustment in plain sRGB gamma space and won't match. – jcupitt Sep 05 '22 at 11:53
  • Ok seems like I found what W3 did as per my edit. Sorry for having been a bit persistent :) – Maxence Sep 05 '22 at 13:21