How to convert RGB images to grayscale in matlab without using rgb2gray - matlab

I'm currently using code:
i = imread('/usr/share/icons/matlab.png');
for k=1:1:m
for l=1:1:n
%a(k,l)=m*n;
a(k,l) = (.299*i(k,l,1))+(.587*i(k,l,2))+(.114*i(k,l,3));
end
end
imshow(a);
It shows only a white screen. Also the newly generated dimensions are n x m x 3 whereas it should be only m x n x 1.
If I use mat2gray it display the image like this

Since the image is a PNG, imread() is returning an integer image, with intensity values in the range [0 255] or equivalent, depending on the original bit depth. The conversion formula makes a a double image, which is expected to have intensities in the range [0 1]. Since all the pixel values in a are probably much greater than 1, they get clipped to 1 (white) by imshow().
The best option is to explicitly convert the image format before you start - this will take care of scaling things correctly:
i = imread('/usr/share/icons/matlab.png');
i = im2double(i);
a = .299*i(:,:,1) + .587*i(:,:,2) + .114*i(:,:,3); % no need for loops
imshow(a);

input=imread('test.jpg');
subplot(1,2,1), imshow(input), title('RGB Scale image');
[x,y,~] = size(input);
for i = 1:1:x
for j = 1:1:y
output(i,j) = 0.40*input(i,j,1) + 0.50*input(i,j,2) + 0.30*input(i,j,3);
end
end
subplot(1,2,2), imshow(output), title('Gray Scale image');

Related

Interpolation algorithm all values 255 -MATLAB

I trying to create my own nearest neighbor interpolation algorithm in Matlab to enlarge an image of 556×612 to 1668×1836.
This is homework!!!
I have attempted this already but encounter the error where the values inside M gets (not all but most) transformed to 255 (White Space), and I cannot get my head around why. Any help would be appreciated! The picture is a picture of a zebra.
%Take in image and convert to greyscale
I = imread('Zebra.jpg');
Igray = rgb2gray(I);
% Step-3: Resize the image to enlarged 1668x1836 by interpolation
% Step-3(a) : Using nearest neighbour
%First we will need to work out the dimension of the image
[j , k] = size(Igray);
%Now we need to set the size of the image we want
NewX = 1836;
NewY = 1668;
% Work out ratio of old to new
ScaleX = NewX./(j-1);
ScaleY = NewY./(k-1);
%Image Buffer
M = zeros(NewX, NewY);
%Create output image
for count1 = 1:NewX
for count2 = 1:NewY
M(count1,count2) = Igray(1+round(count1./ScaleX),1+round(count2./ScaleY));
end
end
%Show Images
imshow(M);
title('Scaled Image NN');
try imshow(M,[]). You created M without specifying type, which makes it double. double images are [0-1], so imshow by default makes white everything with higher value than 1.
Alternatively, create M to be uint8 as the original image
M = zeros(NewX, NewY,'uint8');
even better code would be:
M = zeros(NewX, NewY,class(Igray));

Histogram of binary image in MATLAB

I'm trying to do a vertical histogram of a binary image. I don't want to use MATLAB's functions. How to do it?
I have tried this code but I don't know if it's correct or not:
function H = histogram_binary(image)
[m,n] = size(image);
H = zeros(1,256);
for i = 1:m
for j = 1:n
H(1,image(i,j)) = H(1,image(i,j))+1;
end
end
The image is:
The result:
Why can't I see the value of black pixels in the histogram?
% Read the binary image...
img = imread('66He7.png');
% Count total, white and black pixels...
img_vec = img(:);
total = numel(img_vec);
white = sum(img_vec);
black = total - white;
% Plot the result in the form of an histogram...
bar([black white]);
set(gca(),'XTickLabel',{'Black' 'White'});
set(gca(),'YLim',[0 total]);
Output:
For what concerns your code, it is not counting black pixels since they have a value of 0 and your loop start from 1... rewrite it as follows:
function H = histogram_binary(img)
img_vec = img(:);
H = zeros(1,256);
for i = 0:255
H(i+1) = sum(img_vec == i);
end
end
But keep in mind that counting all the byte occurrences on a binary image (that can only contain 0 or 1 values) is kinda pointless and will make your histogram lack readability. On a side note, avoid using image as a variable name, since this would override an existing function.
As mentioned by #beaker in the comments above, vertical histogram in such cases generally refers to a vertical projection. Here is a way to do this :
I = imread('YcP1o.png'); % Read input image
I1 = rgb2gray(I); % convert image to grayscale
I2 = im2bw(I1); % convert grayscale to binary
I3 = ~I2; % invert the binary image
I4 = sum(I3,1); % project the inverted binary image vertically
I5 = (I4>1); % ceil the vector
plot([1:1:size(I,2)],I5); ylim([0 2])
You can further check for 0->1 transitions to count the number of characters using sum(diff(I5)>0) which gives 13 as answer in this case.

Contrast Stretching for colore images with MATLAB

