How to convert hyperspectral image to RGB in Matlab - matlab

I have a problem with extracting 3 bands under the names R, G, and B, and then using those bands as 3 colour channels for RGB pictures. It is a hyperspectral image and it has 103 bands opposite to the normal RGB image which contains just 3. The one who gave me this question to solve, described it as something very basic, however, I am having a very difficult time with it. I may have syntax problems with Matlab. The picture comes as a .mat file. with the size of 610x340x103.
I used different codes, searched forums etc. But I failed. here is the code I tried last time.
load("PaviaU.mat")
Blue=paviaU(:,:,7);
imwrite(Blue,"Blue.jpg")
newBlue=imread("Blue.jpg");
imshow(newBlue)
imagesc(newBlue) %*this line gives the result as a scaled image*
Green=paviaU(:,:,21);
imwrite(Green,"Green.jpg")
newGreen=imread("Green.jpg");
imshow(newGreen)
Red=paviaU(:,:,53);
imwrite(Red,"Red.jpg")
newRed=imread("Red.jpg");
imshow(newRed)
rgbImage = paviaU(:,:,[newRed,newGreen,newBlue])
when I use imagesc(Red) instead of imshow I see pictures as a result, but it is not what I would like to see. Also, I analysed this .mat file on the App section of Matlab where there is a hyperspectral image app, I could not observe the spectrum because band information was absent. However, I think, inside the library of image processing there is a file with the name of paviaU.dat that file works, and inside the App, I observed the spectrum and played with it.
What is the point I don't understand here? Is there a solution? You can find that PaviaU.mat file with the variable inside as paviaU through this link PaviaU.mat

As commented, in MATLAB the range of RGB images should be [0, 1] (for type double).
Dividing by the maximum value is a simple way for converting the range to [0, 1] (assuming input pixels are positive).
rgbImage = rgbImage / max(rgbImage(:));
When showing a single channel image as Grayscale, we may use imshow(I, []).
For inspecting pixel values, we may add impixelinfo after imshow (we may also add a title):
figure;
imshow(Blue, []);
title('Blue');
impixelinfo
The RGB image looks dark, probably because the images are captured without Gamma correction.
For applying Gamma correction, according to sRGB standard, we may use lin2rgb function:
srgbImage = lin2rgb(rgbImage);
Code sample:
load("PaviaU.mat")
Blue = paviaU(:,:,7);
figure;imshow(Blue, []);title('Blue');impixelinfo
Green = paviaU(:,:,21);
figure;imshow(Green, []);title('Green');impixelinfo
Red = paviaU(:,:,53);
figure;imshow(Red, []);title('Red');impixelinfo
rgbImage = cat(3, Red, Green, Blue); % Concatenate Red, Green and Blue channels.
rgbImage = rgbImage / max(rgbImage(:)); % Convert to range [0, 1]
figure;imshow(rgbImage);title('rgbImage');impixelinfo
srgbImage = lin2rgb(rgbImage); % Apply Gamma correction.
figure;imshow(srgbImage);title('srgbImage');impixelinfo
srgbImage = im2uint8(srgbImage); % Convert from double in range [0, 1] to uint8 in range [0, 255] (optional).
imwrite(srgbImage, 'srgbImage.png'); % Save image for testing.
Output srgbImage image:

Related

Converting a RGB image to grayscale based on the intensity of one color channel [duplicate]

