Houghlines in MATLAB - matlab

After detecting the lines in an image using Hough lines, how can I use it to calculate the change in angle (rotation) of the lines of a reference image?

Note to readers: This is a follow-up question, refer to these for background:
How to select maximum intensity in Hough transform in MATLAB?
Calculating displacement moved in MATLAB
The process is similar to what I showed before. Below I am using the images from your previous question (since you provided only one, I created the other by rotating the first by 10 degrees).
We start by detecting the lines for the two images. We do this with the help of the Hough transform functions. This what it looks like applied to both images:
Next, we want to perform image registration using the line endpoints as control-points. First, we make sure the points correspond to each other in the two images. I do this by computing the convex hull using convhull which automatically sorts them in counterclockwise-order (or is it in the opposite direction!). The numbers shown above indicate the order.
Finally, we use the function cp2tform to get the transformation matrix, which we use to align the images and extract the translation, rotation, and scaling.
The following is the complete code:
%% # Step 1: read and prepare images
%# (since you provided only one, I created the other by rotating the first).
I1 = imread('http://i.stack.imgur.com/Se6zX.jpg');
I1 = rgb2gray( imcrop(I1, [85 35 445 345]) ); %# Get rid of white border
I2 = imrotate(I1, -10, 'bilinear', 'crop'); %# Create 2nd by rotating 10 degrees
%% # Step 2: detect the cross sign endpoints (sorted in same order)
p1 = getCross(I1);
p2 = getCross(I2);
%% # Step 3: perform Image Registration
%# Find transformation that maps I2 to I1 using the 4 control points for each
t = cp2tform(p2,p1,'affine');
%# Transform I2 to be aligned with I1
II2 = imtransform(I2, t, 'XData',[1 size(I1,2)], 'YData',[1 size(I1,1)]);
%# Plot
figure('menu','none')
subplot(131), imshow(I1), title('I1')
subplot(132), imshow(I2), title('I2')
subplot(133), imshow(II2), title('I2 (aligned)')
%# Recover affine transformation params (translation, rotation, scale)
ss = t.tdata.Tinv(2,1);
sc = t.tdata.Tinv(1,1);
tx = t.tdata.Tinv(3,1);
ty = t.tdata.Tinv(3,2);
scale = sqrt(ss*ss + sc*sc)
rotation = atan2(ss,sc)*180/pi
translation = [tx ty]
And here's the function that extract the lines endpoints:
function points = getCross(I)
%# Get edges (simply by thresholding)
I = imfilter(I, fspecial('gaussian', [7 7], 1), 'symmetric');
BW = imclearborder(~im2bw(I, 0.5));
%# Hough transform
[H,T,R] = hough(BW);
%# Detect peaks
P = houghpeaks(H, 2);
%# Detect lines
lines = houghlines(BW, T, R, P);
%# Sort 2D points in counterclockwise order
points = [vertcat(lines.point1); vertcat(lines.point2)];
idx = convhull(points(:,1), points(:,2));
points = points(idx(1:end-1),:);
end
with the result:
scale =
1.0025
rotation =
-9.7041
translation =
32.5270 -38.5021
The rotation is recovered as almost 10 degrees (with some inevitable error), and scaling is effectively 1 (meaning there was no zooming). Note that there was a translation component in the above example, because rotation was not performed around the center of the cross sign).

I am not sure what the MATLAB implementation of the Hough transform is, but the orientation of the line will be simply be at a right angle (90 degrees or pi/2 radians) to the angle you've used to identify the line in the first place.
I hope that helps. There's decent coverage of Hough transforms on the web and Wikipedia is a good place to start.

Related

How can I tune the hough transform in MATLAB to detect more lines for checkerboard detection?

