Reduce number of colors in matlab using rgb2ind - matlab

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

Related

How to convert hyperspectral image to RGB in 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:

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!

MATLAB: Matrix with binary info (1 and 0) and image command show nothing

After processing an input I finally have a matrix of N x M x L representing a volume. The values on that matrix are only 0 or 1. When I try to display a "slice" from this volume using image like this:
image(volume(:,:,80))
the displayed figure is all blue. Now if I use imagesc, the image is displayed ok (in blue and red tones). I think this is related to colormap but can't really figure out how to display the images with the image command.
My final goal is to display 3 or 4 slices in one 3d plot, somthing similar to what is shown here: http://www.mathworks.com/help/matlab/visualize/techniques-for-visualizing-scalar-volume-data.html#f5-4457
You are right, your problem is related to colormap. Try
image(volume(:,:,80))
colorbar
You will see that your current colormap ranges from 0 to 64. If you use this command instead:
image(volume(:,:,80),'CDataMapping','scaled')
colorbar
you should get the image you want, and your colormap is now scaled to the range of your data (of course, you don't need to show the colorbar to get the right scaling, I just added it to make things more clear).

Turn a MATLAB plot into image

I have generated a plot like
figure; hold;
axis([0 10 0 10]);
fill([ 1 1 5 5], [5 1 1 5],'b')
and now I want to have this plot as an matrix so that I can i.e. filter the blog with a gaussian. Googleing I found this thread Rasterizing Plot to Image at MATLAB Central. I tried it, but I could only get it to work for line or function plots.
Do you have any ideas?
You can use GETFRAME function. It returns movie frame structure, which is actually rasterized figure. Field cdata will contain your matrix.
F=getframe;
figure(2)
imagesc(F.cdata);
What are the desired characteristics of your target matrix ? And what sort of images do you want to rasterise ?
You see, for the only example you have given us it's almost trivial to define a matrix representing your image ...
1. figmat = ones(10,10,3) % create a 10x10 raster where each entry is a triple for RGB, setting them all to 1 colours the whole raster white
2. figmat(2:5,2:5,1:2) = 0 % sets RG components in the coloured area to 0, leaving only blue
Your matrix is a raster to start with. Now, you can use the built-in function image to visualise your matrix. Have a look at the documentation for that function. And note that my suggestion does not meet the spec for use with image() and colormap().