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:
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)
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')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With