Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting 3 channel16 bit image to 8 bit while preserving color [duplicate]

I have a tiff file of 3 channel 16 bit images. I would like to convert them to 8 bit 3 channel images but when i do a simple scaling i find that those images which are predominantly red turn all black. Is there a way to do this conversion while preserving color of the original 16 bit images. Right now i have this code.

for r in root_:
files = os.listdir(r)
for f in files:
    if "tif" in f[-3:]:
        filepath = r+"/"+f 
        tif = TIFFfile(filepath)
        samples, sample_names = tif.get_samples()
        test = np.moveaxis(samples[0], 0, 2)
        img8 = (test/256).astype('uint8')
like image 558
arrhhh Avatar asked Dec 14 '25 16:12

arrhhh


2 Answers

I am guessing you want to apply adaptive range adjustment.

Linear "stretching" between some global minimum and global maximum is a simple solution.
Finding lower and upper percentile is more robust solution than minimum and maximum.

Here is an example:

import cv2
import numpy as np

# Build input image for testing
test = cv2.imread('chelsea.png').astype(np.uint16) * 100

# lo - low value as percentile 0.1 (1/1000 of test values are below lo)
# hi - high value as percentile 99.9 (1/1000 of test values are above hi)
lo, hi = np.percentile(test, (0.1, 99.9))

# Apply linear "stretech" - lo goes to 0, and hi goes to 255
img8 = (test.astype(float) - lo) * (255/(hi-lo))

#Clamp range to [0, 255] and convert to uint8
img8 = np.maximum(np.minimum(img8, 255), 0).astype(np.uint8)

#Display images before and after linear "stretech":
cv2.imshow('test', test)
cv2.imshow('img8', img8)
cv2.waitKey(0)
cv2.destroyAllWindows()

Result:

test:
enter image description here

img8:
enter image description here

Try to revise your question so less guessing is required.

Please let me know if my guess was right.

like image 173
Rotem Avatar answered Dec 17 '25 07:12

Rotem


I would extract the 3 channels:

c1 = test[:,:][0]
c2 = test[:,:][1]
c3 = test[:,:][2]

Scale them to 8bit with a helper function:

def bytescale(image, cmin=None, cmax=None, high=255, low=0):

    if image.dtype == np.uint8:
        return image

    if high > 255:
        high = 255
    if low < 0:
        low = 0
    if high < low:
        raise ValueError("`high` should be greater than or equal to `low`.")

    if cmin is None:
        cmin = image.min()
    if cmax is None:
        cmax = image.max()

    cscale = cmax - cmin
    if cscale == 0:
        cscale = 1

    scale = float(high - low) / cscale
    bytedata = (image - cmin) * scale + low
    return (bytedata.clip(low, high) + 0.5).astype(np.uint8)

Do scaling on channels:

c1new = bytescale(c1)
c2new = bytescale(c2)
c3new = bytescale(c3)

Put all back together:

x = np.array([c1new, c2new, c3new])

Let me know if this helps.

like image 34
kalzso Avatar answered Dec 17 '25 09:12

kalzso



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!