This is code for serial Gabor filters. The problem I am getting is the statement J = J + abc should return a final filtered image as a superposition of all filters but only the result of the last iteration is displayed:
function [J] = gabor(I)
J = zeros(size(I));
for phi = 5*pi/8:pi/8:pi;
for theta = 1:0.5:2;
for filterSize = 4:6;
sigma = 0.65*theta;
G = zeros(filterSize);
for i=(0:filterSize-1)/filterSize
for j=(0:filterSize-1)/filterSize
xprime= j*cos(phi);
yprime= i*sin(phi);
K = exp(2*pi*theta*sqrt(-1)*(xprime+ yprime));
G(round((i+1)*filterSize),round((j+1)*filterSize)) = exp(-(i^2+j^2)/(sigma^2))*K;
end
end
abc = conv2(double(I),double(G),'same');
J = J + abc;
end
end
end
figure; imshow(J);
end
Here is what the output image looks like:
My gut feeling (or it rather looks like...) is that your image output type is double, and imshow only displays intensities between [0,1] for class double. Any values that are below 0 or above 1 get saturated to black or white respectively, which is why your image output only appears to be black or white. This is also apparent as J is defaulted to a double type. Try doing this for your imshow command so that it stretches the intensities to fit within the [0,1] range:
imshow(J, []);
Take note that this does not modify the image. It only changes how the image is visualized. This imshow command appears at the end of your code, and so change that command here.
By the way, sqrt(-1) is considered bad form. Use 1j or 1i instead, and change your for loop indices so that you're not using i and j as these should be used to represent the complex number. It has been shown by Shai that using i and j as loop indices can lead to poor performance. You should reserve these for the complex number instead. Check this post out for more details: Using i and j as variables in Matlab
Also, you probably want to contrast stretch the image so that it does fit between 0 and 1. As such, do this before you exit your function:
J = im2double(J);
Related
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.
I'm tyring to implement (I know there's a custom function for achieving it) the grayscale image histogram in Matlab, so far I've tried:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values)
However when I call the function, for example: histogram_matlab('Harris.png')
I obtain some graph like:
which is obviously not what I expect, x axis should go from 0 to 255 and y axis from 0 to whatever max value is stored in histogram_values.
I need to obtain something like what imhist offers:
How should I set it up? Am I doing a bad implementation?
Edit
I've changed my code to improvements and corrections suggested by #rayryeng:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = zeros(256,1)
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) +1;
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values, 0:255)
However the histogram plot is not that expected:
Here it's noticeable that there's some issue or error on y axis as it definitely would reach MORE than 2.
In terms of calculating the histogram, the computation of the frequency per intensity is correct though there is a slight error... more on that later. Also, I would personally avoid using loops here. See my small note at the end of this post.
Nevertheless, there are three problems with your code:
Problem #1 - Histogram is not initialized properly
histogram_values should contain your histogram, yet you are initializing the histogram by a vector of 0:255. Each intensity value should start with a count of 0, and so you actually need to do this:
histogram_values = zeros(256,1);
Problem #2 - Slight error in for loop
Your intensities range from 0 to 255, yet MATLAB starts indexing at 1. If you ever get intensities that are 0, you will get an out-of-bounds error. As such, the proper thing to do is to take p and add it with 1 so that you start indexing at 1. However, one intricacy I need to point out is that if you have a uint8 precision image, adding 1 to an intensity of 255 will simply saturate the value to 255. It won't go to 256.... so it's also prudent that you cast to something like double to ensure that 256 will be reached.
Therefore:
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
Problem #3 - Not calling histogram right
You should override the behaviour of histogram and include the edges. Basically, do this:
histogram(histogram_values, 0:255);
The second vector specifies where we should place bars on the x-axis.
Small note
You can totally implement the histogram computation yourself without any for loops. You can try this with a combination of bsxfun, permute, reshape and two sum calls:
mat = bsxfun(#eq, permute(0:255, [1 3 2]), im);
h = reshape(sum(sum(mat, 2), 1), 256, 1);
If you'd like a more detailed explanation of how this code works under the hood, see this conversation between kkuilla and myself: https://chat.stackoverflow.com/rooms/81987/conversation/explanation-of-computing-an-images-histogram-vectorized
However, the gist of it as below.
The first line of code creates a 3D vector of 1 column that ranges from 0 to 255 by permute, and then using bsxfun with the eq (equals) function, we use broadcasting so that we get a 3D matrix where each slice is the same size as the grayscale image and gives us locations that are equal to an intensity of interest. Specifically, the first slice tells you where elements are equal to 0, the second slice tells you where elements are equal to 1 up until the last slice where it tells you where elements are equal to 255.
For the second line of code, once we compute this 3D matrix, we compute two sums - first summing each row independently, then summing each column of this intermediate result. We then get the total sum per slice which tells us how many values there were for each intensity. This is consequently a 3D vector, and so we reshape this back into a single 1D vector to finish the computation.
In order to display a histogram, I would use bar with the histc flag. Here's a reproducible example if we use the cameraman.tif image:
%// Read in grayscale image
openImage = imread('cameraman.tif');
[rows,cols] = size(openImage);
%// Your code corrected
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
%// Show histogram
bar(0:255, histogram_values, 'histc');
We get this:
Your code looks correct. The problem is with the call to histogram. You need to supply the number of bins in the call to histogram, otherwise they will be computed automatically.
Try this simple modification which calls stem to get the right plot, instead of relying on histogram
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
stem(histogram_values); axis tight;
EDIT: After some inspection of the code you have a 0/1 error. If you have a pixel of value zero then histogram_value(p) will give you an index error
Try this instead. No need for vectorization for this simple case:
function hv = histogram_matlab_vec(I)
assert(isa(I,'uint8')); % for now we assume uint8 with range [0, 255]
hv = zeros(1,256);
for i = 1 : numel(I)
p = I(i);
hv(p + 1) = hv(p + 1) + 1;
end
stem(hv); axis tight;
end
I have implemented otsu's threshold implementation which segments an image into foreground and background image.The output of my implementation seems off from the desired one. Any thoughts on it? Thanks in advance! Would really appreciate it someone can tell me how do i resolve the issue.
My output:
For image 1-
For image 2-
My Code:
im1=imread('D:\root-image.pgm');
% im1=rgb2gray(im1);
[n,m]=size(im1);
hst=imhist(im1);
mu=zeros(255,1);
N=0;
for i=1:255
N=N+hst(i);
end
% The total mean level of the original image
for i=1:255
mu(i)=mu(i)+((i.*hst(i))./N);
end
for T=1:254
qb=0;
muT=0;
qo=0;
for i=1:T
qb=qb+(hst(i)./N); % probability of class occurence (background)
m=m+((i.*hst(i))./N);% probability of class mean (background)
end
for i=T+1:255
qo=qo+(hst(i)./N);% probability of class occurence (object)
end
sigma(T)=((mu(T)-(qb*muT))^2)/(qb*qo);
end
[Y,T] = max(sigma);
[n,m]=size(im1);
for i=1:n
for j=1:m
if im1(i,j)>T
im(i,j)=1;
else
im(i,j)=0;
end
end
end
figure(1);
subplot(1,2,1);
imshow(im1);
% subplot(1,3,2);
% imhist(im1);
subplot(1,2,2);
imshow(im);
There are a few issues with your code, and I'll outline where it's wrong:
I'm simply nitpicking, but you can count the total number of pixels (N) by using numel... it's just cleaner :)
In your original code, you are checking for the right threshold between 1 and 254. You really should be checking from 0 to 255, as there are 256 possible intensities in your image.
You also need to change your sigma declaration so that there are 256 elements, not 255. Remember, there are 256 possible intensities in your image.
Within your for loop for checking each intensity, when you are calculating the probability of class occurrences, you need to check for intensity 0 as well. Because of the fact that MATLAB starts indexing arrays at 1, you'll need to offset your access index so that you're starting at 1.
Your definition of the variance between the object and background is slightly off. You need to also calculate the probability of the class mean for the object as well. You can check the code for more details.
Your probability of class mean definitions are slightly inaccurate. You need to divide by qb and qo, not N.
You are using m to calculate then mean when you should be storing it in muT.
Finally, when you find the maximum of the variance between the object and background, you need to subtract by 1, as this will provide an intensity between 0 and 255.
As such, this is what your code looks like. Note that I have omitted the code that thresholds your image. I'm only providing the code that calculates the threshold for your image.
hst=imhist(im1);
sigma = zeros(256,1); %// Change
N = numel(im1); %// Change
for T=0:255 %// Change
qb=0;
muT=0;
qo=0;
muQ=0; %// Change
for i=0:T %// Change
qb=qb+(hst(i+1)./N); % probability of class occurence (background)
end
for i=T+1:255
qo=qo+(hst(i+1)./N);% probability of class occurence (object)
end
for i=0:T%// Change
muT=muT+((i.*hst(i+1))./qb);% probability of class mean (background)
end
for i=T+1:255 %// Change
muQ=muQ+((i.*hst(i+1))./qo);% probability of class mean (object)
end
sigma(T+1) = qb*qo*((muT-muQ)^2); %// Change
end
[Y,T] = max(sigma);
T = T-1; %// Change - For 0 to 255
This code should now work. I ran this code with my own implementation of Otsu, and I get the same calculated threshold. To be honest, I find this code to be rather inefficient due to the many for loops. What I would personally do is vectorize it, but I'll leave that to you as a learning exercise :)
Edit
OK, I'll give in. Here's some code that I wrote for Otsu that is more vectorized. This essentially performs what you are doing above, but in a more vectorized manner. You're more than welcome to use it for your own purposes, but do cite me if you intend to use it of course :)
%// Function that performs Otsu's adaptive bimodal thresholding
%// Written by Raymond Phan - Version 1.0
%// Input - im - Grayscale image
%// Output - out - Thresholded image via Otsu
function [out] = otsu(im)
%// Compute histogram
J = imhist(im);
%// Total number of intensities
L = length(J);
%// Some pre-processing stuff
%// Determine total number of pixels
num_pixels = sum(J(:));
%// Determine PDF
pdf = J / num_pixels;
%// Storing between-class variances for each intensity
s_b = zeros(1,L);
for idx = 1 : L
%// Calculate w_0
w_0 = sum(pdf(1:idx));
%// Calculate w_1
w_1 = 1 - w_0;
%// Calculate u_0
u_0 = sum((0:idx-1)'.*pdf(1:idx)) / w_0;
%// Calculate u_1
u_1 = sum((idx:L-1)'.*pdf(idx+1:L)) / w_1;
% // Calculate \sigma_b^{2}
s_b(idx) = w_0*w_1*((u_1 - u_0)^2);
end
%// Find intensity that provided the biggest variance
[max_s_b T] = max(s_b);
T = T - 1; %// Must subtract by 1, since the index starts from 1
%// Now create an output image that thresholds the input image
out = im >= T;
end
Edits by Divakar
Divakar (thanks!) has created vectorized code to replace the loop portion of the above function code and this essentially gets rid of the pre-allocation for s_b:
w_0 = cumsum(pdf);
w_1 = 1 - w_0;
u_0 = cumsum((0:L-1)'.*pdf)./w_0;
u_1 = flipud([0 ; cumsum((L-1:-1:1)'.*pdf((L:-1:2)))])./w_1;
s_b = w_0.*w_1.*((u_1 - u_0).^2);
i have created a function that represents a triangle sign.
this function does not work on vectors. i want to evaluate a vector x:
x=[-2:0.01:2]
and save the answer in vector y, for this purpose i came up with the following code:
for i=1:400, y(i) = triangle(x(i))
after i got the ans i plotted is using plot. in this case it worked ok but i am interested on observing the influence of time shifting and shrinking so when i try to use lets say:
for i=1:200, y(i) = triangle(x(2*i))
i get a vector y not the same length as vector x and i cant even plot them... is there any easy way to achieve it? and how should i plot the answer?
here is my function:
function [ out1 ] = triangle( input1 )
if abs(input1) < 1,
out1 = 1 - abs(input1);
else
out1 = 0;
end
end
y is a different length in each for loop because each loop iterated a different number of times. In the example below, I use the same for loops and plot y2 with the corresponding values of x. i is already defined in matlab so I've changed it to t in the example below.
clear all
x=[-2:0.01:2];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:200
y2(t) = triangle(x(2*t));
end
Or, if you want to see y2 plotted over the same range you can increase the size of x:
clear all
x=[-2:0.01:8];
for t=1:400
y(t) = triangle(x(t));
end
for t=1:400
y2(t) = triangle(x(2*t));
end
plot(x(1:length(y)),y,'r')
hold on
plot(x(1:length(y2)),y2,'b')
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