What is the simplest/cleanest way to rescale the intensities of a PIL Image?
Suppose that I have a 16-bit image from a 12-bit camera, so only the values 0–4095 are in use. I would like to rescale the intensities so that the entire range 0–65535 is used. What is the simplest/cleanest way to do this when the image is represented as PIL's Image type?
The best solution I have come up with so far is:
pixels = img.getdata()
img.putdata(pixels, 16)
That works, but always leaves the four least significant bits blank. Ideally, I would like to shift each value four bits to the left, then copy the four most significant bits to the four least significant bits. I don't know how to do that fast.
Since you know that the pixel values are 0-4095, I can't find a faster way than this:
new_image= image.point(lambda value: value<<4 | value>>8)
According to the documentation, the lambda function will be called at most 4096 times, whatever the size of your image.
EDIT: Since the function given to point must be of the form argument * scale + offset
for in I
image, then this is the best possible using the point
function:
new_image= image.point(lambda argument: argument*16)
The maximum output pixel value will be 65520.
A modified version of your own solution, using itertools
for improved efficiency:
import itertools as it # for brevity
import operator
def scale_12to16(image):
new_image= image.copy()
new_image.putdata(
it.imap(operator.or_,
it.imap(operator.lshift, image.getdata(), it.repeat(4)),
it.imap(operator.rshift, image.getdata(), it.repeat(8))
)
)
return new_image
This avoids the limitation of the point
function argument.
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