i'm working in matlab and i wanted to apply the Contrast Stretching for grey scale image and also RGB image ,
so for the grey scale i've tried this one and it worked
clear all;
clc;
itemp = imread('cameraman.tif'); %read the image
i = itemp(:,:,1);
rtemp = min(i); % find the min. value of pixels in all the
columns (row vector)
rmin = min(rtemp); % find the min. value of pixel in the image
rtemp = max(i); % find the max. value of pixels in all the
columns (row vector)
rmax = max(rtemp); % find the max. value of pixel in the image
m = 255/(rmax - rmin); % find the slope of line joining point
(0,255) to (rmin,rmax)
c = 255 - m*rmax; % find the intercept of the straight line
with the axis
i_new = m*i + c; % transform the image according to new slope
figure,imshow(i); % display original image
figure,imshow(i_new); % display transformed image
this is for greyscale image ,
the problem is that that i don't know how to do for the RGB image
any idea? how to implement that?
thank you :)
Could the function stretchlim (reference) be useful for your purpose?
Find limits to contrast stretch image.
Low_High = stretchlim(RGB,Tol) returns Low_High, a two-element vector
of pixel values that specify lower and upper limits that can be used
for contrast stretching truecolor image RGB.
img = imread('myimg.png');
lohi = stretchlim(img,[0.2 0.8]);
If you write
rmin = min(i(:));
Then it computes the minimum over all values in i. This will work for RGB images also, which simply are 3D matrices with 3 values along the 3rd dimension.
The rest of your code also applies directly to such images.

Speeding up Octave / Matlab plot

Gnoivce and Hartmut helped a lot with this code but it takes a while to run.
The CData property in the bar command doesn't seem to be implemented in the Octave 4.0-4.2.1 version which I'm using. The work around for this was to plot all the single bars individually and set an individual color for each individual bar. People helped me out and got me this far but it takes 5 minutes for the plot to show does anyone know a way of speeding this up?
The following code runs:
marbles.jpg image file used below:
clear all,clf reset,tic,clc
rgbImage = imread('/tmp/marbles.jpg');
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:360; %# Edges of histogram bins
N = histc(hPlane(:),binEdges); %# Bin the pixel hues from above
C = colormap(hsv(360)); %# create an HSV color map with 360 points
stepsize = 1; % stepsize 1 runs for a while...
for n=binEdges(2:stepsize:end) %# Plot the histogram, one bar each
if (n==1), hold on, end
h=bar(n,N(n));
set(h,'FaceColor',C(n,:)); %# set the bar color individually
end
axis([0 360 0 max(N)]); %# Change the axes limits
set(gca,'Color','k'); %# Change the axes background color
set(gcf,'Pos',[50 400 560 200]); %# Change the figure size
xlabel('HSV hue (in degrees)'); %# Add an x label
ylabel('Bin counts'); %# Add a y label
fprintf('\nfinally Done-elapsed time -%4.4fsec- or -%4.4fmins- or -%4.4fhours-\n',toc,toc/60,toc/3600);
Plot created after 5 mins:
To see original question original question
I'm guessing the loop is the bottleneck in your code that is taking so long? You could remove the loop and create the plot with one call to bar, then call set to modify the hggroup object and its child patch object:
h = bar(binEdges(1:end-1), N(1:end-1), 'histc'); % hggroup object
set(h, 'FaceColor', 'flat', 'EdgeColor', 'none');
hPatch = get(h, 'Children'); % patch object
set(hPatch, 'CData', 1:360, 'CDataMapping', 'direct');
Repeating your code with this fix renders right away for me in Octave 4.0.3:
As I suggested in a comment, I would use image (takes 0.12s on my system for your image).
EDIT: more comments, fix little bug, allow to create bins with stepsize > 1
img_fn = "17S9PUK.jpg";
if (! exist (img_fn, "file"))
disp ("downloading image from imgur.com...");
fflush (stdout);
urlwrite ("http://i.imgur.com/17S9PUK.jpg", "17S9PUK.jpg");
endif
rgbImage = imread (img_fn);
## for debugging so the matrixes fit on screen
if (0)
pkg load image
rgbImage = imresize (rgbImage, [6 8]);
endif
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
## create bins, I've choosen 2 step to "smooth" the result
binEdges = 1:2:360;
N = histc (hPlane(:), binEdges)';
cm = permute (hsv (numel (binEdges)), [3 1 2]);
## Create an image with x = hue
img = repmat (cm, max(N), 1);
## Create binary mask which is used to black "img" dependent on N
sp = sparse (N(N > 0), (1:360)(N > 0), true, max(N), numel (binEdges));
mask = full (cumsum (flipud (sp)));
## extend mask in depth to suppress RGB
mask = repmat (mask, [1 1 3]);
## use inverted mask to "black out" pixels < N
img(logical (1 - flipud (mask))) = 0;
## show image
image (binEdges, 1:max(N), img)
set (gca, "ydir", "normal");
xlabel('HSV hue (in degrees)');
ylabel('Bin counts');
## print it for stackoverflow
print ("out.png")
Same as above but with bin width 1 (Elapsed time is 0.167423 seconds.)
binEdges = 1:360;

