Hough transform in MATLAB without using hough function - matlab

I found an implementation of the Hough transform in MATLAB at Rosetta Code, but I'm having trouble understanding it. Also I would like to modify it to show the original image and the reconstructed lines (de-Houghing).
Any help in understanding it and de-Houghing is appreciated. Thanks
Why is the image flipped?
theImage = flipud(theImage);
I can't wrap my head around the norm function. What is its purpose, and can it be avoided?
EDIT: norm is just a synonym for euclidean distance: sqrt(width^2 + height^2)
rhoLimit = norm([width height]);
Can someone provide an explanation of how/why rho, theta, and houghSpace is calculated?
rho = (-rhoLimit:1:rhoLimit);
theta = (0:thetaSampleFrequency:pi);
numThetas = numel(theta);
houghSpace = zeros(numel(rho),numThetas);
How would I de-Hough the Hough space to recreate the lines?
Calling the function using a 10x10 image of a diagonal line created using the identity (eye) function
theImage = eye(10)
thetaSampleFrequency = 0.1
[rho,theta,houghSpace] = houghTransform(theImage,thetaSampleFrequency)
The actual function
function [rho,theta,houghSpace] = houghTransform(theImage,thetaSampleFrequency)
%Define the hough space
theImage = flipud(theImage);
[width,height] = size(theImage);
rhoLimit = norm([width height]);
rho = (-rhoLimit:1:rhoLimit);
theta = (0:thetaSampleFrequency:pi);
numThetas = numel(theta);
houghSpace = zeros(numel(rho),numThetas);
%Find the "edge" pixels
[xIndicies,yIndicies] = find(theImage);
%Preallocate space for the accumulator array
numEdgePixels = numel(xIndicies);
accumulator = zeros(numEdgePixels,numThetas);
%Preallocate cosine and sine calculations to increase speed. In
%addition to precallculating sine and cosine we are also multiplying
%them by the proper pixel weights such that the rows will be indexed by
%the pixel number and the columns will be indexed by the thetas.
%Example: cosine(3,:) is 2*cosine(0 to pi)
% cosine(:,1) is (0 to width of image)*cosine(0)
cosine = (0:width-1)'*cos(theta); %Matrix Outerproduct
sine = (0:height-1)'*sin(theta); %Matrix Outerproduct
accumulator((1:numEdgePixels),:) = cosine(xIndicies,:) + sine(yIndicies,:);
%Scan over the thetas and bin the rhos
for i = (1:numThetas)
houghSpace(:,i) = hist(accumulator(:,i),rho);
end
pcolor(theta,rho,houghSpace);
shading flat;
title('Hough Transform');
xlabel('Theta (radians)');
ylabel('Rho (pixels)');
colormap('gray');
end

