Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rescale intensities of a PIL Image

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.

like image 962
Vebjorn Ljosa Avatar asked Mar 01 '23 09:03

Vebjorn Ljosa


1 Answers

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 second take:

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.

like image 191
tzot Avatar answered Mar 08 '23 01:03

tzot