Roberts Operator just makes the image brighter

I posted another question about the Roberts operator, but I decided to post a new one since my code has changed significantly since that time.
My code runs, but it does not generate the correct image, instead the image becomes slightly brighter.
I have not found a mistake in the algorithm, but I know this is not the correct output. If I compare this program's output to edge(<image matrix>,'roberts',<threshold>);, or to images on wikipedia, it looks nothing like the effect of the roberts operator shown there.
code:
function [] = Robertize(filename)
Img = imread(filename);
NewImg = Img;
SI = size(Img);
I_W = SI(2)
I_H = SI(1)
Robertsx = [1,0;0,-1];
Robertsy = [0,-1;1,0];
M_W = 2; % do not need the + 1, I assume the for loop means while <less than or equal to>
% x and y are reversed...
for y=1 : I_H
for x=1 : I_W
S = 0;
for M_Y = 1 : M_W
for M_X = 1 : M_W
if (x + M_X - 1 < 1) || (x + M_X - 1 > I_W)
S = 0;
%disp('out of range, x');
continue
end
if (y + M_Y - 1 < 1) || (y + M_Y - 1 > I_H)
S = 0;
%disp('out of range, y');
continue
end
S = S + Img(y + M_Y - 1 , x + M_X - 1) * Robertsx(M_Y,M_X);
S = S + Img(y + M_Y - 1, x + M_X - 1) * Robertsy(M_Y,M_X);
% It is y + M_Y - 1 because you multiply Robertsx(1,1) *
% Img(y,x).
end
end
NewImg(y,x) = S;
end
end
imwrite(NewImg,'Roberts.bmp');
end
I think you may be misinterpreting how the Roberts Cross operator works. Use this page as a guide. Notice that it states that you convolve the original image separately with the X and Y operator. Then, you may calculate the final gradient (i.e. "total edge content") value by taking the square root of the sum of squares of the two (x and y) gradient values for a particular pixel. You're presently summing the x and y values into a single image, which will not give the correct results.
EDIT
I'll try to explain a bit better. The problem with summation instead of squaring/square root is that you can end up with negative values. Negative values are natural using this operator depending on the edge orientation. That may be why you think the image 'lightens' -- because when you display the image in MATLAB the negative values go to black, the zero values go to grey, and the positive values go to white. Here's the image I get when I run your code (with a few changes -- mostly setting NewImg to be zeros(size(Img)) so it's a double type instead of uint8. uint8 types don't allow negative values... Here's the image I get:.
You have to be very careful when trying to save files as well. Instead of calling imwrite, call imshow(NewImg,[]). That will automatically rescale the values in the double-valued image to show them correctly, with the most negative number being equal to black and most positive equal to white. Thus, in areas with little edge content (like the sky), we would expect grey and that's what we get!
I ran your code and got the effect you described. See how everything looks lighter:
Figure 1 - Original on the left, original roberts transformation on the right
The image on my system was actually saturated. My image was uint8 and the operations were pushing the image past 255 or under 0 (for the negative side) and everything became lighter.
By changing the line of code in the imread to convert to double as in
Img = double(rgb2gray( imread(filename)));
(note my image was color so I did an rgb conversion, too. You might use
Img = double(( imread(filename)));
I got the improved image:
Original on left, corrected code on right.
Note that I could also produce this result using 2d convolution rather than your loop:
Robertsx = [1,0;0,-1];
Robertsy = [0,-1;1,0];
dataR = conv2(data, Robertsx) + conv2(data, Robertsy);
figure(2);
imagesc(dataR);
colormap gray
axis image
For the following result:
Here is an example implementation. You could easily replace CONV2/IMFILTER with your own 2D convolution/correlation function:
%# convolve image with Roberts kernels
I = im2double(imread('lena512_gray.jpg')); %# double image, range [0,1]
hx = [+1 0;0 -1]; hy = [0 +1;-1 0];
%#Gx = conv2(I,hx);
%#Gy = conv2(I,hy);
Gx = imfilter(I,hx,'conv','same','replicate');
Gy = imfilter(I,hy,'conv','same','replicate');
%# gradient approximation
G = sqrt(Gx.^2+Gy.^2);
figure, imshow(G), colormap(gray), title('Gradient magnitude [0,1]')
%# direction of the gradient
Gdir = atan2(Gy,Gx);
figure, imshow(Gdir,[]), title('Gradient direction [-\pi,\pi]')
colormap(hot), colorbar%, caxis([-pi pi])
%# quiver plot
ySteps = 1:8:size(I,1);
xSteps = 1:8:size(I,2);
[X,Y] = meshgrid(xSteps,ySteps);
figure, imshow(G,[]), hold on
quiver(X, Y, Gx(ySteps,xSteps), Gy(ySteps,xSteps), 3)
axis image, hold off
%# binarize gradient, and compare against MATLAB EDGE function
BW = im2bw(G.^2, 6*mean(G(:).^2));
figure
subplot(121), imshow(BW)
subplot(122), imshow(edge(I,'roberts')) %# performs additional thinning step