The Hough Transform is a "voting" approach where each image point casts a vote on the existence of a certain line (not a line segment) in an image. The voting is carried out in the parameter space for a line: the polar coordinate representation of normal vectors.
We discretize the parameter space and allow each image point to suggest parameters which would be compatible with a line through the point. Each of your questions can be addressed in terms of how the parameter space is treated in code. Wikipedia has a good article with worked examples that might clarify things (if you are having any conceptual troubles).
For your specific questions:
The image is flipped so the origin is the bottom right corner. As far as I can tell this step is not technically necessary. It does change the outcome somewhat due to discretization issues. The other implementations on Rosetta Code do not flip the image.
rhoLimit holds the maximum radius of an image point in polar coordinates (recall the norm of a vector is its magnitude).
rho and theta are discretizations of the polar coordinate plane according to a sampling rate. houghSpace creates a matrix with an element for each possible combination of the discrete rho/theta values.
The Hough Transform does not specify the lengths of putative lines; the peaks in the voting space just specify the polar coordinates of the normal vector of the line. You can "de-Hough" by selecting the peaks and drawing the corresponding lines, or perhaps by drawing every possible line and using the number of votes as a grayscale weight. It is not possible to re-create the original image from the Hough Transform, just the lines identified by the transform (and your thresholding scheme on the votes).
Following the example from the question produces the following graph. The placement of grid lines and the datatips cursor can be a bit misleading (though the variable values in the 'tip are correct). Since this is an image of the parameter space and not the image space the sampling rate we chose is determining the number of bins in each variable. At this sampling rate, the image points are compatible with more than one possible line; in other words our lines have subpixel resolution, in the sense that they cannot be drawn without overlap in a 10x10 image.
Once we have chosen a peak, such as that corresponding to the line with normal (rho,theta) = (6.858,0.9), we can draw that line in an image however we choose. Automated peak picking, that is thresholding to find the highly up-voted lines, is its own problem - you could ask a another question about the topic in DSP or about a particular algorithm here.
For example methods see the code and documentation of MATLAB's houghpeaks and houghlines functions.

Related

finding center of cropped circle using MATLAB and no image processing toolbox

I have a large set of images (~2000) that show a grayscale picture of cropped circles. Each image is one circle with clear background. the images are homogeneous and so is the background. The images are almost not noisy. Image size is about 700x700 pixels.
some of the images are cropped, in the sense that a part of them is outside the image boundaries. the circles are about 1/2 size of the image.
I have Matlab, but no image processing toolbox.
I have prior information about the radius of the images. I would appreciate receiving it from the algorithm for validation purposes, but I can use it a-priori.
How do I get the center and radius of the circles?
Thanks!
This is a typical application for the Hough transform, but since you have only one circle, we can do a little bit better.
The code below computes the gradient of the image. You have a lot of noise, but your circle is also very large. I'm using a large sigma for the Gaussian regularization that the gradient operator uses (I like to use convolution with the derivative of the Gaussian to compute derivatives). Next, I find the pixels with the largest gradient magnitude, and set up a system of equations for these points. We note that, for each point i,
origin_x + radius * gradient_x(i) = coordinate_x(i)
origin_y + radius * gradient_y(i) = coordinate_y(i)
(sorry, we don't get to do proper equations on SO). coordinate is the coordinate of the point, and gradient is the normalized gradient at that point, and the _x and _y indicate the corresponding component of the vector. radius can be negative, depending on the direction of the gradient. We can solve this system of linear equations with MATLAB's \ operator.
% Load image (take only first channel, they're all the same)
img = imread('https://i.stack.imgur.com/wAwdh.jpg');
img = dip_image(img(:,:,1));
% Compute gradient
grad = gradient(img,10);
% Find N pixels with largest gradient magnitude
N = 5000;
mask = norm(grad);
mask = setborder(mask,0,50); % Don't use data close to the edge of the image
tmp = sort(double(mask(:)));
mask = mask > tmp(end-N);
index = find(mask);
value = grad(index);
value = value / norm(value);
coords = ind2sub(mask,index); % value(i) at coords(i,:)
% Solve set of linear equations
p = [ones(N,1),zeros(N,1),double(value{1})';zeros(N,1),ones(N,1),double(value{2})'] \ coords(:);
origin = p(1:2)'
radius = p(3)
rmse = sqrt(mean((origin + radius * squeeze(double(value))' - coords).^2))
% Plot some of the lines
img
hold on
for ii=1:25:N
plot(coords(ii,1)-[0,radius*double(value{1}(ii-1))],coords(ii,2)-[0,radius*double(value{2}(ii-1))],'r-')
end
Output:
origin =
-2.5667 177.5305
radius =
322.5899
rmse =
13.8160 13.0136
As you can see, the noise causes a lot of trouble estimating the gradient. But because there is no bias in the estimate at each pixel, the least squares estimate should lead to an accurate value.
The code above uses DIPimage 3, which is an open-source image analysis toolbox for MATLAB (Apache License). You'll have to compile it yourself, because we don't have a pre-compiled release package yet. You can instead download DIPimage 2.9, which has the same functionality, though I might have used some new syntax in the code above, I'm not sure. DIPimage 2.9 is not open source, and is free for use only in non-commercial applications.

plot polar grey values in matrix without interpolating every for loop

I have a matrix with grey values between 0 and 1. For every entry in the matrix, there are certain polar coordinates that indicate the position of the grey values. I already have either Theta and Rho values (polar) ,both in separate 512×960 matrices. And grayscale values (in a matrix called C) for every Theta and Rho combination. I have the same for X and Y, as I just use pol2cart for the transformation. The problem is that I cannot directly plot these values, as they do not yet fit in the 'bins' of the new matrix.
What I want: to put the grey values in a square matrix of size 1024×1024. I cannot do this directly, because the polar coordinates fall in between the grid of this matrix. Therefore, we now use interpolation, but this is extremely time consuming and has to be done separately for every dataset, although the transformation from the original matrices to this final one will always be the same. Therefore, I'd like to solve this matrix once (either analytically or numerically) and use a matrix multiplication or something similar to apply the manipulation efficiently in every cycle of the code.
One example of what one of these transformations could look like this:
The zeros in the first matrix are the grid, and the value 1 (in between the grid) is the grey value that falls in between four grid points, then I'd like to transform to the second matrix (don't mind the visual spacing between the points).
For every dataset, I have hundreds of these matrices, so I would like to make the code more efficient.
Background: I'm using TriScatteredInterp now for the interpolation. We tried scatteredInterpolant as well, but that is slower. I also posted a related question, but decided to split the two possible solutions, because the solution I ask for here is also applicable to non-MATLAB code and will probably be faster and makes for a smoother (no continuous popping up of figures) execution of the code.
Using the image processing toolbox
Images work a bit differently than the data you have. However, it's fairly straightforward to map one representation into the other.
There is only one problem I see: wrapping. Obviously, θ = 2π = 0, but MATLAB does not know that. AFAIK, there is no easy way to tell MATLAB that.
Why does this matter? Well, simply put, inter-pixel interpolation uses information from the nearest N neighbors to find intermediate colors, with N depending on the interpolation kernel. When doing this somewhere in the middle of the image there is no problem, but at the edges MATLAB has to know that the left edge equals the right edge. This is not standard image processing, and I'm not aware of any function that is capable of this.
Implementation
Now, when disregarding the wrapping problem, this is one way to do it:
function resize_polar()
%% ORIGINAL IMAGE
% ==========================================================================
% Some random greyscale data
C = double(rgb2gray(imread('stars.png')))/255;
% Your current size, and desired size
sz_x = size(C,2); new_sz_x = 1024;
sz_y = size(C,1); new_sz_y = 1024;
% Ranges for teat and rho;
% replace with your actual values
rho_start = 0; theta_start = 0;
rho_end = 10; theta_end = 2*pi;
% Generate regularly spaced grid;
theta = linspace(theta_start, theta_end, sz_x);
rho = linspace(rho_start, rho_end, sz_y);
[theta, rho] = meshgrid(theta,rho);
% Make plot of generated data
plot_polar(theta, rho, C, 'Original image');
% Resize data
[theta,rho,C] = resize_polar_data(theta, rho, C, [new_sz_y new_sz_x]);
% Make plot of generated data
plot_polar(theta, rho, C, 'Rescaled image');
end
function [theta,rho,data] = resize_polar_data(theta,rho,data, new_dims)
% Create fake RGB image cube
IMG = cat(3, theta,rho,data);
% Rescale as if theta and rho are RG color data in the RGB
% image cube
IMG = imresize(IMG, new_dims, 'nearest');
% Split up the data again
theta = IMG(:,:,1);
rho = IMG(:,:,2);
data = IMG(:,:,3);
end
function plot_polar(theta, rho, data, label)
[X,Y] = pol2cart(theta, rho);
figure('renderer', 'opengl')
clf, hold on
surf(X,Y,zeros(size(X)), data, ...
'edgecolor', 'none');
colormap gray
title(label);
end
The images used and plotted:
Le awesomely-drawn 512×960 PNG image
Now, the two look the same (couldn't really come up with a better-suited image), so you'll have to believe me that the 512×960 has indeed been rescaled to 1024×1024, with nearest-neighbor interpolation.
Here are some timings for the actual imresize() operation for some simple kernels:
nearest : 0.008511 seconds.
bilinear: 0.019651 seconds.
bicubic : 0.025390 seconds. <-- default kernel
But this depends strongly on your hardware; I believe imresize offloads a lot of work to the GPU, so if you have a crappy one, it'll be slower.
Wrapping
If the wrapping problem is really important to you, you can modify the function above to do the following:
first, rescale the image with imresize() like before
horizontally concatenate the second half of the grayscale data and the first half. Meaning, you swap the first and second halves to make the left and right edges (0 and 2π) touch in the middle.
rescale this intermediate image with imresize()
Extract the central vertical strip of the rescaled intermediate image
split that up in two equal-width strips
and replace the edge strips of the output image with the two strips you just created
Now, this is kind of a brute force approach: you are re-scaling an image twice, and most of the pixels of the second image round will be discarded. If performance is a problem, you can of course apply the rescale to only the central strip of that intermediate image. But, well, that will be a bit more complicated.

Matlab area calculation

If I have an arbitrary shape (attached is a really simple mock up), how would I estimate the area of the enclosed surface in Matlab. To get some random points along the curve, I used the ginput command to get a rough estimate of the curve, with unequal spacing between the points. I want to get an estimate of the area, but I believe the trapz command would overestimate the area due to the overlap (Please correct me if I am wrong here). Is there a more accurate way to obtain the area?
Thanks!
Well, you didn't really give enough info to solve the problem entirely, but here's one approach you can take to find the boundary automatically in order to calculate the area:
% Get image and convert to logical mask
img = ~im2bw(imread('polyarea.jpg'));
% Fill the hole
img = imfill(img,'holes');
% Get boundary
b = bwboundaries(img);
% Approximate area of boundary
area = polyarea(b{1}(:,1), b{1}(:,2));
% Print area
disp(['Area: ' num2str(area)]);
imshow(img);
hold on;
plot(b{1}(:,2),b{1}(:,1),'go');
The idea is you have an input, you form a logical mask, get the boundary of the mask, and then you can approximate the area enclosed by the boundary by using polyarea.
The output is:
Area: 228003
Additionally, you could also use regionprops(img,'Area') which outputs:
ans =
Area: 229154

Decomposing an image into two frequency components using DCT?

I am a beginner in digital image processing field, recently I am working on a project where I have to decompose an image into two frequency components namely (low and high) using DCT. I searched a lot on web and I found that MATLAB has a built-in function for Discrete Cosine Transform which is used like this:
dct_img = dct2(img);
where img is input image and dct_img is resultant DCT of img.
Question
My question is, "How can I decompose the dct_img into two frequency components namely low and high frequency components".
As you've mentioned, dct2 and idct2 will do most of the job for you. The question that remains is then: What is high frequency and what is low frequency content? The coefficients after the 2 dimensional transform will actually represent two frequencies each (one in x- and one in y-direction). The following figure shows the bases for each coefficient in an 8x8 discrete cosine transform:
Therefore, that question of low vs. high can be answered in different ways. A common way, which is also used in the JPEG encoding, proceeds diagonally from zero-frequency downto the max as shown above. As we can see in the following example that is mostly motivated because natural images are largely located in the "top left" corner of "low" frequencies. It is certainly worth looking at the result of dct2 and play around with the actual choice of your regions for high and low.
In the following I'm dividing the spectrum diagonally and also plotting the DCT coefficients - in logarithmic scale because otherwise we would just see one big peak around (1,1). In the example I'm cutting far above half of the coefficients (adjustable with cutoff) we can see that the high-frequency part ("HF") still contains some relevant image information. If you set cutoff to 0 or below only noise of small amplitude will be left.
%// Load an image
Orig = double(imread('rice.png'));
%// Transform
Orig_T = dct2(Orig);
%// Split between high- and low-frequency in the spectrum (*)
cutoff = round(0.5 * 256);
High_T = fliplr(tril(fliplr(Orig_T), cutoff));
Low_T = Orig_T - High_T;
%// Transform back
High = idct2(High_T);
Low = idct2(Low_T);
%// Plot results
figure, colormap gray
subplot(3,2,1), imagesc(Orig), title('Original'), axis square, colorbar
subplot(3,2,2), imagesc(log(abs(Orig_T))), title('log(DCT(Original))'), axis square, colorbar
subplot(3,2,3), imagesc(log(abs(Low_T))), title('log(DCT(LF))'), axis square, colorbar
subplot(3,2,4), imagesc(log(abs(High_T))), title('log(DCT(HF))'), axis square, colorbar
subplot(3,2,5), imagesc(Low), title('LF'), axis square, colorbar
subplot(3,2,6), imagesc(High), title('HF'), axis square, colorbar
(*) Note on tril: The lower triangle-function operates with respect to the mathematical diagonal from top-left to bottom-right, since I want the other diagonal I'm flipping left-right before and afterwards.
Also note that this kind of operations are not usually applied to entire images, but rather to blocks of e.g. 8x8. Have a look at blockproc and this article.
An easy example:
I2 = dct_img;
I2(8:end,8:end) = 0;
I3 = idct2(I2);
imagesc(I3)
I3 can be seen as the image after low pass filter (the low frequency components), then idct2(dct_img - I2) can be viewed as high frequency.

Detecting center point of cross using Matlab

Hello, I have an image as shown above. Is it possible for me to detect the center point of the cross and output the result using Matlab? Thanks.
Here you go. I'm assuming that you have the image toolbox because if you don't then you probably shouldn't be trying to do this sort of thing. However, all of these functions can be implemented with convolutions I believe. I did this process on the image you presented above and obtained the point (139,286) where 138 is the row and 268 is the column.
1.Convert the image to a binary image:
bw = bw2im(img, .25);
where img is the original image. Depending on the image you might have to adjust the second parameters (which ranges from 0 to 1) so that you only get the cross. Don't worry about the cross not being fully connected because we'll remedy that in the next step.
2.Dilate the image to join the parts. I had to do this twice because I had to set the threshold so low on the binary image conversion (some parts of your image were pretty dark). Dilation essentially just adds pixels around existing white pixels (I'll also be inverting the binary image as I send it into bwmorph because the operations are made to act on white pixels which are the ones that have a value of 1).
bw2 = bwmorph(~bw, 'dilate', 2);
The last parameter says how many times to do the dilation operation.
3.Shrink the image to a point.
bw3 = bwmorph(bw2, 'shrink',Inf);
Again, the last parameter says how many times to perform the operation. In this case I put in Inf which shrinks until there is only one pixel that is white (in other words a 1).
4.Find the pixel that is still a 1.
[i,j] = find(bw3);
Here, i is the row and j is the column of the pixel in bw3 such that bw3(i,j) is equal to 1. All the other pixels should be 0 in bw3.
There might be other ways to do this with bwmorph, but I think that this way works pretty well. You might have to adjust it depending on the picture too. I can include images of each step if desired.
I just encountered the same kind of problem, and I found other solutions that I would like to share:
Assume image file name is pict1.jpg.
1.Read input image, crop relevant part and covert to Gray-scale:
origI = imread('pict1.jpg'); %Read input image
I = origI(32:304, 83:532, :); %Crop relevant part
I = im2double(rgb2gray(I)); %Covert to Grayscale and to double (set pixel range [0, 1]).
2.Convert image to binary image in robust approach:
%Subtract from each pixel the median of its 21x21 neighbors
%Emphasize pixels that are deviated from surrounding neighbors
medD = abs(I - medfilt2(I, [21, 21], 'symmetric'));
%Set threshold to 5 sigma of medD
thresh = std2(medD(:))*5;
%Convert image to binary image using above threshold
BW = im2bw(medD, thresh);
BW Image:
3.Now I suggest two approaches for finding the center:
Find find centroid (find center of mass of the white cluster)
Find two lines using Hough transform, and find the intersection point
Both solutions return sub-pixel result.
3.1.Find cross center using regionprops (find centroid):
%Find centroid of the cross (centroid of the cluster)
s = regionprops(BW, 'centroid');
centroids = cat(1, s.Centroid);
figure;imshow(BW);
hold on, plot(centroids(:,1), centroids(:,2), 'b*', 'MarkerSize', 15), hold off
%Display cross center in original image
figure;imshow(origI), hold on, plot(82+centroids(:,1), 31+centroids(:,2), 'b*', 'MarkerSize', 15), hold off
Centroid result (BW image):
Centroid result (original image):
3.2 Find cross center by intersection of two lines (using Hough transform):
%Create the Hough transform using the binary image.
[H,T,R] = hough(BW);
%ind peaks in the Hough transform of the image.
P = houghpeaks(H,2,'threshold',ceil(0.3*max(H(:))));
x = T(P(:,2)); y = R(P(:,1));
%Find lines and plot them.
lines = houghlines(BW,T,R,P,'FillGap',5,'MinLength',7);
figure, imshow(BW), hold on
L = cell(1, length(lines));
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
% Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
%http://robotics.stanford.edu/~birch/projective/node4.html
%Find lines in homogeneous coordinates (using cross product):
L{k} = cross([xy(1,1); xy(1,2); 1], [xy(2,1); xy(2,2); 1]);
end
%https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
%Lines intersection in homogeneous coordinates (using cross product):
p = cross(L{1}, L{2});
%Convert from homogeneous coordinate to euclidean coordinate (divide by last element).
p = p./p(end);
plot(p(1), p(2), 'x', 'LineWidth', 1, 'Color', 'white', 'MarkerSize', 15)
Hough transform result:
I think that there is a far simpler way of solving this. The lines which form the cross-hair are of equal length. Therefore it in will be symmetric in all orientations. So if we do a simple line scan horizontally as well as vertically, to find the extremities of the lines forming the cross-hair. the median of these values will give the x and y co-ordinates of the center. Simple geometry.
I just love these discussions of how to find something without defining first what that something is! But, if I had to guess, I’d suggest the center of mass of the original gray scale image.
What about this;
a) convert to binary just to make the algorithm faster.
b) Perform a find on the resulting array
c) choose the element which has either lowest/highest row/column index (you would have four points to choose from then
d) now keep searching neighbours
have a global criteria for search that if search does not result in more than a few iterations, the point selected is false and choose another extreme point
e) going along the neighbouring points, you will end up at a point where you have three possible neighbours.That is you intersection
I would start by using the grayscale image map. The darkest points are on the cross, so discriminating on the highest values is a starting point. After discrimination, set all the lower points to white and leave the rest as they are. This would maximize the contrast between points on the cross and points in the image. Next up is to come up with a filter for determining the position with the highest average values. I would step through the entire image with a NxM array and take the mean value at the center point. Create a new array of these means and you should have the highest mean at the intersection. I'm curious to see how someone else may try this!