convert 16 bit image to 8 bit image
124 次查看(过去 30 天)
显示 更早的评论
采纳的回答
Jan
2012-3-5
The uint16 image used values from 0 to 2^16-1, while the uint8 images use a range from, 0 to 2^8-1 only. If you just cast the original values to uint8, the saturation will destroy a lot of information. Better use:
img8 = uint8(img16 / 256);
3 个评论
dror yemini
2020-9-10
yes but you loase somthing better before take bits with information so maybe do equalization of histogram before conversion ?
DGM
2024-7-7
编辑:DGM
2024-7-7
I know this is old, but I have to disagree. Let's consider the uint16-uint8 downconversion that's given. The obvious transformation seems fairly simple. Just map the endpoints of the two intervals to each other. Since this is unsigned, we really only have one endpoint pair to deal with.
% a full-scale ramp
x = uint16(0:65535);
% simple linear transformation between interval endpoints
ir = [0 65535];
or = [0 255];
y0 = double(x)/(ir(2)/or(2));
The catch is picking the right scaling factor and rounding method. This is how the IPT tools do it.
% default IPT-style rescaling
% Y = round(X/((2^bitsin - 1)/(2^bitsout - 1)))
%y1 = im2uint8(x);
y1 = uint8(double(x)/257); % 257, not 256
% plot everything
plotthings(x,y0,y1,ir)
A lot of code just does this sort of conversion by bitshifting instead. I suppose there are reasons to do it this way just the same.
% this is equivalent to bitshifting
% Y = fix(X/2^(bitsin - bitsout))
y1 = uint8(fix(double(x)/256));
% plot everything
plotthings(x,y0,y1,ir)
We have to remember though that cast() and uint8(), etc use round() on the input, not fix().
% i don't know what this is
% Y = round(X/2^(bitsin - bitsout))
y1 = uint8(double(x)/256);
% plot everything
plotthings(x,y0,y1,ir)
function plotthings(x,y0,y1,ir)
figure
% compare the error between the integer approximations
% and an ideal linear transformation
w = 2000;
labels = {'IPT-style','ideal'};
subplot(2,2,1)
thisx = x(1:w);
plot(thisx,y1(1:w)); hold on; plot(thisx,y0(1:w));
xlim(imrange(thisx))
legend('string',labels,'location','northwest')
xlabel('uint16 values'); ylabel('uint8 values')
title('bottom of scale')
subplot(2,2,2)
thisx = x(end-w:end);
plot(thisx,y1(end-w:end)); hold on; plot(thisx,y0(end-w:end));
xlim(imrange(thisx))
title('top of scale')
% the IPT method has a more uniformly distributed error,
% with a smaller peak magnitude
% the significance of this error depends on the width of the output class
% i.e. the relative significance of 1LSB
subplot(2,2,3:4)
plot(x,double(y0)-double(y1)); hold on
plot(x,y0-y0)
xlim(ir)
legend('string',labels)
xlabel('uint16 values'); ylabel('uint8 values')
title('error WRT float conversion')
ylim([-1.5 1.5])
end
As the comments mention, this error is still relatively small. It manifests as a slight change in contrast. Especially for wider output classes, it's probably arguable that it doesn't matter. If we want to generalize these conversions, we should also consider narrower outputs, where the error would be more significant. Not too long ago, I was using imcast() to do a bunch of downconversion to uint4-scale. At that point, it's noticeable.
更多回答(3 个)
Rasmus Herlo
2021-2-11
OBS: Jan's answer will do absolutely fine, if your image is optimized to use the entire scale. However, if your pixel values are only occupying a fraction of the available bits (common in much imaging), the direct conversion will lead to a significant loss of information. In addition, you might want to get your image on 1:2^16 (instead of 0:2^16-1) before normalizing.
Image conversion in software like ImageJ therefore uses linear scaling, and one would typically apply a similar approach here, by:
Input: Im16 'uint16' (any 16bit image)
dbIm16 = double(Im16)+1
dbIm16 = min(dbIm16(:)); db16max = max(dbIm16(:));
TgBit = 8; % or any other lower bit scale
% example with 16bit to 8bit
Norm_wOffSet = dbIm16/db16max; % maintaining putative offset from 0 in the data
Im8_wOffSet = uint8(Norm_wOffSet*2^TgBit-1); % back to 0:2^8-1
Norm_woOffSet = (dbIm16-db16min)/(db16max-db16min); % Scales linearly to full range
Im8_woOffSet = uint8(Norm_woOffSet*2^TgBit-1); % back to 0:2^8-1
1 个评论
Tiziana
2022-11-21
Thank so much for your answer. Could I ask you the reasons of this "you might want to get your image on 1:2^16 (instead of 0:2^16-1) before normalizing"? Thank you very much.
Wayne King
2012-3-5
You can use uint8() to cast the image into unsigned 8-bit integers. Or int8() for signed 8-bit integers.
X = uint8(ones(10,10,'uint16'));
class(X)
0 个评论
DGM
2023-12-30
编辑:DGM
2023-12-30
I don't know why nobody brought it up in 10+ years, but:
outpict = im2uint8(inpict); % recast with appropriate scaling
This will preserve the relative scale of the image regardless of the input class. Everything within the expected range of the input class (i.e. [-32768 32767] for int16) will fit within [0 255]. This works regardless of whether the input is int16 or uint16 or double, etc.
@Rasmus Herlo has a good point, though. A lot of technical and medical images in 16b seem to be unscaled raw values that occupy a small portion of the available dynamic range. Depending on the goals, it might be prudent to rescale them. That's easy enough.
outpict = im2uint8(mat2gray(inpict)); % normalize to extrema, then recast/scale
... though if the image is some sort of raw sensor data, a contextually-relevant nonlinear scaling might be more appropriate.
0 个评论
另请参阅
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!