Histogram equalization for non-images in MATLAB - matlab

I have a vector of values and I want to change the values somehow that its histogram is closer to the uniform distribution using MATLAB. I am aware of histeq in MATLAB that takes an image as input and assumes the densities are in 0-255 range. I am looking for a more general version of histeq.

You are looking to do a full scale contrast stretch, correct? If so this function will work. You can change K to be the largest value in your vector if you are not using 8 bit integers.
function [result] = myfscs(image)
K=255;
A= min(image(:));
B= max(image(:));
P=K/(B-A);
L=A*K/(B-A);
J = (P .* image - L);
result = uint8(J); % doesn't have to be a uint8 returned

Related

Downsample number of colors

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*.

Using SVD to compress an image in MATLAB

I am brand new to MATLAB but am trying to do some image compression code for grayscale images.
Questions
How can I use SVD to trim off low-valued eigenvalues to reconstruct a compressed image?
Work/Attempts so far
My code so far is:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=double(B);
%read the image and store it as matrix B, convert the image to a grayscale
photo and convert the matrix to a class 'double' for values 0-255
[U,S,V]=svd(doubleB);
This allows me to successfully decompose the image matrix with eigenvalues stored in variable S.
How do I truncate S (which is 167x301, class double)? Let's say of the 167 eigenvalues I want to take only the top 100 (or any n really), how do I do that and reconstruct the compressed image?
Updated code/thoughts
Instead of putting a bunch of code in the comments section, this is the current draft I have. I have been able to successfully create the compressed image by manually changing N, but I would like to do 2 additional things:
1- Show a pannel of images for various compressions (i/e, run a loop for N = 5,10,25, etc.)
2- Somehow calculate the difference (error) between each image and the original and graph it.
I am horrible with understanding loops and output, but this is what I have tried:
B=imread('images1.jpeg');
B=rgb2gray(B);
doubleB=im2double(B);%
%read the image and store it as matrix B, convert the image to a grayscale
%photo and convert the image to a class 'double'
[U,S,V]=svd(doubleB);
C=S;
for N=[5,10,25,50,100]
C(N+1:end,:)=0;
C(:,N+1:end)=0;
D=U*C*V';
%Use singular value decomposition on the image doubleB, create a new matrix
%C (for Compression diagonal) and zero out all entries above N, (which in
%this case is 100). Then construct a new image, D, by using the new
%diagonal matrix C.
imshow(D);
error=C-D;
end
Obviously there are some errors because I don't get multiple pictures or know how to "graph" the error matrix
Although this question is old, it has helped me a lot to understand SVD. I have modified the code you have written in your question to make it work.
I believe you might have solved the problem, however just for the future reference for anyone visiting this page, I am including the complete code here with the output images and graph.
Below is the code:
close all
clear all
clc
%reading and converting the image
inImage=imread('fruits.jpg');
inImage=rgb2gray(inImage);
inImageD=double(inImage);
% decomposing the image using singular value decomposition
[U,S,V]=svd(inImageD);
% Using different number of singular values (diagonal of S) to compress and
% reconstruct the image
dispEr = [];
numSVals = [];
for N=5:25:300
% store the singular values in a temporary var
C = S;
% discard the diagonal values not required for compression
C(N+1:end,:)=0;
C(:,N+1:end)=0;
% Construct an Image using the selected singular values
D=U*C*V';
% display and compute error
figure;
buffer = sprintf('Image output using %d singular values', N)
imshow(uint8(D));
title(buffer);
error=sum(sum((inImageD-D).^2));
% store vals for display
dispEr = [dispEr; error];
numSVals = [numSVals; N];
end
% dislay the error graph
figure;
title('Error in compression');
plot(numSVals, dispEr);
grid on
xlabel('Number of Singular Values used');
ylabel('Error between compress and original image');
Applying this to the following image:
Gives the following result with only first 5 Singular Values,
with first 30 Singular Values,
and the first 55 Singular Values,
The change in error with increasing number of singular values can be seen in the graph below.
Here you can notice the graph is showing that using approximately 200 first singular values yields to approximately zero error.
Just to start, I assume you're aware that the SVD is really not the best tool to decorrelate the pixels in a single image. But it is good practice.
OK, so we know that B = U*S*V'. And we know S is diagonal, and sorted by magnitude. So by using only the top few values of S, you'll get an approximation of your image. Let's say C=U*S2*V', where S2 is your modified S. The sizes of U and V haven't changed, so the easiest thing to do for now is to zero the elements of S that you don't want to use, and run the reconstruction. (Easiest way to do this: S2=S; S2(N+1:end, :) = 0; S2(:, N+1:end) = 0;).
Now for the compression part. U is full, and so is V, so no matter what happens to S2, your data volume doesn't change. But look at what happens to U*S2. (Plot the image). If you kept N singular values in S2, then only the first N rows of S2 are nonzero. Compression! Except you still have to deal with V. You can't use the same trick after you've already done (U*S2), since more of U*S2 is nonzero than S2 was by itself. How can we use S2 on both sides? Well, it's diagonal, so use D=sqrt(S2), and now C=U*D*D*V'. So now U*D has only N nonzero rows, and D*V' has only N nonzero columns. Transmit only those quantities, and you can reconstruct C, which is approximately like B.
For example, here's a 512 x 512 B&W image of Lena:
We compute the SVD of Lena. Choosing the singular values above 1% of the maximum singular value, we are left with just 53 singular values. Reconstructing Lena with these singular values and the corresponding (left and right) singular vectors, we obtain a low-rank approximation of Lena:
Instead of storing 512 * 512 = 262144 values (each taking 8 bits), we can store 2 x (512 x 53) + 53 = 54325 values, which is approximately 20% of the original size. This is one example of how SVD can be used to do lossy image compression.
Here's the MATLAB code:
% open Lena image and convert from uint8 to double
Lena = double(imread('LenaBW.bmp'));
% perform SVD on Lena
[U,S,V] = svd(Lena);
% extract singular values
singvals = diag(S);
% find out where to truncate the U, S, V matrices
indices = find(singvals >= 0.01 * singvals(1));
% reduce SVD matrices
U_red = U(:,indices);
S_red = S(indices,indices);
V_red = V(:,indices);
% construct low-rank approximation of Lena
Lena_red = U_red * S_red * V_red';
% print results to command window
r = num2str(length(indices));
m = num2str(length(singvals));
disp(['Low-rank approximation used ',r,' of ',m,' singular values']);
% save reduced Lena
imwrite(uint8(Lena_red),'Reduced Lena.bmp');
taking the first n max number of eigenvalues and their corresponding eigenvectors may solve your problem.For PCA, the original data multiplied by the first ascending eigenvectors will construct your image by n x d where d represents the number of eigenvectors.