This question already has an answer here:
Access RGB channels in an image in MATLAB
(1 answer)
Closed 6 years ago.
I'm working with images similar to this: a cell image, and I want to extract only the red-pink sections. As of now I'm using img(:,:,1) to pull out the red values but this produces a binary image. I wanted to know if there was a way to extract the "red" values and produce a grayscale image based on their degree of "redness" or intensity. Any help would be awesome.
You are likely visualizing the result using imshow which will automatically set the color limits of the axes to be between 0 and 1. Your image is RGB and the values of the red channel are going to range from 0 to 255. Because of this, if you only specify one input to imshow, you will get an image where all values > 1 will appear as white and all zero-values will be black. So your image isn't really binary, it just appears that way.
You want to either display your image with imagesc which will automatically scale the color limits to match your data:
imagesc(img(:,:,1));
colormap gray
Or you can specify the second input to imshow to cause it to also scale to fit your data range:
imshow(img(:,:,1), [])
The reason that this isn't an issue when you are visualizing all channels is that if you specify red, green, and blue channels, this is considered a true color image and all axes color limits are ignored.
The data you capture will be correct (and is grayscale), but the visualization may be incorrect. When trying to visualize a 2D matrix (same as your result img(:,:,1)), matlab applies the default colormap and the result is:
[x,y]=meshgrid(1:200, 1:200);
z=x.^2.*sin(y/max(y(:))*pi);
figure;imagesc(z);
If you want to avoid the applied jet colormap, either change the colormap:
colormap('gray')
or change your 2D matrix into a 3D one, explicitely specifying the colors to display (must be values between 0 and 1):
z3d = z(:,:,[1 1 1]); % more efficient than repmat
z3d = (z3d - min(z(:)))./range(z(:)); % make sure values in range [0; 1]
You see banding in the colormap version, because by default a colormap contains 64 different colors; the 3d matrix version doesn't have this problem as it directly displays the colors.
If I may add to your question, it seems to me you're simply trying to isolate and visualise the red, green, and blue fluorofores separately (or in combination). I specifically think this because you mention 'pink'.
It may be nicer to just isolate the channels:
>> F_red = F; F_red(:,:,[2,3]) = 0;
>> F_green = F; F_green(:,:,[1,3]) = 0;
>> F_blue = F; F_blue(:,:,[1,2]) = 0;
>> F_pink = F; F_pink(:,:,2) = 0;
Here's a subplot of the result:
Furthermore, you should know that the 'naive' way of producing a grayscale image does not preserve the 'luminosity' of colours as perceived by the human eye, since 'green' at the same intensity as 'red' and 'blue' will actually be perceived as brighter by the human eye, and similarly 'red' is brighter than 'blue'. Matlab provides an rgb2gray function which converts an rgb image to a grayscale image that correctly preserves luminance. This is irrelevant for your pure red, green, and blue conversions, but it may be something to think about with respect to a 'pink-to-grayscale' image. For instance, compare the two images below, you will see subtle contrast differences.
>> F_pinktogray_naive = mean(F(:,:,[1,3]), 3);
>> F_pinktogray_luminance = rgb2gray(F_pink);
A subplot of the two:
In a sense, you probably care more about the left (naive) one, because you don't care about converting the pink one to a gray one "visually", but you care more about the red and blue fluorofores being "comparable" in terms of their intensity on the grayscale image instead (since they represent measurements rather than a visual scene). But it's an important distinction to keep in mind when converting rgb images to grayscale.

How do I denoise a simple grayscale image

