I have a series of images with decreasing brightness that I would like to try to correct with histogram equalization. I applied histeq to some test data to learn how the function works
% Image that I would like to apply histogram equalization to
C = gallery('wilk',21);
figure, imagesc(C)
E = histeq(C);
figure, imagesc(E);
However, when I look at the output of histeq, I get a result that only has two unique values: 0.873 and 1.000. How come the output doesn't span the whole range of the input? I would expect there to be more than two unique values in the output.
According to the documentation for histeq, if the input is of type double or single it is expected to be in the range: [0, 1].
Intensity values in the appropriate range: [0, 1] for images of class double, [0, 255] for images of class uint8, and [0, 65535] for images of class uint16.
Your data is not normalized and is of type double,
whos C
% Name Size Bytes Class Attributes
%
% C 21x21 3528 double
[min(C(:)), max(C(:))]
% 0 10
You will need to normalized it first. You can use mat2gray to do this:
E = histeq(mat2gray(C));
Related
Log Transform for image is defined as this
s=T(r) = c*log(r+1)
where
s is the output image
r is the input image
c = 255/log(1+Maximum pixel value from the input image).
If the "Maximum pixel value from the input image" is 255 then the C value will be
105.886458025=255/log(256)
This does not make sense. 105.89 is a very high intensity value. Multiplying with this C value will yield a very bright image. Using the 'tire' image from matlab I get the output below:
I=imread('tire.tif');
I2=im2double(I);
J=105*log(1+I2);
imshow(I)
Is my understanding correct?
Image for demonstration
Alternate image
c is a scaling factor.
The following script works both on Matlab and Octave.
clc;
close all;
clearvars;
%imageFileName = 'https://www.forestfoliage.com/wp-content/original/2010_10/Fall-Foliage-mountain-sunset.jpg';
imageFileName = 'https://pixnio.com/free-images/2020/06/27/2020-06-27-11-50-57-1200x800.jpg';
I = imread(imageFileName);
inputMin = min(I(:))
inputMax = max(I(:))
I2 = double(I);
outputMax = 255
c = outputMax/log10(1 + double(inputMax))
J = uint8(double(c) * log10(1 + I2));
%I2(1:5,1:5,:)
%J(1:5,1:5,:)
imshow(I)
title("Raw")
pause(5)
imshow(J)
title("Processed")
im2double documentation:
im2double(I) converts the image I to double precision. I can be a grayscale intensity image, a truecolor image, or a binary image. im2double rescales the output from integer data types to the range [0, 1].
So, we will use double() instead. Thanks to #CrisLuengo for this. Earlier, I was rescaling the scaled output.
In Matlab,
Images stored as matrices of type double should have values in the range [0, 1].
Images stored as integer matrices should have values in the range [0, 255].
On line 12, we are casting the matrix to an integer matrix for this reason.
From the imshow() documentation,
If you specify a truecolor image of data type single or double, then values should be in the range [0, 1]. If pixel values are outside this range, then you can use the rescale function to scale pixel values to the range [0, 1]. The 'DisplayRange' argument has no effect when the input image is truecolor.
I already know uint8 contains intensity values between 0 and 255 (28-1) and single contains values between 0 and 1, it is used to hold larger values without upsetting the range error. But, apart from that, are there any other differences?
What is the difference between imagesc((I/64)*64) and imagesc((Is/64)*64), where I is uint8 and Is is single?
imagesc just calls image underneath. As for image, it behaves a bit differently if integers or floats are supplied, as can be learned from image's documentation:
If C is of type double, then an RGB triplet value of [0 0 0] corresponds to black and [1 1 1] corresponds to white.
If C is an integer type, then the image uses the full range of data to determine the color. For example, if C is of type uint8, then [0 0 0] corresponds to black and [255 255 255] corresponds to white. If CData is of type int8, then [-128 -128 -128] corresponds to black and [127 127 127] corresponds to white.
...
Converting Between Data Types
To convert indexed image data from an integer type to type double, add 1. For example, if X8 is indexed image data of type uint8, convert it to type double using:
X64 = double(X8) + 1;
To convert indexed image data from type double to an integer type, subtract 1 and use round to ensure that all the values are integers. For example, if X64 is indexed image data of type double, convert it to uint8 using:
X8 = uint8(round(X64 - 1));
To convert true color image data from an integer type to type double, rescale the data. For example, if RGB8 is true color image data of type uint8, convert it to double using:
RGB64 = double(RGB8)/255;
To convert true color image data from type double to an integer type, rescale the data and use round to ensure that all the values are integers. For example, if RGB64 is image data of type double, convert it to uint8 using:
RGB8 = uint8(round(RGB64*255));
I = uint8(255*rand(1e3));
Is = single(I)/255;
tmpI = (I/64)*64;
tmpIs = (Is/64)*64;
% plot for prosterity
% figure;
% subplot(211)
% imagesc(tmpI)
% subplot(212)
% imagesc(tmpIs)
numel(unique(tmpI(:))) % gives 5
numel(unique(tmpIs(:))) % gives 256
Dividing an integer basically means binning of values, and then it stretches the data back to the original extend for plotting. In this case, you get 256/64 = 4 bins, with 0 as well, thus 5 possible values for your uint8 image. However, using single you retain all unique numbers, since the precision is a lot higher.
If you'd do the same test with a lot (order 2^52) elements in the rand and use double you'd see that that again has 2^32 times the number of unique elements of single, just as uint16 will have 2^8 the number of unique elements of uint8.
I am getting some error in this code section
X=imread ('Lighthouse.jpg'); %reads picture as int8 matrix
figure, imagesc(X), colormap gray, title('original picture'), % display picture
filter=[-1 0 1; -2 0 2; -1 0 1]; % builds Sobel filter matrix
filter=single(filter); %convert double to single
x=single(X); % convert int8 to single
x=x/max(max(x)); %normalisation to [0,1]
The error I get:
Error using /
Inputs must be 2-D, or at least one input must be scalar.
To compute elementwise RDIVIDE, use RDIVIDE (./) instead.
Error in sobel (line 10)
x=x/max(max(x)); %normalisation to [0,1]
Also when I am using ./ as suggested, I get new error:
Array dimensions must match for binary array op.
Error in sobel (line 10)
x=x./max(max(x)); %normalisation to [0,1]
I am doing something wrong in the normalization step.
How do I resolve this issue?
Why do you call max twice. If I run the code with
x=x/max(x(:))
I do not get an error. This runs the matrix in 1D.
Whilst Caduceus' answer is correct; it normalises over all three colours in one go. What's probably better for your case is rgb2gray, to get a single colour channel and then normalise that instead (using x/max(x(:))).
X=imread ('lighthouse.png'); %reads picture as int8 matrix
filter=[-1 0 1; -2 0 2; -1 0 1]; % builds Sobel filter matrix
filter=single(filter); %convert double to single
x = single(rgb2gray(X)); % rgb2gray gives a uint8, you want single
% x=x/max(x(:)); %normalisation to [0,1] , not needed here as x can directly be used
% for Sobel purposes as it's a grey scale image.
figure;
subplot(1,2,1)
imagesc(X)
colormap(gray)
title('original picture'), % display picture
subplot(1,2,2)
imagesc(x)
colormap(gray)
title 'Grey scale'
The reason for the first error is is that max gives a column-wise maximum, and that this is a 3D matrix. max(max()) thus gives a 1D one, instead of the desired scalar.
Then the second error occurs because max(max()) gives an array, which doesn't have the same amount of entries as the full matrix (obviously).
Basically if size(x) = [row, column channels], size(max(x)) = [row channels]
and size(max(max(x)) = [row]. Using the colon operator actually makes the entire 3D matrix a single column vector, and max(x(:)) thus gives a single value, which is the maximum across all rows, columns and channels.
When I run your code the error message says "Use RDIVIDE (./)".
implement it like this:
x=x./max(max(x));
This divides each RGB layer by its maximum. You may have to replicate the max values (I guess this depends on matlab version), use this line instead
x=x./repmat(max(max(x)),size(X,1),size(X,2),1);
The following code snippet results a double image.
f = imread('C:\Users\Administrator\Desktop\2.tif');
h = double(f);
figure;
imshow(h);
whereas, this other code snippet results a uint8 image.
f = imread('C:\Users\Administrator\Desktop\2.tif');
figure;
imshow(f);
While displaying these two figures, the displayed results of these two images using imshow are different, but what is the reason behind this difference?
Images of type double are assumed to have values between 0 and 1 and uint8 images are assumed to have values between 0 and 255. Since your double data contains values between 0 and 255 (since you simply cast it as a double and don't perform any scaling), it will appear as mostly white since most values are greater than 1.
You can use the second input to imshow to indicate that you would like to ignore this assumption and automatically scale the display to the dynamic range of the data
imshow(h, [])
Or you can normalize the double version using mat2gray prior to displaying the image
h = mat2gray(h);
imshow(h)
I'd like to plot a 3D dataset with colors. That is, each point has an associated rgb color. When I use scatter3 for this task, the plotting process is veeeeery slow. I have searched for alternative options and came up with the function plot3k from FileExchange:
This function however is only able to plot each point's color by referring to some colormap via an index and does not take the rgb values directly. Also, it repeatedly uses plot3 to do its plotting which also gets very slow when the colormap is too large.
So, I was wondering:
Is there a function to downsample the number of colors? I.e., I pass a N x 3 RGB-Array to the function and the function returns indices and a new array A, where size(A,1) < N and A(indices,:) are the new approximated colors.
Yes, there is such a function in core Matlab: rgb2ind. Its purpose is to approximate truecolor images by indexed images, so we have to fiddle around a bit to make it work.
I assume that xyz is an N x 3 array of coordinates and rgb is an N x 3 array of colors. If all the colors are different,
scatter3(xyz(:,1), xyz(:,2), xyz(:,3), 4, rgb)
takes about 21 seconds for N = 100000 (on my machine).
If ncol is the number of different colors used for approximation, then this does the trick:
% convert colors from 0–1 double format to 0–255 uint8 format
rgb = uint8(floor(rgb * (256 - eps)));
% give the color array the form of a truecolor image (N x 1 x 3)
rgb = permute(rgb, [1 3 2]);
% reduce the number of colors
[ind, map] = rgb2ind(rgb, ncol, 'nodither');
The result is a sequence of integer color indices ind into the color map map. The nodither option is necessary because our rgb is not really an image and therefore spatial error diffusion doesn't make sense here. The data can now be plotted using
scatter3(xyz(:,1), xyz(:,2), xyz(:,3), 4, ind)
colormap(map)
caxis([0 ncol] - 0.5) % ensure the correct 1:1 mapping
For ncols = 100, color conversion and plotting together take about 1.4 seconds, a speed-up by a factor 15!
rgb2ind does minimum variance quantization in RGB-space, meaning that it only takes into account numeric similarity in its approximation, but not visual similarity. It should be possible to improve results by using another color space for approximation, for example CIE L*a*b*.