5

I want to do something similar to the levels function in Photoshop, but can't find the right openCV functions.

Basically I want to stretch the greys in an image to go from almost white to practically black instead of from almost white to slightly greyer, while leaving white as white and black as black (I am using greyscale images).

leinaD_natipaC
  • 4,299
  • 5
  • 21
  • 40

3 Answers3

8

The following python code fully implements Photoshop Adjustments -> Levels dialog.

Change the values for each channel to the desired ones.

img is input rgb image of np.uint8 type.

inBlack  = np.array([0, 0, 0], dtype=np.float32)
inWhite  = np.array([255, 255, 255], dtype=np.float32)
inGamma  = np.array([1.0, 1.0, 1.0], dtype=np.float32)
outBlack = np.array([0, 0, 0], dtype=np.float32)
outWhite = np.array([255, 255, 255], dtype=np.float32)

img = np.clip( (img - inBlack) / (inWhite - inBlack), 0, 255 )                            
img = ( img ** (1/inGamma) ) *  (outWhite - outBlack) + outBlack
img = np.clip( img, 0, 255).astype(np.uint8)
iperov
  • 455
  • 7
  • 8
  • 1
    This is the correct answer. Playing with inBlack and outBlack values is sufficient to get the desired effect. Thanks @iperov! – roosevelt May 04 '20 at 15:35
4

I think this is a function mapping input levels to output levels as shown below in the figure.

enter image description here

For example, the orange curve is a straight line from (a, c) to (b, d), blue curve is a straight line from (a, d) to (b, c) and green curve is a non-linear function from (a,c) to (b, d).

We can define the blue curve as (x - a)/(y - d) = (a - b)/(d - c). Limiting values of a, b, c and d depend on the range supported by the channel that you are applying this transformation to. For gray scale this is [0, 255].

For example, if you want a transformation like (a, d) = (10, 200), (b, c) = (250, 50) for a gray scale image,

y = -150*(x-10)/240 + 200 for x [10, 250]

y = x for [0, 10) and (250, 255] if you want remaining values unchanged.

You can use a lookup table in OpenCV (LUT function) to calculate the output levels and apply this transformation to your image or the specific channel. You can apply any piecewise transformation this way.

dhanushka
  • 10,492
  • 2
  • 37
  • 47
3

I don't know what are the "Photoshop levels". But from description, I think you should try the following:

  1. Convert your image to YUV using cvtColor. Y will represent the intensity plane. (You can also use Lab, Luv, or any similar colorspace with separate intensity component).
  2. Split the planes using split, so that the intensity plane will be a separate image.
  3. Call equalizeHist on the intensity plane
  4. Merge the planes back together using merge

Details on histogram equalization can be found here

Also note, that there's an implementation of somewhat improved histogram equalization method - CLAHE (but I can't find a better link than this, also @berak suggested a good link on the topic)

Community
  • 1
  • 1
Sergei Nosov
  • 1,637
  • 11
  • 9
  • [this](http://stackoverflow.com/questions/24341114/simple-illumination-correction-in-images-opencv-c/24341809#24341809) might be the missing link to CLAHE – berak Nov 13 '14 at 16:07
  • I'm using greyscale images, if that changes anything. I guess that would mean I don't have to convert, split then merge right? Just the equalizeHist bit. – leinaD_natipaC Nov 13 '14 at 17:58
  • Unfortunately equalizeHist seems to be eating up some of the colors in my image. Where there was several tones of very light gray now there is only black and very few tones of dark gray, so I can't use it. – leinaD_natipaC Nov 14 '14 at 16:09
  • To repeat myself, you can also try to use CLAHE, which is an "adaptive" alternative to global histogram equalization. – Sergei Nosov Nov 15 '14 at 11:29