Here is the original image with better vision: we can see a lot of noise around the main skeleton, the circle thing, which I want to remove them, and do not affect the main skeleton. I'm not sure if it called noise
I'm doing it to deblurring a image, and this image is the motion blur kernel which identify the camera motion when the camera capture a image.
ps: this image is the kernel for one case, and what I need is a general method in here. thank you for your help
there is a paper in CVPR2014 named "Separable Kernel for Image Deblurring" which talk about this, I want to extract main skeleton of the image to make the kernel more robust, sorry for the explaination here as my English is not good
and here is the ture grayscale image:
I want it to be like this:
How can I do it using Matlab?
here are some other kernel images:
As #rayryeng well explained, median filtering is the best option to clean noise in the image, which I realized when I had studied about image restoration. However, in your case, what you need to do seems to me not cleaning noise in the image. You want to more likely eliminate the sparks in the image.
Simply I applied single thresholding to your noisy image to eliminate sparks.
Try this:
desIm = imread('http://i.stack.imgur.com/jyYUx.png'); % // Your expected (desired) image
nIm = imread('http://i.stack.imgur.com/pXO0p.png'); % // Your original image
nImgray = rgb2gray(nIm);
T = graythresh(nImgray)*255; % // Thereshold value
S = size(nImgray);
R = zeros(S) + 5; % // Your expected image bluish so I try to close it
G = zeros(S) + 3; % // Your expected image bluish so I try to close it
B = zeros(S) + 20; % // Your expected image bluish so I try to close it
logInd = nImgray > T; % // Logical index of pixel exclude spark component
R(logInd) = nImgray(logInd); % // Get original pixels without sparks
G(logInd) = nImgray(logInd); % // Get original pixels without sparks
B(logInd) = nImgray(logInd); % // Get original pixels without sparks
rgbImage = cat(3, R, G, B); % // Concatenating Red Green Blue channels
figure,
subplot(1, 3, 1)
imshow(nIm); title('Original Image');
subplot(1, 3, 2)
imshow(desIm); title('Desired Image');
subplot(1, 3, 3)
imshow(uint8(rgbImage)); title('Restoration Result');
What I got is:
The only thing I can see that is different between the two images is that there is some quantization noise / error around the perimeter of the object. This resembles salt and pepper noise and the best way to remove that noise is to use median filtering. The median filter basically analyzes local overlapping pixel neighbourhoods in your image, sorts the intensities and chooses the median value as the output for each pixel neighbourhood. Salt and pepper noise corrupts image pixels by randomly selecting pixels and setting their intensities to either black (pepper) or white (salt). By employing the median filter, sorting the intensities puts these noisy pixels at the lower and higher ends and by choosing the median, you would get the best intensity that could have possibly been there.
To do median filtering in MATLAB, use the medfilt2 function. This is assuming you have the Image Processing Toolbox installed. If you don't, then what I am proposing won't work. Assuming that you do have it, you would call it in the following way:
out = medfilt2(im, [M N]);
im would be the image loaded in imread and M and N are the rows and columns of the size of the pixel neighbourhood you want to analyze. By choosing a 7 x 7 pixel neighbourhood (i.e. M = N = 7), and reading your image directly from StackOverflow, this is the result I get:
Compare this image with your original one:
If you also look at your desired output, this more or less mimics what you want.
Also, the code I used was the following... only three lines!
im = rgb2gray(imread('http://i.stack.imgur.com/pXO0p.png'));
out = medfilt2(im, [7 7]);
imshow(out);
The first line I had to convert your image into grayscale because the original image was in fact RGB. I had to use rgb2gray to do that. The second line performs median filtering on your image with a 7 x 7 neighbourhood and the final line shows the image in a separate window with imshow.
Want to implement median filtering yourself?
If you want to get an idea of how to actually write a median filtering algorithm yourself, check out my recent post here. A question poser asked to implement the filtering mechanism without using medfilt2, and I provided an answer.
Matlab Median Filter Code
Hope this helps.
Good luck!

Reduce number of colors in matlab using rgb2ind

I am doing some image processing and I needed to reduce the number of colors of an image. I found that rgb2ind could do that and wrote the following snippet:
clc
clear all
[X,map] = rgb2ind(RGB,6,'nodither');
X = rgb2ind(RGB, map);
rgb=ind2rgb(X,map);
rgb_uint8=uint8(rgb*255+0.5);
imshow(rgb_uint8);
But the output looks like this and I doubt there are only 6 colors in it.
It may perceptually look like there is more than 6 colours, but there is truly 6 colours. If you take a look at your map variable, it will be a 6 x 3 matrix. Each row contains a colour that you want to quantize your image to.
To double check, convert this image into a grayscale image, then do a histogram of this image. If rgb2ind worked, you should only see 6 spikes in the histogram.
BTW, to be able to reconstruct your problem, you used the peppers.png image that is built-in to MATLAB's system path. As such, this is what I did to describe what I'm talking about:
RGB = imread('peppers.png');
%// Your code
[X,map] = rgb2ind(RGB,6,'nodither');
X = rgb2ind(RGB, map);
rgb=ind2rgb(X,map);
rgb_uint8=uint8(rgb*255+0.5);
imshow(rgb_uint8);
%// My code - Double check colour distribution
figure;
imhist(rgb2gray(rgb_uint8));
axis tight;
This is the figure I get:
As you can see, there are 6 spikes in our histogram. If there are truly 6 unique colours when you ran your code, then there should be an equivalent of 6 equivalent grayscale intensities when you convert the image into grayscale, and the histogram above verifies our findings.
As such, you are quantizing your image to 6 colours, but it doesn't look like it due to quantization noise of your image.
Don't doubt of your result, the image contains exactly 6 colours.
As explained in the Matlab documentation, the rgb2ind function returns an indexed matrix (X in your code) and a colormap (map in your code). So if you want to check the number of colours in X, you can simply check the size of the colormap: size(map)
In your case the size will be 6x3: 6 colours described on 3 channels (red, greed and blue).