Draw Normal Distribution Graph of a Sample in Matlab

I have 100 sampled numbers, and I need to draw the normal distribution curve of them in matlab.
The mean and standard deviation of these sampled data can be calculated easily, but is there any function that plots the normal distribution?
If you have access to Statistics Toolbox, the function histfit does what I think you need:
>> x = randn(10000,1);
>> histfit(x)
Just like with the hist command, you can also specify the number of bins, and you can also specify which distribution is used (by default, it's a normal distribution).
If you don't have Statistics Toolbox, you can reproduce a similar effect using a combination of the answers from #Gunther and #learnvst.
Use hist:
hist(data)
It draws a histogram plot of your data:
You can also specify the number of bins to draw, eg:
hist(data,5)
If you only want to draw the resulting pdf, create it yourself using:
mu=mean(data);
sg=std(data);
x=linspace(mu-4*sg,mu+4*sg,200);
pdfx=1/sqrt(2*pi)/sg*exp(-(x-mu).^2/(2*sg^2));
plot(x,pdfx);
You probably can overlay this on the previous hist plot (I think you need to scale things first however, the pdf is in the range 0-1, and the histogram is in the range: number of elements per bin).
If you want to draw a Gaussian distribution for your data, you can use the following code, replacing mean and standard deviation values with those calculated from your data set.
STD = 1;
MEAN = 2;
x = -4:0.1:4;
f = ( 1/(STD*sqrt(2*pi)) ) * exp(-0.5*((x-MEAN)/STD).^2 );
hold on; plot (x,f);
The array x in this example is the xaxis of your distribution, so change that to whatever range and sampling density you have.
If you want to draw your Gaussian fit over your data without the aid of the signal processing toolbox, the following code will draw such a plot with correct scaling. Just replace y with your own data.
y = randn(1000,1) + 2;
x = -4:0.1:6;
n = hist(y,x);
bar (x,n);
MEAN = mean(y);
STD = sqrt(mean((y - MEAN).^2));
f = ( 1/(STD*sqrt(2*pi)) ) * exp(-0.5*((x-MEAN)/STD).^2 );
f = f*sum(n)/sum(f);
hold on; plot (x,f, 'r', 'LineWidth', 2);

Why isn't this inverse Fourier transform giving the correct results?

I want to invert the Fourier transform of an image in MATLAB, but the result is not the original image (as it should be). There is obviously some implementation detail that I don't know about that's causing the issue. Here's the code:
img = imread('img.jpg');
fft = fft2(img);
inv = ifft2(fft);
imshow(inv);
Since fft2 and ifft2 both perform calculations in either double or single precision, your image data (which is likely of type uint8) gets converted to type double first before being processed by fft2. You will therefore have to convert your output image inv back to an unsigned 8-bit integer using the function uint8 to recover the original image:
>> img = imread('peppers.png'); % Load a sample image
>> fft = fft2(img); % Get the Fourier transform
>> inv = ifft2(fft); % Get the inverse Fourier transform
>> inv = uint8(inv); % Convert to uint8
>> imshow(inv); % Show the image
>> isequal(img, inv) % Test if inv matches the original image img
ans =
1 % It does!
NOTE: As an additional tip, I would avoid naming your variables fft and inv since functions with those names already exist in MATLAB.
Also if you are trying to do FFT on color (24-bit) image - note that imread() will return M x N x 3 array. So you should perform FFT on each R/G/B channel separately.
See this for detail.

matlab image processing 3d

i have 100 b&w image of smthing.the probllem is i want to scan each image in 0&1 formatin mby n format and then place each image to one over one and again scan and save them in mbynby100 form.
how i do this and from where i should start
_jaysean
Your question is vague and hard to understand, but my guess is that you want to take 100 M-by-N grayscale intensity images, threshold them to create logical matrices (i.e. containing zeroes and ones), then put them together into one M-by-N-by-100 matrix. You can do the thresholding by simply picking a threshold value yourself, like 0.5, and applying it to an image A as follows:
B = A > 0.5;
The matrix B will now be an M-by-N logical matrix with ones where A is greater than 0.5 and zeroes where A is less than or equal to 0.5.
If you have the Image Processing Toolbox, you could instead use the function GRAYTHRESH to pick a threshold and the function IM2BW to apply it:
B = im2bw(A,graythresh(A));
Once you do this, you can easily put the images into an M-by-N-by-100 logical matrix. Here's an example of how you could do this in a loop, assuming the variables M and N are defined:
allImages = false(M,N,100); %# Initialize the matrix to store all the images
for k = 1:100
%# Here, you would load your image into variable A
allImages(:,:,k) = im2bw(A,graythresh(A)); %# Threshold A and add it to
%# the matrix allImages
end