1

I have been trying to learn some image processing on OpenCV python. I have a 16-bit image, and I would like to apply a LUT conversion on this 16-bit image without reducing it to 8-bit. From the documentation, I read that LUT function in OpenCV is applicable only for 8-bit images. Does anyone know of an efficient way to use this function for 16-bit image?

I have used LUT coversion for 8-bit images. They work alright, but for 16bit images, the follwing error throws up: error: (-215:Assertion failed) (lutcn == cn || lutcn == 1) && \_lut.total() == 256 && \_lut.isContinuous() && (depth == CV_8U || depth == CV_8S) in function 'cv::LUT'.

Later, I found that this is because LUT function is application only for 8-bit images.

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
  • Doesn't OpenCV have good interoperation with numpy? – Mark Ransom Apr 04 '22 at 09:30
  • It may be because common image formats don't support (or implementations never support) lookup tables for 16-bit images (see [this question](https://stackoverflow.com/questions/12941526/is-there-support-in-android-for-indexed-png-files-especially-16-bit-png-files)), not that the method could not be applied. You may be able to use OpenCV's code with some modification for your own purpose. – blarg Apr 04 '22 at 09:35

1 Answers1

4

As you've already discovered, the implementation of OpenCV's LUT method only supports 8-bit LUTs. However, you can implement your own for arbitrary bit resolutions and it's actually quite simple. For each value in the image, this is directly used to access the LUT which will output the desired value. Because OpenCV interfaces with NumPy, you can just use the input image and index into the LUT directly in order to obtain the final output, taking advantage of NumPy array indexing.

First define a LUT - you'll need to ensure it's 16-bit and I'm assuming you have values that go from 0 to 65535 to respect the 16-bit resolution. Once you do that, use the table to index into your image. Here's an example using gamma adjusting:

import numpy as np

def adjust_gamma(image, gamma=1.0):
    # build a lookup table mapping the pixel values [0, 65535] to
    # their adjusted gamma values
    inv_gamma = 1.0 / gamma
    table = ((np.arange(0, 65536) / 65535) ** inv_gamma) * 65535

    # Ensure table is 16-bit
    table = table.astype(np.uint16)

    # Now just index into this with the intensities to get the output
    return table[image]

This applies inverse gamma adjusting of an input image, where we first generate a LUT that is 16-bit, then the image is used to directly index into it to create the output image. Take note that the input image is also assumed to be 16-bit. If you have any values that are beyond the 0-65535 range, this will give you an out-of-bounds indexing error.

Note - Multi-channel images

Take note that the above case assumes a single-channel image. If you want to apply this for multi-channel (i.e. RGB images), then you'll need to define a LUT for each channel and apply the LUT to each channel separately. The easiest way to do this would be a for loop across all channels. There are definitely more vectorized ways to do this in one-shot, but I will not diverge from the intent of your question and I want this to be as simple to read as possible.

First define a 2D LUT where each row in this matrix is a single LUT. Specifically, row i corresponds to the LUT to apply to channel i of the image. Once you're finished, loop through the channel dimension and apply the LUT. What we can also do to save some time is to preallocate the output image so that it's all zeroes, then fill in each channel accordingly.

Something like:

# Assume LUT is defined as `table` and it's a 2D NumPy array
output = np.zeros_like(image)
for i in range(image.shape[2]):
    output[..., i] = table[i, image[..., i]]

output will contain the desired result. However, for the special case where the LUT is the same across all channels, you can just use the same 1D LUT you had previously and you can use the same indexing method that I talked about earlier:

output = table[image]
rayryeng
  • 102,964
  • 22
  • 184
  • 193