Manually turn RGB image into Grayscale Matlab

Let me preface this by saying, I understand there are functions that would do this for me, but I wanted to do it manually so I could understand what exactly is happening here.
So my goal is to read in a RGB image and turn it into a grayscale. All of my image processing work up to this point has been solely grayscale based, so I am a little bit lost.
I have tried to first read in the image by doing
fid = fopen('color.raw');
myimage = imread(fid, [512 384], 'uint8');
fclose(fid);
but myimage ends up as an empty 0 x 0 matrix. I think I need to assign an "R" "G" and "B" value to each pixel, thus giving each pixel three values for the three colors, but I am not sure if thats correct, and even how to attempt that.
my question is the following: How would I read in a RGB image and then turn into a grayscale one.
EDIT: So I understand how I would go about turning the RGB into the grayscale after getting the R G and B values, but I cannot seem to make Matlab read in the image, can anyone offer any assistance? using imread seems to make the most sense, but
[pathname] = ...
uigetfile({'*.raw';'*.mdl';'*.mat';'*.*'},'File Selector');
fid = fopen(pathname);
myimage = imread(fid);
fclose(fid);
Is not working, im getting an error of invalid filename for fopen, and I really do not understand why.
Conceptually you just average the red, green, and blue components and then providing that average as the output for the red, green, and blue components. You can do this using a simple arithmetic mean
(r+g+b)/3.
Or for kicks, you can use a geometric mean to get a slightly different contrast..
(r*g*b)^(1/3).
imread - read image from graphics file (it's in the documentation)
http://www.mathworks.com/help/matlab/ref/imread.html
For RGB to gray scale use the luminosity method. http://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
The luminosity method is a more sophisticated version of the average
method. It also averages the values, but it forms a weighted average
to account for human perception. We’re more sensitive to green than
other colors, so green is weighted most heavily. The formula for
luminosity is 0.21 R + 0.71 G + 0.07 B.
The problem with averaging all 3 values as other users have indicated is that that is not how our eyes work at all.
When you average your images, you get the result below (on the built in peppers.png image)
You are effectively doing grayImg=.33*R+.33*G+.33*B when you find the average, now compare that to how MATLAB calculates grayscale values (with consideration into how humans view images)
0.2989 * R + 0.5870 * G + 0.1140 * B
See the stark contrast in values?
To get better looking data, stay closer to the coefficients MATLAB uses :)
The way MATLAB renders it is like this:

How to get the red image from gray image in matlab

i have an image let say a=imread('example.bmp'i got all three channel from it :
R=a(:,:,1);
G=a(:,:,2);
B=a(:,:,3);
and i have the gray image of it:
igray=rgb2gray(a);
Can I get the red component from the gray image ?
No, you can't, since igray will be a two dimensional image (a is three dimensional, with the third dimension being colors planes), containing only intensity values for each pixel.
To convert an RGB image to grayscale, rbg2gray uses a formula you can find here
As you can see, it's a 3 variables equation, therefore you can't find them using intensity value alone.
The rgb2gray function effectively does this to every RGB pixel (type edit rgb2gray):
Gray = 0.298936021293776*Red+0.587043074451121*Green+0.114020904255103*Blue;
If you only have Gray in the equation above then you have one equation with three unknowns. More information is needed to solve for Red.
If you just want an RGB image where every channel has the same components, i.e., those created by rgb2gray, then use
igray(:,:,3) = rgb2gray(a); % Set last component first to fully allocate array
igray(:,:,1) = igray(:,:,3);
igray(:,:,2) = igray(:,:,3);
Or an RGB image where all the channels are equivalent to the red channel:
igray(:,:,3) = a(:,:,1);
igray(:,:,1) = a(:,:,1);
igray(:,:,2) = a(:,:,1);
The repmat function can be used as well if you prefer.
While nothing in horchler's very long answer is incorrect, I think you just want to get the red channel from the rgb image which is very easy.
A=imread('colorImg.jpg')
redChannel=A(:,:,1)
That's it!
That will return a matrix of type uint8, to convert to double, just do double(redChannel) and you can multipy/divide it by 255 as necessary.