OpenCV doesn't support reading Tiff image in float format (at least not the standard Python version).
We may use tifffile for reading the image:
import tifffile
...
image = tifffile.imread(filename)
There are multiple ways to enhance the contrast of the image.
Two examples: "Linear stretching" and CLAHE.
Linear stretching (from my following answer):
Find minimal and maximal percentile, and apply linear "stretch" such that low percentile goes to 0, and high_prc percentile goes to 255:
def lin_stretch_img(img, low_prc, high_prc):
lo, hi = np.percentile(img, (low_prc, high_prc)) # Example: 1% - Low percentile, 99% - High percentile
if lo == hi:
return np.full(img.shape, 128, np.uint8) # Protection: return gray image if lo = hi.
stretch_img = (img.astype(np.float32) - lo) * (255/(hi-lo)) # Linear stretch: lo goes to 0, hi to 255.
stretch_img = stretch_img.clip(0, 255).astype(np.uint8) # Clip range to [0, 255] and convert to uint8
return stretch_img
OpenCV CLAHE:
Enhancing the contrast in each block, allows much higher contrast compared to the linear stretching.
Since CLAHE in OpenCV does not support float32
, we have to convert the image to uint16
type before applying CLAHE.
image_as_uint16 = cv2.normalize(image, None, 0, 65535, cv2.NORM_MINMAX, cv2.CV_16U) # Convert to uint16 before applying CLAHE
clahe = cv2.createCLAHE(clipLimit=20, tileGridSize=(8, 8))
cl1 = clahe.apply(image_as_uint16) # CLAHE in OpenCV does not support float32 apply CLAHE to the uint16 image.
cl1 = cv2.convertScaleAbs(cl1, alpha=255/65535) # Convert from uint16 to uint8
Code sample:
import os
import cv2
import tifffile
import numpy as np
def lin_stretch_img(img, low_prc, high_prc):
"""
Apply linear "stretch" - low_prc percentile goes to 0,
and high_prc percentile goes to 255.
The result is clipped to [0, 255] and converted to np.uint8
"""
lo, hi = np.percentile(img, (low_prc, high_prc)) # Example: 1% - Low percentile, 99% - High percentile
if lo == hi:
return np.full(img.shape, 128, np.uint8) # Protection: return gray image if lo = hi.
stretch_img = (img.astype(np.float32) - lo) * (255/(hi-lo)) # Linear stretch: lo goes to 0, hi to 255.
stretch_img = stretch_img.clip(0, 255).astype(np.uint8) # Clip range to [0, 255] and convert to uint8
return stretch_img
filename = 'top_high_1493_2065_132_132_0.tiff'
#image = cv2.imread(filename, cv2.IMREAD_GRAYSCALE)
image = tifffile.imread(filename)
alpha = 1.0035
beta = 0
enhanced_image = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
# Apply "linear stretching" (lower percentile 0.1 goes to 0, and percentile 99.9 to 255).
lin_enhanced_image = lin_stretch_img(image, 0.1, 99.9)
# Normalizing frame to range [0, 65535], and get the result as type uint16. (65535 = 2**16-1)
image_as_uint16 = cv2.normalize(image, None, 0, 65535, cv2.NORM_MINMAX, cv2.CV_16U) # Convert to uint16 before applying CLAHE
clahe = cv2.createCLAHE(clipLimit=20, tileGridSize=(8, 8))
cl1 = clahe.apply(image_as_uint16) # CLAHE in OpenCV does not support float32 apply CLAHE to the uint16 image.
cl1 = cv2.convertScaleAbs(cl1, alpha=255/65535) # Convert from uint16 to uint8
output_filename = os.path.splitext(filename)[0] + '_enhanced.tif'
cv2.imwrite(output_filename, enhanced_image)
cv2.imwrite(os.path.splitext(filename)[0] + '_lin_enhanced.tif', lin_enhanced_image)
cv2.imwrite(os.path.splitext(filename)[0] + '_cl1_enhanced.tif', cl1)
CLAHE output for example:

The contrast is enhanced, but the input image is mainly noise...