I'm trying to write an algorithm for detecting checkerboard patterns. I know that MATLAB has a built-in function for this but I want to do it on my own.
Anyway, I have like 30 pictures of a checkerboard pattern that are shown to the camera in different positions. I have already developed an algorithm for finding the region of interest using regionprops which is working reasonably well. However, when I apply a canny filter on the photo for detecting the edges and then finding the lines, the Hough transform fails to detect some of the lines that must be detected in few photos. See figure 1 and figure 2 for example.
Here is an MCV of my code to see my preprocessing step and my current parameters of the Hough transform:
img = imread('checkerboard13.jpg');
bw = im2bw(img, graythresh(img));
bw = medfilt2(bw, [9,9]);
BW = edge(bw);
[H,T,R] = hough(BW);
P = houghpeaks(H, 100,'NHoodSize',[1 1],'threshold',ceil(0.01*max(H(:))));
lines = houghlines(BW, T, R, P, 'FillGap', 80, 'MinLength', 400);
x = T(P(:,2)); y = R(P(:,1));
figure;
imagesc(I);hold on;colormap gray;
axis image;
max_len = 0;
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
len = norm(lines(k).point1 - lines(k).point2);
if ( len > max_len)
max_len = len;
xy_long = xy;
end
end
So, as I said in the comments, you have to play with the parameters for dilation and houghlines.
Problem: The lines that you are looking for, specifically the checker board ones, are very thin and not connected. Hence, using just Houghlines might be possible but lot of tuning of the parameters is required.
Solution: Dilate the image before applying hough lines and adjust the parameters for the hough lines accordingly.
Dilation is basically a morphological operation where a pixel element is '1' if atleast one pixel under the kernel is '1'. So it increases the white region in the image or size of foreground object increases. As an alternative you can also try using Opening which is basically erosion (opposite of dilation) + dilation.
Parameters for Houghlines:
OpenCV function:
lines = cv.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]])
The most important parameters in your case are:
threshold - Decides which lines to consider, only lines having intensities > threshold are chosen.
minLineLength - As the name says, this determines what should be the minimum length of the line (in pixels) to be categorized as a line
maxLineGap - Probably the second most important parameter, if not the most important (tough competition vs the threshold), this defines what should be the maximum gap between two lines to be categorized as one single line.
Other parameters:
rho: Distance resolution in pixels, 1 here
theta: Angle resolution in radians, 1 here
For a good tutorial, have a look at how hough transform works?
Using OpenCV with the following code:
import cv2
import numpy as np
image1 = cv2.imread('lines.jpg')
output = image1
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
canny = cv2.Canny(image1, 100, 200)
canny = cv2.dilate(canny, (5,5), 7)
lines = cv2.HoughLinesP(canny, 1, np.pi/360, 120, minLineLength=200, maxLineGap=30)
for line in lines:
for x1,y1,x2,y2 in line:
cv2.line(output, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imwrite("lines_res.jpg", output)
Original image:
Result:
Hope it helps! Let me know what you think!

Extract line shaped objects

I'm working on images with overlapping line shapes (left plot). Ultimately I want to segment single objects. I'm working with a Hough transform to achieve this and it works well in finding lines of (significantly) different orientation - e.g. represented by the two maxima in the hough space below (middle plot).
the green and yellow lines (left plot) and crosses (right plot) stem from an approach to do something with the thickness of the line. I couldn't figure out how to extract a broad line though, so I didn't follow up.
I'm aware of the ambiguity of assigning the "overlapping pixels". I will address that later.
Since I don't know, how many line objects one connected region may contain, my idea is to iteratively extract the object corresponding to the hough line with the highest activation (here painted in blue), i.e. remove the line shaped object from the image, so that the next iteration will find only the other line.
But how do I detect, which pixels belong to the line shaped object?
The function hough_bin_pixels(img, theta, rho, P) (from here - shown in the right plot) gives pixels corresponding to the particular line. But that obviously is too thin of a line to represent the object.
Is there a way to segment/detect the whole object that is orientied along the strongest houghline?
The key is knowing that thick lines in the original image translate to wider peaks on the Hough Transform. This image shows the peaks of a thin and a thick line.
You can use any strategy you like to group all the pixels/accumulator bins of each peak together. I would recommend using multithresh and imquantize to convert it to a BW image, and then bwlabel to label the connected components. You could also use any number of other clustering/segmentation strategies. The only potentially tricky part is figuring out the appropriate thresholding levels. If you can't get anything suitable for your application, err on the side of including too much because you can always get rid of erroneous pixels later.
Here are the peaks of the Hough Transform after thresholding (left) and labeling (right)
Once you have the peak regions, you can find out which pixels in the original image contributed to each accumulator bin using hough_bin_pixels. Then, for each peak region, combine the results of hough_bin_pixels for every bin that is part of the region.
Here is the code I threw together to create the sample images. I'm just getting back into matlab after not using it for a while, so please forgive the sloppy code.
% Create an image
image = zeros(100,100);
for i = 10:90
image(100-i,i)=1;
end;
image(10:90, 30:35) = 1;
figure, imshow(image); % Fig. 1 -- Original Image
% Hough Transform
[H, theta_vals, rho_vals] = hough(image);
figure, imshow(mat2gray(H)); % Fig. 2 -- Hough Transform
% Thresholding
thresh = multithresh(H,4);
q_image = imquantize(H, thresh);
q_image(q_image < 4) = 0;
q_image(q_image > 0) = 1;
figure, imshow(q_image) % Fig. 3 -- Thresholded Peaks
% Label connected components
L = bwlabel(q_image);
figure, imshow(label2rgb(L, prism)) % Fig. 4 -- Labeled peaks
% Reconstruct the lines
[r, c] = find(L(:,:)==1);
segmented_im = hough_bin_pixels(image, theta_vals, rho_vals, [r(1) c(1)]);
for i = 1:size(r(:))
seg_part = hough_bin_pixels(image, theta_vals, rho_vals, [r(i) c(i)]);
segmented_im(seg_part==1) = 1;
end
region1 = segmented_im;
[r, c] = find(L(:,:)==2);
segmented_im = hough_bin_pixels(image, theta_vals, rho_vals, [r(1) c(1)]);
for i = 1:size(r(:))
seg_part = hough_bin_pixels(image, theta_vals, rho_vals, [r(i) c(i)]);
segmented_im(seg_part==1) = 1;
end
region2 = segmented_im;
figure, imshow([region1 ones(100, 1) region2]) % Fig. 5 -- Segmented lines
% Overlay and display
out = cat(3, image, region1, region2);
figure, imshow(out); % Fig. 6 -- For fun, both regions overlaid on original image

Fourier transform for fiber alignment

I'm working on an application to determine from an image the degree of alignment of a fiber network. I've read several papers on this issue and they basically do this:
Find the 2D discrete Fourier transform (DFT = F(u,v)) of the image (gray, range 0-255)
Find the Fourier Spectrum (FS = abs(F(u,v))) and the Power Spectrum (PS = FS^2)
Convert spectrum to polar coordinates and divide it into 1º intervals.
Calculate number-averaged line intensities (FI) for each interval (theta), that is, the average of all the intensities (pixels) forming "theta" degrees with respect to the horizontal axis.
Transform FI(theta) to cartesian coordinates
Cxy(theta) = [FI*cos(theta), FI*sin(theta)]
Find eigenvalues (lambda1 and lambda2) of the matrix Cxy'*Cxy
Find alignment index as alpha = 1 - lamda2/lambda1
I've implemented this in MATLAB (code below), but I'm not sure whether it is ok since point 3 and 4 are not really clear for me (I'm getting similar results to those of the papers, but not in all cases). For instance, in point 3, "spectrum" is referring to FS or to PS?. And in point 4, how should this average be done? are all the pixels considered? (even though there are more pixels in the diagonal).
rgb = imread('network.tif');%513x513 pixels
im = rgb2gray(rgb);
im = imrotate(im,-90);%since FFT space is rotated 90º
FT = fft2(im) ;
FS = abs(FT); %Fourier spectrum
PS = FS.^2; % Power spectrum
FS = fftshift(FS);
PS = fftshift(PS);
xoffset = (513-1)/2;
yoffset = (513-1)/2;
% Avoid low frequency points
x1 = 5;
y1 = 0;
% Maximum high frequency pixels
x2 = 255;
y2 = 0;
for theta = 0:pi/180:pi
% Transposed rotation matrix
Rt = [cos(theta) sin(theta);
-sin(theta) cos(theta)];
% Find radial lines necessary for improfile
xy1_rot = Rt * [x1; y1] + [xoffset; yoffset];
xy2_rot = Rt * [x2; y2] + [xoffset; yoffset];
plot([xy1_rot(1) xy2_rot(1)], ...
[xy1_rot(2) xy2_rot(2)], ...
'linestyle','none', ...
'marker','o', ...
'color','k');
prof = improfile(F,[xy1_rot(1) xy2_rot(1)],[xy1_rot(2) xy2_rot(2)]);
i = i + 1;
FI(i) = sum(prof(:))/length(prof);
Cxy(i,:) = [FI(i)*cos(theta), FI(i)*sin(theta)];
end
C = Cxy'*Cxy;
[V,D] = eig(C)
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1
Figure: A) original image, B) plot of log(P+1), C) polar plot of FI.
My main concern is that when I choose an artificial image perfectly aligned (attached figure), I get alpha = 0.91, and it should be exactly 1.
Any help will be greatly appreciated.
PD: those black dots in the middle plot are just the points used by improfile.
I believe that there are a couple sources of potential error here that are leading to you not getting a perfect alpha value.
Discrete Fourier Transform
You have discrete imaging data which forces you to take a discrete Fourier transform which inevitably (depending on the resolution of the input data) have some accuracy issues.
Binning vs. Sampling Along a Line
The way that you have done the binning is that you literally drew a line (rotated by a particular angle) and sampled the image along that line using improfile. Using improfile performs interpolation of your data along that line introducing yet another potential source of error. The default is nearest neighbor interpolation which in the example shown below can cause multiple "profiles" to all pick up the same points.
This was with a rotation of 1-degree off-vertical when technically you'd want those peaks to only appear for a perfectly vertical line. It is clear to see how this sort of interpolation of the Fourier spectrum can lead to a spread around the "correct" answer.
Data Undersampling
Similar to Nyquist sampling in the Fourier domain, sampling in the spatial domain has some requirements as well.
Imagine for a second that you wanted to use 45-degree bin widths instead of the 1-degree. Your approach would still sample along a thin line and use that sample to represent 45-degrees worth or data. Clearly, this is a gross under-sampling of the data and you can imagine that the result wouldn't be very accurate.
It becomes more and more of an issue the further you get from the center of the image since the data in this "bin" is really pie wedge shaped and you're approximating it with a line.
A Potential Solution
A different approach to binning would be to determine the polar coordinates (r, theta) for all pixel centers in the image. Then to bin the theta components into 1-degree bins. Then sum all of the values that fall into that bin.
This has several advantages:
It removes the undersampling that we talked about and draws samples from the entire "pie wedge" regardless of the sampling angle.
It ensures that each pixel belongs to one and only one angular bin
I have implemented this alternate approach in the code below with some false horizontal line data and am able to achieve an alpha value of 0.988 which I'd say is pretty good given the discrete nature of the data.
% Draw a bunch of horizontal lines
data = zeros(101);
data([5:5:end],:) = 1;
fourier = fftshift(fft2(data));
FS = abs(fourier);
PS = FS.^2;
center = fliplr(size(FS)) / 2;
[xx,yy] = meshgrid(1:size(FS,2), 1:size(FS, 1));
coords = [xx(:), yy(:)];
% De-mean coordinates to center at the middle of the image
coords = bsxfun(#minus, coords, center);
[theta, R] = cart2pol(coords(:,1), coords(:,2));
% Convert to degrees and round them to the nearest degree
degrees = mod(round(rad2deg(theta)), 360);
degreeRange = 0:359;
% Band pass to ignore high and low frequency components;
lowfreq = 5;
highfreq = size(FS,1)/2;
% Now average everything with the same degrees (sum over PS and average by the number of pixels)
for k = degreeRange
ps_integral(k+1) = mean(PS(degrees == k & R > lowfreq & R < highfreq));
fs_integral(k+1) = mean(FS(degrees == k & R > lowfreq & R < highfreq));
end
thetas = deg2rad(degreeRange);
Cxy = [ps_integral.*cos(thetas);
ps_integral.*sin(thetas)]';
C = Cxy' * Cxy;
[V,D] = eig(C);
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1;

Hough Transform - Finding the center of the hough transform circles

I have been reading up quite a bit on Hough circles and how it can be used to detect the center and radius of a circular object. Although I understood how the transform works I am still not able to to understand how to get the center of a circle. The code snippet below is customized purely for coins.png in MATLAB. I tried it with a slightly more complex picture and it fails miserably. I am fixing the radius and then getting the accumulator matrix.
Code:
temp = accum;
temp(find(temp<40))=0; %Thresholding
imshow(temp,[0 max(temp(:))])
temp = imdilate(temp, ones(6,6)); %Dilating
imshow(temp,[0 max(max(temp))]);
temp = imerode(temp,ones(3,3)); %Eroding
imshow(temp,[0 max(max(temp))]);
s = regionprops(im2bw(temp),'centroid'); %Computing centroid
centroids = cat(1, s.Centroid);
I wanted to test the code out on a different picture and found this one on google. The Hough Transform produced a favorable result, but it's more overlapping than the previous case and my method fails.
Can someone suggest what the best method is to compute the centroid of the hough circle?
Original image:
Full Code:
%%Read and find edges using a filter
i = imread('coins4.jpg');
i = rgb2gray(i);
i = imresize(i,0.125);
i_f = edge(i, 'canny',[0.01 0.45]);
imshow(i_f)
%%
[x y] = find(i_f>0); % Finds where the edges are and stores the x and y coordinates
edge_index = size(x);
radius = 30; %Fixed radius value
theta = 0:0.01:2*pi;
accum = zeros(size(i,1), size(i,2)); %Create an accumulator matrix.
r_co = radius*cos(theta);
r_si = radius*sin(theta);
x1 = repmat(x, 1, length(r_co));
y1 = repmat(y, 1, length(r_si));
x_r_co = repmat(r_co, length(x),1);
y_r_si = repmat(r_si, length(y),1);
%% Filling the accumulator
a = x1 - x_r_co;
b = y1 - y_r_si;
for cnt = 1:numel(a)
a_cnt = round(a(cnt));
b_cnt = round(b(cnt));
if(a_cnt>0 && b_cnt>0 && a_cnt<=size(accum,1) && b_cnt<=size(accum,2))
accum(a_cnt,b_cnt) = accum(a_cnt,b_cnt) + 1;
end
end
imshow(accum,[0 max(max(accum))]);
%% Thresholding and get the center of the circle.
close all;
temp = accum;
temp(find(temp<40))=0;
imshow(temp,[0 max(temp(:))])
temp = imdilate(temp, ones(6,6));
imshow(temp,[0 max(max(temp))]);
temp = imerode(temp,ones(3,3));
imshow(temp,[0 max(max(temp))]);
s = regionprops(im2bw(temp),'centroid');
centroids = cat(1, s.Centroid);
imshow(i);
hold on;
plot(centroids(:,1), centroids(:,2),'*b')
If you want to stick with the 2D image you have now, this is a simple algorithm that might work for your first image since all the coins are about the same size and it isn't too cluttered:
find the global maximum of your accumulator array using [~,max_idx] = max(accum(:)); [i,j] = ind2sub(size(accum), max_idx];
Take a small neighbourhood around this pixel (maybe a square with a 10 pixel radius) and calculate its center of mass and get its index (this basically finds the middle of the bright rings you see)
add the center of mass pixel index to a list of circle centers
set all pixels in the small neighbourhood to zero (to prevent double-detection of the same pixel center)
repeat from step 1. until you reach approximately 70% or so of the original global max
If that doesn't work, I'd suggest taking the 3D accumulator approach, which is much better for coins of variable size.
The reason you are getting "donut" shapes for some of the coins rather than circles with intense bright points at the centers is because the radius you are assuming is a little off. If you were to explore the 3rd dimension of the accumulator (the radius dimension), you would find that these "donuts" taper to a point, which you could then detect as a local maximum.
This does require a lot more time and memory but it would lead to the most accurate result and isn't a big adjustment code-wise. You can detect the maxima using a method similar to the one I mentioned above, but in 3D and without the center-of-mass trick in step 2.
So, there is a function in matlab that does exactly what you want, called imfindcircles. You give an image and a range of possible radii, it outputs the computed radii and the centers of the circles. Some other parameters allow to tweak the computation (and in my experience, using them is necessary for good results).
Now, if you want to find the centers and radii yourself (not using matlab) then you want to use a maximum finder on the accumulation matrix. The concept of the Hough transform is that the maxima you find on the accumulation matrix each correspond to a circle (parametrized usually in (radius, x_center, y_center). The tough part here is that you have a gigantic searchspace and many many many many maxima that are not "real" circles but artefacts. I do not know how to reliably differentiate these fake circles of the real ones -- and I expect it's a rather complicated check to do.
Hope this helps!

How to draw a straight across the centroid points of the barcode using best fit points Matlab

This is the processed image and I can't increase the bwareaopen() as it won't work for my other image.
Anyway I'm trying to find the shortest points in the centre points of the barcode, to get the straight line across the centre points in the barcode.
Example:
After doing a centroid command, the points in the barcode are near to each other. Therefore, I just wanted to get the shortest points(which is the barcode) and draw a straight line across.
All the points need not be join, best fit points will do.
Step 1
Step 2
Step 3
If you dont have the x,y elements Andrey uses, you can find them by segmenting the image and using a naive threshold value on the area to avoid including the number below the bar code.
I've hacked out a solution in MATLAB doing the following:
Loading the image and making it binary
Extracting all connected components using bwlabel().
Getting useful information about each of them via regionprops() [.centroid will be a good approximation to the middel point for the lines].
Thresholded out small regions (noise and numbers)
Extracted x,y coordinates
Used Andreys linear fit solution
Code:
set(0,'DefaultFigureWindowStyle','docked');
close all;clear all;clc;
Im = imread('29ekeap.jpg');
Im=rgb2gray(Im);
%%
%Make binary
temp = zeros(size(Im));
temp(Im > mean(Im(:)))=1;
Im = temp;
%Visualize
f1 = figure(1);
imagesc(Im);colormap(gray);
%Find connected components
LabelIm = bwlabel(Im);
RegionInfo = regionprops(LabelIm);
%Remove background region
RegionInfo(1) = [];
%Get average area of regions
AvgArea = mean([RegionInfo(1:end).Area]);
%Vector to keep track of likely "bar elements"
Bar = zeros(length(RegionInfo),1);
%Iterate over regions, plot centroids if area is big enough
for i=1:length(RegionInfo)
if RegionInfo(i).Area > AvgArea
hold on;
plot(RegionInfo(i).Centroid(1),RegionInfo(i).Centroid(2),'r*')
Bar(i) = 1;
end
end
%Extract x,y points for interpolation
X = [RegionInfo(Bar==1).Centroid];
X = reshape(X,2,length(X)/2);
x = X(1,:);
y = X(2,:);
%Plot line according to Andrey
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange,'LineWidth',2,'Color',[0.9 0.2 0.2]);
The result is a pretty good fitted line. You should be able to extend it to the ends by using the 'p' polynomal and evaluate when you dont encounter any more '1's if needed.
Result:
If you already found the x,y of the centers, you should use polyfit function:
You will then find the polynomial coefficients of the best line. In order to draw a segment, you can take the minimal and maximal x
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange);
If your ultimate goal is to generate a line perpendicular to the bars in the bar code and passing roughly through the centroids of the bars, then I have another option for you to consider...
A simple solution would be to perform a Hough transform to detect the primary orientation of lines in the bar code. Once you find the angle of the lines in the bar code, all you have to do is rotate that by 90 degrees to get the slope of a perpendicular line. The centroid of the entire bar code can then be used as an intercept for this line. Using the functions HOUGH and HOUGHPEAKS from the Image Processing Toolbox, here's the code starting with a cropped version of your image from step 1:
img = imread('bar_code.jpg'); %# Load the image
img = im2bw(img); %# Convert from RGB to BW
[H, theta, rho] = hough(img); %# Perform the Hough transform
peak = houghpeaks(H); %# Find the peak pt in the Hough transform
barAngle = theta(peak(2)); %# Find the angle of the bars
slope = -tan(pi*(barAngle + 90)/180); %# Compute the perpendicular line slope
[y, x] = find(img); %# Find the coordinates of all the white image points
xMean = mean(x); %# Find the x centroid of the bar code
yMean = mean(y); %# Find the y centroid of the bar code
xLine = 1:size(img,2); %# X points of perpendicular line
yLine = slope.*(xLine - xMean) + yMean; %# Y points of perpendicular line
imshow(img); %# Plot bar code image
hold on; %# Add to the plot
plot(xMean, yMean, 'r*'); %# Plot the bar code centroid
plot(xLine, yLine, 'r'); %# Plot the perpendicular line
And here's the resulting image: