skip to Main Content

I have grayscale image whose background is, on a 0-255 color scale, a mid-white color with an average pixel color value of 246; the foreground is mid-grey with an average pixel-color value of 186.

I would like to ‘shift’ every pixel above 246 to 255, every pixel below 186 to zero, and ‘stretch’ everything between. Is there any ready-made algorithm/process to do this in numpy or python, or must the new levels/histogram be calculated ‘manually’ (as I have done thus far)?

This is the equivalent of, in Gimp or Photoshop, opening the levels window and selecting, with the white and black eyedropper respectively, a light region we want to make white and a darker region we want to make black: the application modifies the levels/histogram (‘stretches’ the values between the points selected) accordingly.

Some images of what I’m attempting:

page after page shadow elimination
Sampled colours
Result

2

Answers


  1. Here’s one way –

    def stretch(a, lower_thresh, upper_thresh):
        r = 255.0/(upper_thresh-lower_thresh+2) # unit of stretching
        out = np.round(r*(a-lower_thresh+1)).astype(a.dtype) # stretched values
        out[a<lower_thresh] = 0
        out[a>upper_thresh] = 255
        return out
    

    As per OP, the criteria set was :

    • ‘shift’ every pixel above 246 to 255, hence 247 and above should become 255.

    • every pixel below 186 to zero, hence 185 and below should become 0.

    • Hence, based on above mentioned two requirements, 186 should become something greater than 0 and so on, until 246 which should be lesser than 255.

    Alternatively, we can also use np.where to make it a bit more compact –

    def stretch(a, lower_thresh, upper_thresh):
        r = 255.0/(upper_thresh-lower_thresh+2) # unit of stretching
        out = np.round(r*np.where(a>=lower_thresh,a-lower_thresh+1,0)).clip(max=255)
        return out.astype(a.dtype)
    

    Sample run –

    # check out first row input, output for variations
    In [216]: a
    Out[216]: 
    array([[186, 187, 188, 246, 247],
           [251, 195, 103,   9, 211],
           [ 21, 242,  36,  87,  70]], dtype=uint8)
    
    In [217]: stretch(a, lower_thresh=186, upper_thresh=246)
    Out[217]: 
    array([[  4,   8,  12, 251, 255], 
           [255,  41,   0,   0, 107],
           [  0, 234,   0,   0,   0]], dtype=uint8)
    
    Login or Signup to reply.
  2. If your picture is uint8 and typical picture size, one efficient method is setting up a lookup table:

    L, H = 186, 246
    lut = np.r_[0:0:(L-1)*1j, 0.5:255.5:(H-L+3)*1j, 255:255:(255-H-1)*1j].astype('u1')
    
    # example
    from scipy.misc import face
    f = face()
    
    rescaled = lut[f]
    

    For smaller images it is faster (on my setup it crosses over at around 100,000 gray scale pixels) to transform directly:

    fsmall = (f[::16, ::16].sum(2)//3).astype('u1')
    
    slope = 255/(H-L+2)
    rescaled = ((1-L+0.5/slope+fsmall)*slope).clip(0, 255).astype('u1')
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search